a simple pds frontend for listing accounts and generating invite codes
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add email link. improve mobile layout

Winter 2c495d22 0525f22d

+55 -11
+13 -4
src/routes/+page.server.ts
··· 11 11 } 12 12 13 13 export const load: PageServerLoad = async () => { 14 - const listResult = await publicRpc.get('com.atproto.sync.listRepos', { 15 - params: { limit: 1000 }, 16 - }); 14 + const [listResult, describeServer] = await Promise.all([ 15 + publicRpc.get('com.atproto.sync.listRepos', { 16 + params: { limit: 1000 }, 17 + }), 18 + publicRpc.get('com.atproto.server.describeServer', {}).catch(() => null), 19 + ]); 20 + 21 + const contactEmail = 22 + describeServer?.data?.contact && 23 + typeof (describeServer.data.contact as Record<string, unknown>).email === 'string' 24 + ? (describeServer.data.contact as Record<string, unknown>).email as string 25 + : null; 17 26 18 27 if (listResult.data.cursor) { 19 28 console.warn('[listRepos] response truncated at 1000 — cursor present, some accounts not shown'); ··· 66 75 .filter((r) => r.status === 'fulfilled') 67 76 .map((r) => (r as PromiseFulfilledResult<Account>).value); 68 77 69 - return { accounts }; 78 + return { accounts, contactEmail }; 70 79 };
+42 -7
src/routes/+page.svelte
··· 56 56 <header> 57 57 <div class="header-title"> 58 58 <h1>pds.madoka.systems</h1> 59 - <a class="source-link" href="https://tangled.org/madoka.systems/tea-party">source</a> 59 + <div class="header-links"> 60 + {#if data.contactEmail} 61 + <a class="header-link" href="mailto:{data.contactEmail}">contact</a> 62 + {/if} 63 + <a class="header-link" href="https://tangled.org/madoka.systems/tea-party">source</a> 64 + </div> 60 65 </div> 61 66 <p class="subtitle">{data.accounts.length} accounts</p> 62 67 </header> ··· 160 165 161 166 <style> 162 167 main { 163 - padding: 1rem; 168 + padding: 0; 164 169 } 165 170 166 171 .container { 167 - max-width: 600px; 168 - margin-inline: auto; 169 - margin-block: 0.5rem; 170 - border: 2px solid var(--color-fg); 171 172 background-color: var(--color-surface); 172 173 } 173 174 175 + @media (min-width: 640px) { 176 + main { 177 + padding: 1rem; 178 + } 179 + 180 + .container { 181 + max-width: 600px; 182 + margin-inline: auto; 183 + margin-block: 0.5rem; 184 + border: 2px solid var(--color-fg); 185 + } 186 + } 187 + 174 188 header { 175 189 padding: 1.25rem 1.5rem 1rem; 176 190 } 177 191 178 192 .header-title { 179 193 display: flex; 180 - align-items: center; 194 + align-items: baseline; 181 195 justify-content: space-between; 196 + flex-wrap: wrap; 197 + gap: 0.5rem; 198 + } 199 + 200 + .header-title h1 { 201 + font-size: clamp(1rem, 5vw, 1.5rem); 202 + min-width: 0; 203 + overflow: hidden; 204 + text-overflow: ellipsis; 205 + white-space: nowrap; 206 + } 207 + 208 + .header-links { 209 + display: flex; 210 + gap: 0.75rem; 211 + flex-shrink: 0; 212 + } 213 + 214 + .header-link { 215 + font-size: 0.875rem; 216 + white-space: nowrap; 182 217 } 183 218 184 219 .subtitle {