Formatting, cleanup, misc style changes, more config options
All checks were successful
Deploy / Deploy (push) Successful in 28s
All checks were successful
Deploy / Deploy (push) Successful in 28s
This commit is contained in:
parent
3af16a98e2
commit
2db2ca4a05
9 changed files with 122 additions and 106 deletions
47
config.ts
47
config.ts
|
@ -2,27 +2,34 @@
|
|||
* Configuration module for the PDS Dashboard
|
||||
*/
|
||||
export class Config {
|
||||
/**
|
||||
* The base URL of the PDS (Personal Data Server)
|
||||
* @default "https://pds.witchcraft.systems"
|
||||
*/
|
||||
static readonly PDS_URL: string = "https://pds.witchcraft.systems";
|
||||
/**
|
||||
* The base URL of the PDS (Personal Data Server)
|
||||
* @default "https://pds.witchcraft.systems"
|
||||
*/
|
||||
static readonly PDS_URL: string = "https://pds.witchcraft.systems";
|
||||
|
||||
/**
|
||||
* The base URL of the frontend service for linking to replies
|
||||
* @default "https://deer.social"
|
||||
*/
|
||||
static readonly FRONTEND_URL: string = "https://deer.social";
|
||||
|
||||
/**
|
||||
* Maximum number of posts to show in the feed (across all users)
|
||||
* @default 100
|
||||
*/
|
||||
static readonly MAX_POSTS: number = 100;
|
||||
|
||||
/**
|
||||
* Footer text for the dashboard
|
||||
* @default "Astrally projected from witchcraft.systems"
|
||||
*/
|
||||
static readonly FOOTER_TEXT: string =
|
||||
"Astrally projected from <a href='https://witchcraft.systems' target='_blank'>witchcraft.systems</a>";
|
||||
|
||||
/**
|
||||
* The base URL of the frontend service for linking to replies
|
||||
* @default "https://deer.social"
|
||||
* Whether to show the posts that are in the future
|
||||
* @default false
|
||||
*/
|
||||
static readonly FRONTEND_URL: string = "https://deer.social";
|
||||
|
||||
/**
|
||||
* Maximum number of posts to show in the feed (across all users)
|
||||
* @default 100
|
||||
*/
|
||||
static readonly MAX_POSTS: number = 100;
|
||||
|
||||
/**
|
||||
* Footer text for the dashboard
|
||||
* @default "Astrally projected from witchcraft.systems"
|
||||
*/
|
||||
static readonly FOOTER_TEXT: string = "Astrally projected from <a href='https://witchcraft.systems' target='_blank'>witchcraft.systems</a>";
|
||||
static readonly SHOW_FUTURE_POSTS: boolean = false;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
|
@ -9,39 +9,38 @@
|
|||
|
||||
<main>
|
||||
<div id="Content">
|
||||
{#await accountsPromise}
|
||||
<p>Loading...</p>
|
||||
{:then accountsData}
|
||||
<div id="Account">
|
||||
<h1 id="Header">ATProto PDS</h1>
|
||||
<p>Home to {accountsData.length} accounts</p>
|
||||
<div id="accountsList">
|
||||
{#each accountsData as accountObject}
|
||||
<AccountComponent account={accountObject} />
|
||||
{/each}
|
||||
{#await accountsPromise}
|
||||
<p>Loading...</p>
|
||||
{:then accountsData}
|
||||
<div id="Account">
|
||||
<h1 id="Header">ATProto PDS</h1>
|
||||
<p>Home to {accountsData.length} accounts</p>
|
||||
<div id="accountsList">
|
||||
{#each accountsData as accountObject}
|
||||
<AccountComponent account={accountObject} />
|
||||
{/each}
|
||||
</div>
|
||||
<p>{@html Config.FOOTER_TEXT}</p>
|
||||
</div>
|
||||
<p>{@html Config.FOOTER_TEXT}</p>
|
||||
</div>
|
||||
{:catch error}
|
||||
<p>Error: {error.message}</p>
|
||||
{/await}
|
||||
{:catch error}
|
||||
<p>Error: {error.message}</p>
|
||||
{/await}
|
||||
|
||||
{#await postsPromise}
|
||||
<p>Loading...</p>
|
||||
{:then postsData}
|
||||
<div id="Feed">
|
||||
<div id="spacer"></div>
|
||||
{#each postsData as postObject}
|
||||
<PostComponent post={postObject as Post} />
|
||||
{/each}
|
||||
<div id="spacer"></div>
|
||||
</div>
|
||||
{/await}
|
||||
{#await postsPromise}
|
||||
<p>Loading...</p>
|
||||
{:then postsData}
|
||||
<div id="Feed">
|
||||
<div id="spacer"></div>
|
||||
{#each postsData as postObject}
|
||||
<PostComponent post={postObject as Post} />
|
||||
{/each}
|
||||
<div id="spacer"></div>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
||||
/* desktop style */
|
||||
|
||||
#Content {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@font-face {
|
||||
font-family: 'ProggyClean';
|
||||
font-family: "ProggyClean";
|
||||
src: url(https://witchcraft.systems/ProggyCleanNerdFont-Regular.ttf);
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ body {
|
|||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
background-color: var(--background-color);
|
||||
font-family: 'ProggyClean', monospace;
|
||||
font-family: "ProggyClean", monospace;
|
||||
font-size: 24px;
|
||||
color: var(--text-color);
|
||||
border-color: var(--border-color);
|
||||
|
|
|
@ -145,7 +145,6 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
@ -197,6 +196,7 @@
|
|||
background-color: var(--content-background-color);
|
||||
color: var(--text-color);
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-line;
|
||||
}
|
||||
#replyingText {
|
||||
font-size: 0.7em;
|
||||
|
|
|
@ -45,7 +45,7 @@ class Post {
|
|||
|
||||
constructor(
|
||||
record: ComAtprotoRepoListRecords.Record,
|
||||
account: AccountMetadata,
|
||||
account: AccountMetadata
|
||||
) {
|
||||
this.postCid = record.cid;
|
||||
this.recordName = processAtUri(record.uri).rkey;
|
||||
|
@ -67,8 +67,8 @@ class Post {
|
|||
this.videosLinkCid = null;
|
||||
switch (post.embed?.$type) {
|
||||
case "app.bsky.embed.images":
|
||||
this.imagesCid = post.embed.images.map((imageRecord: any) =>
|
||||
imageRecord.image.ref.$link
|
||||
this.imagesCid = post.embed.images.map(
|
||||
(imageRecord: any) => imageRecord.image.ref.$link
|
||||
);
|
||||
break;
|
||||
case "app.bsky.embed.video":
|
||||
|
@ -81,8 +81,8 @@ class Post {
|
|||
this.quotingUri = processAtUri(post.embed.record.record.uri);
|
||||
switch (post.embed.media.$type) {
|
||||
case "app.bsky.embed.images":
|
||||
this.imagesCid = post.embed.media.images.map((imageRecord) =>
|
||||
imageRecord.image.ref.$link
|
||||
this.imagesCid = post.embed.media.images.map(
|
||||
(imageRecord) => imageRecord.image.ref.$link
|
||||
);
|
||||
|
||||
break;
|
||||
|
@ -111,36 +111,37 @@ const rpc = new XRPC({
|
|||
}),
|
||||
});
|
||||
|
||||
const getDidsFromPDS = async () : Promise<At.Did[]> => {
|
||||
const getDidsFromPDS = async (): Promise<At.Did[]> => {
|
||||
const { data } = await rpc.get("com.atproto.sync.listRepos", {
|
||||
params: {},
|
||||
});
|
||||
return data.repos.map((repo: any) => (repo.did)) as At.Did[];
|
||||
return data.repos.map((repo: any) => repo.did) as At.Did[];
|
||||
};
|
||||
const getAccountMetadata = async (did: `did:${string}:${string}`) : Promise<AccountMetadata> => {
|
||||
const getAccountMetadata = async (
|
||||
did: `did:${string}:${string}`
|
||||
): Promise<AccountMetadata> => {
|
||||
// gonna assume self exists in the app.bsky.actor.profile
|
||||
try {
|
||||
const { data } = await rpc.get("com.atproto.repo.getRecord", {
|
||||
params: {
|
||||
repo: did,
|
||||
collection: "app.bsky.actor.profile",
|
||||
rkey: "self",
|
||||
},
|
||||
});
|
||||
const value = data.value as AppBskyActorProfile.Record;
|
||||
const handle = await blueskyHandleFromDid(did);
|
||||
const account: AccountMetadata = {
|
||||
did: did,
|
||||
handle: handle,
|
||||
displayName: value.displayName || "",
|
||||
avatarCid: null,
|
||||
};
|
||||
if (value.avatar) {
|
||||
account.avatarCid = value.avatar.ref["$link"];
|
||||
}
|
||||
return account;
|
||||
}
|
||||
catch (e) {
|
||||
const { data } = await rpc.get("com.atproto.repo.getRecord", {
|
||||
params: {
|
||||
repo: did,
|
||||
collection: "app.bsky.actor.profile",
|
||||
rkey: "self",
|
||||
},
|
||||
});
|
||||
const value = data.value as AppBskyActorProfile.Record;
|
||||
const handle = await blueskyHandleFromDid(did);
|
||||
const account: AccountMetadata = {
|
||||
did: did,
|
||||
handle: handle,
|
||||
displayName: value.displayName || "",
|
||||
avatarCid: null,
|
||||
};
|
||||
if (value.avatar) {
|
||||
account.avatarCid = value.avatar.ref["$link"];
|
||||
}
|
||||
return account;
|
||||
} catch (e) {
|
||||
console.error(`Error fetching metadata for ${did}:`, e);
|
||||
return {
|
||||
did: "error",
|
||||
|
@ -151,14 +152,14 @@ const getAccountMetadata = async (did: `did:${string}:${string}`) : Promise<Acco
|
|||
}
|
||||
};
|
||||
|
||||
const getAllMetadataFromPds = async () : Promise<AccountMetadata[]> => {
|
||||
const getAllMetadataFromPds = async (): Promise<AccountMetadata[]> => {
|
||||
const dids = await getDidsFromPDS();
|
||||
const metadata = await Promise.all(
|
||||
dids.map(async (repo: `did:${string}:${string}`) => {
|
||||
return await getAccountMetadata(repo);
|
||||
}),
|
||||
})
|
||||
);
|
||||
return metadata.filter(account => account.did !== "error");
|
||||
return metadata.filter((account) => account.did !== "error");
|
||||
};
|
||||
|
||||
const fetchPosts = async (did: string) => {
|
||||
|
@ -173,14 +174,14 @@ const fetchPosts = async (did: string) => {
|
|||
return {
|
||||
records: data.records as ComAtprotoRepoListRecords.Record[],
|
||||
did: did,
|
||||
error: false
|
||||
error: false,
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(`Error fetching posts for ${did}:`, e);
|
||||
return {
|
||||
records: [],
|
||||
did: did,
|
||||
error: true
|
||||
error: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -195,7 +196,7 @@ const identityResolve = async (did: At.Did) => {
|
|||
|
||||
if (did.startsWith("did:plc:") || did.startsWith("did:web:")) {
|
||||
const doc = await resolver.resolve(
|
||||
did as `did:plc:${string}` | `did:web:${string}`,
|
||||
did as `did:plc:${string}` | `did:web:${string}`
|
||||
);
|
||||
return doc;
|
||||
} else {
|
||||
|
@ -221,15 +222,15 @@ const blueskyHandleFromDid = async (did: At.Did) => {
|
|||
const fetchAllPosts = async () => {
|
||||
const users: AccountMetadata[] = await getAllMetadataFromPds();
|
||||
const postRecords = await Promise.all(
|
||||
users.map(async (metadata: AccountMetadata) =>
|
||||
await fetchPosts(metadata.did)
|
||||
),
|
||||
users.map(
|
||||
async (metadata: AccountMetadata) => await fetchPosts(metadata.did)
|
||||
)
|
||||
);
|
||||
const validPostRecords = postRecords.filter(record => !record.error);
|
||||
const validPostRecords = postRecords.filter((record) => !record.error);
|
||||
const posts: Post[] = validPostRecords.flatMap((userFetch) =>
|
||||
userFetch.records.map((record) => {
|
||||
const user = users.find((user: AccountMetadata) =>
|
||||
user.did == userFetch.did
|
||||
const user = users.find(
|
||||
(user: AccountMetadata) => user.did == userFetch.did
|
||||
);
|
||||
if (!user) {
|
||||
throw new Error(`User with DID ${userFetch.did} not found`);
|
||||
|
@ -237,7 +238,16 @@ const fetchAllPosts = async () => {
|
|||
return new Post(record, user);
|
||||
})
|
||||
);
|
||||
|
||||
posts.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
if(!Config.SHOW_FUTURE_POSTS) {
|
||||
// Filter out posts that are in the future
|
||||
const now = Date.now();
|
||||
const filteredPosts = posts.filter((post) => post.timestamp <= now);
|
||||
return filteredPosts.slice(0, Config.MAX_POSTS);
|
||||
}
|
||||
|
||||
return posts.slice(0, Config.MAX_POSTS);
|
||||
};
|
||||
export { fetchAllPosts, getAllMetadataFromPds, Post };
|
||||
|
|
12
src/main.ts
12
src/main.ts
|
@ -1,9 +1,9 @@
|
|||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
import { mount } from "svelte";
|
||||
import "./app.css";
|
||||
import App from "./App.svelte";
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!,
|
||||
})
|
||||
target: document.getElementById("app")!,
|
||||
});
|
||||
|
||||
export default app
|
||||
export default app;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
export default {
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import { defineConfig } from "vite";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
})
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue