this repo has no description

Somewhat sensible design (WIP)

astrra.space dcca38a9 164571ec

verified
+162 -75
+35 -1
src/App.svelte
··· 7 7 </script> 8 8 9 9 <main> 10 - <h1>Welcome to the Feed</h1> 10 + <div id="Content"> 11 11 {#await accountsPromise} 12 12 <p>Loading...</p> 13 13 {:then accountsData} 14 14 <div id="Account"> 15 + <h1 id="Header">ATProto PDS</h1> 16 + <p>Home to {accountsData.length} accounts</p> 15 17 {#each accountsData as accountObject} 16 18 <AccountComponent account={accountObject} /> 17 19 {/each} ··· 29 31 {/each} 30 32 </div> 31 33 {/await} 34 + </div> 32 35 </main> 33 36 34 37 <style> 38 + #Content { 39 + display: flex; 40 + /* split the screen in half, left for accounts, right for posts */ 41 + width: 100%; 42 + height: 100%; 43 + flex-direction: row; 44 + justify-content: space-between; 45 + align-items: center; 46 + background-color: #12082b; 47 + color: #ffffff; 48 + } 49 + #Feed { 50 + width: 65%; 51 + height: 80vh; 52 + overflow-y: scroll; 53 + padding: 20px; 54 + } 55 + #Account { 56 + width: 35%; 57 + height: 80vh; 58 + overflow-y: scroll; 59 + padding: 20px; 60 + background-color: #070311; 61 + 62 + border-radius: 10px; 63 + } 64 + #Header { 65 + text-align: center; 66 + font-size: 2em; 67 + margin-bottom: 20px; 68 + } 35 69 </style>
+20 -46
src/app.css
··· 1 - :root { 2 - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; 3 - line-height: 1.5; 4 - font-weight: 400; 1 + @font-face { 2 + font-family: 'ProggyClean'; 3 + src: url(https://witchcraft.systems/ProggyCleanNerdFont-Regular.ttf); 4 + } 5 5 6 - color-scheme: light dark; 7 - color: rgba(255, 255, 255, 0.87); 8 - background-color: #242424; 6 + ::-webkit-scrollbar { 7 + width: 0px; 8 + background: transparent; 9 + } 9 10 10 - font-synthesis: none; 11 - text-rendering: optimizeLegibility; 12 - -webkit-font-smoothing: antialiased; 13 - -moz-osx-font-smoothing: grayscale; 11 + * { 12 + scrollbar-width: thin; 13 + scrollbar-color: transparent transparent; 14 + -ms-overflow-style: none; /* IE and Edge */ 15 + -webkit-overflow-scrolling: touch; 16 + -webkit-scrollbar: none; /* Safari */ 14 17 } 15 18 16 19 a { ··· 28 31 place-items: center; 29 32 min-width: 320px; 30 33 min-height: 100vh; 34 + background-color: #12082b; 35 + font-family: 'ProggyClean', monospace; 36 + font-size: 24px; 37 + color: white; 38 + border-color: #8054f0; 31 39 } 32 40 33 41 h1 { ··· 35 43 line-height: 1.1; 36 44 } 37 45 38 - .card { 39 - padding: 2em; 40 - } 41 - 42 46 #app { 43 - max-width: 1280px; 47 + max-width: 1400px; 44 48 margin: 0 auto; 45 49 padding: 2rem; 46 50 text-align: center; 47 51 } 48 52 49 - button { 50 - border-radius: 8px; 51 - border: 1px solid transparent; 52 - padding: 0.6em 1.2em; 53 - font-size: 1em; 54 - font-weight: 500; 55 - font-family: inherit; 56 - background-color: #1a1a1a; 57 - cursor: pointer; 58 - transition: border-color 0.25s; 59 - } 60 - button:hover { 61 - border-color: #646cff; 62 - } 63 - button:focus, 64 - button:focus-visible { 65 - outline: 4px auto -webkit-focus-ring-color; 66 - } 67 53 68 - @media (prefers-color-scheme: light) { 69 - :root { 70 - color: #213547; 71 - background-color: #ffffff; 72 - } 73 - a:hover { 74 - color: #747bff; 75 - } 76 - button { 77 - background-color: #f9f9f9; 78 - } 79 - }
+20 -4
src/lib/AccountComponent.svelte
··· 1 1 <script lang="ts"> 2 2 import type { AccountMetadata } from "./pdsfetch"; 3 3 const { account }: { account: AccountMetadata } = $props(); 4 + import { Config } from "../../config"; 4 5 </script> 5 6 <div id="accountContainer"> 6 7 {#if account.avatarCid} 7 8 <img 8 9 id="avatar" 9 10 alt="avatar of {account.displayName}" 10 - src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}" 11 + src="{Config.PDS_URL}/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}" 11 12 /> 12 13 {/if} 13 - <p>{account.displayName}</p> 14 + <div id="accountName">{account.displayName || account.did}</div> 14 15 </div> 15 16 <style> 16 17 #accountContainer { 17 - display: column; 18 + display: flex; 18 19 text-align: start; 19 - border: 2px solid black; 20 + align-items: center; 21 + background-color: #0d0620; 20 22 padding: 4%; 23 + margin: 10px; 24 + 25 + /* round corners */ 26 + border-radius: 10px; 27 + } 28 + #accountName { 29 + margin-left: 10px; 30 + font-size: 0.9em; 31 + 32 + /* replace overflow with ellipsis */ 33 + overflow: hidden; 34 + text-overflow: ellipsis; 35 + white-space: nowrap; 36 + max-width: 80%; 21 37 } 22 38 #avatar { 23 39 width: 50px;
+51 -10
src/lib/PostComponent.svelte
··· 13 13 alt="avatar of {post.displayName}" 14 14 /> 15 15 {/if} 16 - <p>{post.displayName} | {post.timenotstamp}</p> 16 + <div id="headerText">{post.displayName} | {post.timenotstamp}</div> 17 17 </div> 18 18 <div id="postContent"> 19 - <p>{post.text}</p> 20 19 {#if post.replyingDid} 21 - <p>Replying to: {post.replyingDid}</p> 22 - {/if} 20 + <p id="replyingText">replying to: {post.replyingDid}</p> 21 + {/if} 22 + <p id="postText">{post.text}</p> 23 23 {#if post.imagesCid} 24 24 <div id="imagesContainer"> 25 25 {#each post.imagesCid as imageLink} ··· 42 42 43 43 <style> 44 44 #postContainer { 45 - display: column; 45 + display: flex; 46 + flex-direction: column; 47 + border: 1px solid #8054f0; 48 + background-color: black; 49 + margin-bottom: -1px; 50 + } 51 + #postHeader { 52 + display: flex; 53 + flex-direction: row; 54 + align-items: center; 55 + justify-content: start; 56 + background-color: #1f1145; 57 + padding: 0px 0px; 58 + height: fit-content; 59 + border-bottom: 1px solid #8054f0; 60 + font-weight: bold; 61 + } 62 + #postContent { 63 + display: flex; 46 64 text-align: start; 47 - border: 2px solid black; 48 - padding: 4%; 65 + flex-direction: column; 66 + padding: 10px; 67 + background-color: #0d0620; 68 + color: white; 69 + } 70 + #replyingText { 71 + font-size: 0.7em; 72 + color: white; 73 + margin: 0; 74 + margin-bottom: 10px; 75 + padding: 0; 49 76 } 50 - #postHeader { 51 - text-decoration: underline; 77 + #postText { 78 + margin: 0; 79 + padding: 0; 80 + } 81 + #headerText { 82 + margin-left: 10px; 83 + font-size: 0.9em; 84 + text-align: start; 52 85 } 53 86 #avatar { 54 87 width: 50px; 55 88 height: 50px; 56 - border-radius: 50%; 89 + margin: 0px; 90 + margin-left: 0px; 91 + border-right: #8054f0 1px solid; 57 92 } 58 93 #embedImages { 94 + width: 50%; 95 + height: 50%; 96 + margin-top: 0px; 97 + margin-bottom: -5px; 98 + } 99 + #embedVideo { 59 100 width: 50%; 60 101 height: 50%; 61 102 }
+36 -14
src/lib/pdsfetch.ts
··· 5 5 AppBskyFeedPost, 6 6 ComAtprotoRepoListRecords, 7 7 } from "@atcute/client/lexicons"; 8 + import { Config } from "../../config"; 8 9 // import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons"; 9 10 // import { AppBskyFeedPost } from "@atcute/client/lexicons"; 10 11 // import { AppBskyActorDefs } from "@atcute/client/lexicons"; ··· 87 88 88 89 const rpc = new XRPC({ 89 90 handler: simpleFetchHandler({ 90 - service: "https://pds.witchcraft.systems", 91 + service: Config.PDS_URL, 91 92 }), 92 93 }); 93 94 ··· 99 100 }; 100 101 const getAccountMetadata = async (did: `did:${string}:${string}`) => { 101 102 // gonna assume self exists in the app.bsky.actor.profile 103 + try { 102 104 const { data } = await rpc.get("com.atproto.repo.getRecord", { 103 105 params: { 104 106 repo: did, ··· 116 118 account.avatarCid = value.avatar.ref["$link"]; 117 119 } 118 120 return account; 121 + } 122 + catch (e) { 123 + console.error(`Error fetching metadata for ${did}:`, e); 124 + return { 125 + did: "error", 126 + displayName: "", 127 + avatarCid: null, 128 + }; 129 + } 119 130 }; 120 131 121 132 const getAllMetadataFromPds = async () => { ··· 125 136 return await getAccountMetadata(repo); 126 137 }), 127 138 ); 128 - return metadata; 139 + return metadata.filter(account => account.did !== "error"); 129 140 }; 130 141 131 142 const fetchPosts = async (did: string) => { 132 - const { data } = await rpc.get("com.atproto.repo.listRecords", { 133 - params: { 134 - repo: did, 135 - collection: "app.bsky.feed.post", 136 - limit: 5, 137 - }, 138 - }); 139 - return { 140 - records: data.records as ComAtprotoRepoListRecords.Record[], 141 - did: did, 142 - }; 143 + try { 144 + const { data } = await rpc.get("com.atproto.repo.listRecords", { 145 + params: { 146 + repo: did, 147 + collection: "app.bsky.feed.post", 148 + limit: 5, 149 + }, 150 + }); 151 + return { 152 + records: data.records as ComAtprotoRepoListRecords.Record[], 153 + did: did, 154 + error: false 155 + }; 156 + } catch (e) { 157 + console.error(`Error fetching posts for ${did}:`, e); 158 + return { 159 + records: [], 160 + did: did, 161 + error: true 162 + }; 163 + } 143 164 }; 144 165 145 166 const fetchAllPosts = async () => { ··· 149 170 await fetchPosts(metadata.did) 150 171 ), 151 172 ); 152 - const posts: Post[] = postRecords.flatMap((userFetch) => 173 + const validPostRecords = postRecords.filter(record => !record.error); 174 + const posts: Post[] = validPostRecords.flatMap((userFetch) => 153 175 userFetch.records.map((record) => { 154 176 const user = users.find((user: AccountMetadata) => 155 177 user.did == userFetch.did