Somewhat sensible design (WIP)

This commit is contained in:
Astra 2025-04-20 17:15:00 +09:00
parent 164571ec19
commit dcca38a994
Signed by: astra
SSH key fingerprint: SHA256:jQDNS75/33T59Ey4yAzrUPP/5YQaXEetsW8hwUae+ag
5 changed files with 162 additions and 75 deletions

View file

@ -7,11 +7,13 @@
</script> </script>
<main> <main>
<h1>Welcome to the Feed</h1> <div id="Content">
{#await accountsPromise} {#await accountsPromise}
<p>Loading...</p> <p>Loading...</p>
{:then accountsData} {:then accountsData}
<div id="Account"> <div id="Account">
<h1 id="Header">ATProto PDS</h1>
<p>Home to {accountsData.length} accounts</p>
{#each accountsData as accountObject} {#each accountsData as accountObject}
<AccountComponent account={accountObject} /> <AccountComponent account={accountObject} />
{/each} {/each}
@ -29,7 +31,39 @@
{/each} {/each}
</div> </div>
{/await} {/await}
</div>
</main> </main>
<style> <style>
#Content {
display: flex;
/* split the screen in half, left for accounts, right for posts */
width: 100%;
height: 100%;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: #12082b;
color: #ffffff;
}
#Feed {
width: 65%;
height: 80vh;
overflow-y: scroll;
padding: 20px;
}
#Account {
width: 35%;
height: 80vh;
overflow-y: scroll;
padding: 20px;
background-color: #070311;
border-radius: 10px;
}
#Header {
text-align: center;
font-size: 2em;
margin-bottom: 20px;
}
</style> </style>

View file

@ -1,16 +1,19 @@
:root { @font-face {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: 'ProggyClean';
line-height: 1.5; src: url(https://witchcraft.systems/ProggyCleanNerdFont-Regular.ttf);
font-weight: 400; }
color-scheme: light dark; ::-webkit-scrollbar {
color: rgba(255, 255, 255, 0.87); width: 0px;
background-color: #242424; background: transparent;
}
font-synthesis: none; * {
text-rendering: optimizeLegibility; scrollbar-width: thin;
-webkit-font-smoothing: antialiased; scrollbar-color: transparent transparent;
-moz-osx-font-smoothing: grayscale; -ms-overflow-style: none; /* IE and Edge */
-webkit-overflow-scrolling: touch;
-webkit-scrollbar: none; /* Safari */
} }
a { a {
@ -28,6 +31,11 @@ body {
place-items: center; place-items: center;
min-width: 320px; min-width: 320px;
min-height: 100vh; min-height: 100vh;
background-color: #12082b;
font-family: 'ProggyClean', monospace;
font-size: 24px;
color: white;
border-color: #8054f0;
} }
h1 { h1 {
@ -35,45 +43,11 @@ h1 {
line-height: 1.1; line-height: 1.1;
} }
.card {
padding: 2em;
}
#app { #app {
max-width: 1280px; max-width: 1400px;
margin: 0 auto; margin: 0 auto;
padding: 2rem; padding: 2rem;
text-align: center; text-align: center;
} }
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View file

@ -1,23 +1,39 @@
<script lang="ts"> <script lang="ts">
import type { AccountMetadata } from "./pdsfetch"; import type { AccountMetadata } from "./pdsfetch";
const { account }: { account: AccountMetadata } = $props(); const { account }: { account: AccountMetadata } = $props();
import { Config } from "../../config";
</script> </script>
<div id="accountContainer"> <div id="accountContainer">
{#if account.avatarCid} {#if account.avatarCid}
<img <img
id="avatar" id="avatar"
alt="avatar of {account.displayName}" alt="avatar of {account.displayName}"
src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}" src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}"
/> />
{/if} {/if}
<p>{account.displayName}</p> <div id="accountName">{account.displayName || account.did}</div>
</div> </div>
<style> <style>
#accountContainer { #accountContainer {
display: column; display: flex;
text-align: start; text-align: start;
border: 2px solid black; align-items: center;
background-color: #0d0620;
padding: 4%; padding: 4%;
margin: 10px;
/* round corners */
border-radius: 10px;
}
#accountName {
margin-left: 10px;
font-size: 0.9em;
/* replace overflow with ellipsis */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 80%;
} }
#avatar { #avatar {
width: 50px; width: 50px;

View file

@ -13,13 +13,13 @@
alt="avatar of {post.displayName}" alt="avatar of {post.displayName}"
/> />
{/if} {/if}
<p>{post.displayName} | {post.timenotstamp}</p> <div id="headerText">{post.displayName} | {post.timenotstamp}</div>
</div> </div>
<div id="postContent"> <div id="postContent">
<p>{post.text}</p>
{#if post.replyingDid} {#if post.replyingDid}
<p>Replying to: {post.replyingDid}</p> <p id="replyingText">replying to: {post.replyingDid}</p>
{/if} {/if}
<p id="postText">{post.text}</p>
{#if post.imagesCid} {#if post.imagesCid}
<div id="imagesContainer"> <div id="imagesContainer">
{#each post.imagesCid as imageLink} {#each post.imagesCid as imageLink}
@ -42,21 +42,62 @@
<style> <style>
#postContainer { #postContainer {
display: column; display: flex;
text-align: start; flex-direction: column;
border: 2px solid black; border: 1px solid #8054f0;
padding: 4%; background-color: black;
margin-bottom: -1px;
} }
#postHeader { #postHeader {
text-decoration: underline; display: flex;
flex-direction: row;
align-items: center;
justify-content: start;
background-color: #1f1145;
padding: 0px 0px;
height: fit-content;
border-bottom: 1px solid #8054f0;
font-weight: bold;
}
#postContent {
display: flex;
text-align: start;
flex-direction: column;
padding: 10px;
background-color: #0d0620;
color: white;
}
#replyingText {
font-size: 0.7em;
color: white;
margin: 0;
margin-bottom: 10px;
padding: 0;
}
#postText {
margin: 0;
padding: 0;
}
#headerText {
margin-left: 10px;
font-size: 0.9em;
text-align: start;
} }
#avatar { #avatar {
width: 50px; width: 50px;
height: 50px; height: 50px;
border-radius: 50%; margin: 0px;
margin-left: 0px;
border-right: #8054f0 1px solid;
} }
#embedImages { #embedImages {
width: 50%; width: 50%;
height: 50%; height: 50%;
margin-top: 0px;
margin-bottom: -5px;
}
#embedVideo {
width: 50%;
height: 50%;
} }
</style> </style>

View file

@ -5,6 +5,7 @@ import type {
AppBskyFeedPost, AppBskyFeedPost,
ComAtprotoRepoListRecords, ComAtprotoRepoListRecords,
} from "@atcute/client/lexicons"; } from "@atcute/client/lexicons";
import { Config } from "../../config";
// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons"; // import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
// import { AppBskyFeedPost } from "@atcute/client/lexicons"; // import { AppBskyFeedPost } from "@atcute/client/lexicons";
// import { AppBskyActorDefs } from "@atcute/client/lexicons"; // import { AppBskyActorDefs } from "@atcute/client/lexicons";
@ -87,7 +88,7 @@ const didFromATuri = (aturi: string) => {
const rpc = new XRPC({ const rpc = new XRPC({
handler: simpleFetchHandler({ handler: simpleFetchHandler({
service: "https://pds.witchcraft.systems", service: Config.PDS_URL,
}), }),
}); });
@ -99,6 +100,7 @@ const getDidsFromPDS = async () => {
}; };
const getAccountMetadata = async (did: `did:${string}:${string}`) => { const getAccountMetadata = async (did: `did:${string}:${string}`) => {
// gonna assume self exists in the app.bsky.actor.profile // gonna assume self exists in the app.bsky.actor.profile
try {
const { data } = await rpc.get("com.atproto.repo.getRecord", { const { data } = await rpc.get("com.atproto.repo.getRecord", {
params: { params: {
repo: did, repo: did,
@ -116,6 +118,15 @@ const getAccountMetadata = async (did: `did:${string}:${string}`) => {
account.avatarCid = value.avatar.ref["$link"]; account.avatarCid = value.avatar.ref["$link"];
} }
return account; return account;
}
catch (e) {
console.error(`Error fetching metadata for ${did}:`, e);
return {
did: "error",
displayName: "",
avatarCid: null,
};
}
}; };
const getAllMetadataFromPds = async () => { const getAllMetadataFromPds = async () => {
@ -125,10 +136,11 @@ const getAllMetadataFromPds = async () => {
return await getAccountMetadata(repo); return await getAccountMetadata(repo);
}), }),
); );
return metadata; return metadata.filter(account => account.did !== "error");
}; };
const fetchPosts = async (did: string) => { const fetchPosts = async (did: string) => {
try {
const { data } = await rpc.get("com.atproto.repo.listRecords", { const { data } = await rpc.get("com.atproto.repo.listRecords", {
params: { params: {
repo: did, repo: did,
@ -139,7 +151,16 @@ const fetchPosts = async (did: string) => {
return { return {
records: data.records as ComAtprotoRepoListRecords.Record[], records: data.records as ComAtprotoRepoListRecords.Record[],
did: did, did: did,
error: false
}; };
} catch (e) {
console.error(`Error fetching posts for ${did}:`, e);
return {
records: [],
did: did,
error: true
};
}
}; };
const fetchAllPosts = async () => { const fetchAllPosts = async () => {
@ -149,7 +170,8 @@ const fetchAllPosts = async () => {
await fetchPosts(metadata.did) await fetchPosts(metadata.did)
), ),
); );
const posts: Post[] = postRecords.flatMap((userFetch) => const validPostRecords = postRecords.filter(record => !record.error);
const posts: Post[] = validPostRecords.flatMap((userFetch) =>
userFetch.records.map((record) => { userFetch.records.map((record) => {
const user = users.find((user: AccountMetadata) => const user = users.find((user: AccountMetadata) =>
user.did == userFetch.did user.did == userFetch.did