-
-
Notifications
You must be signed in to change notification settings - Fork 479
feat: add PDS landing page #1750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
d47b3ab
24196d6
790f6c8
ebc78de
792a650
cb3791f
1e79737
685bd09
9047e49
d35efbc
ab84ed3
d0ee467
4f9df46
6d73507
cff42e1
65d03bb
9b101bf
023c77c
ecc9664
bbf7270
2932dbc
2fe2631
ead7f52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| <script setup lang="ts"> | ||
| import type { AtprotoProfile } from '#shared/types/atproto' | ||
|
|
||
| const router = useRouter() | ||
| const canGoBack = useCanGoBack() | ||
|
|
||
| useSeoMeta({ | ||
| title: () => `${$t('pds.title')} - npmx`, | ||
| ogTitle: () => `${$t('pds.title')} - npmx`, | ||
| twitterTitle: () => `${$t('pds.title')} - npmx`, | ||
| description: () => $t('pds.meta_description'), | ||
| ogDescription: () => $t('pds.meta_description'), | ||
| twitterDescription: () => $t('pds.meta_description'), | ||
| }) | ||
|
|
||
| defineOgImageComponent('Default', { | ||
| primaryColor: '#60a5fa', | ||
| title: 'npmx.social', | ||
| description: 'The official **PDS** for the npmx community.', | ||
| }) | ||
|
|
||
| const brokenImages = ref(new Set<string>()) | ||
|
|
||
| const handleImageError = (handle: string) => { | ||
| brokenImages.value.add(handle) | ||
| } | ||
|
|
||
| const { data: pdsUsers, status: pdsStatus } = useLazyFetch<AtprotoProfile[]>( | ||
| '/api/atproto/pds-users', | ||
| { | ||
| default: () => [], | ||
| }, | ||
| ) | ||
|
|
||
| const usersWithAvatars = computed(() => { | ||
| return pdsUsers.value.filter(user => user.avatar && !brokenImages.value.has(user.handle)) | ||
| }) | ||
| </script> | ||
|
|
||
| <template> | ||
| <main class="container flex-1 py-12 sm:py-16 overflow-x-hidden"> | ||
| <article class="max-w-2xl mx-auto"> | ||
| <header class="mb-12"> | ||
| <div class="flex items-baseline justify-between gap-4 mb-4"> | ||
| <h1 class="font-mono text-3xl sm:text-4xl font-medium"> | ||
| {{ $t('pds.title') }} | ||
| </h1> | ||
| <button | ||
| type="button" | ||
| class="cursor-pointer inline-flex items-center gap-2 p-1.5 -mx-1.5 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-accent/70 shrink-0" | ||
| @click="router.back()" | ||
| v-if="canGoBack" | ||
| > | ||
| <span class="i-lucide:arrow-left rtl-flip w-4 h-4" aria-hidden="true" /> | ||
| <span class="hidden sm:inline">{{ $t('nav.back') }}</span> | ||
| </button> | ||
| </div> | ||
| <p class="text-fg-muted text-lg"> | ||
| {{ $t('pds.meta_description') }} | ||
| </p> | ||
| </header> | ||
|
|
||
| <section class="max-w-none space-y-12"> | ||
| <div> | ||
| <h2 class="text-lg text-fg uppercase tracking-wider mb-4"> | ||
| {{ $t('pds.join.title') }} | ||
| </h2> | ||
| <p class="text-fg-muted leading-relaxed mb-4"> | ||
| {{ $t('pds.join.description') }} | ||
| </p> | ||
| <div class="mt-6"> | ||
| <LinkBase | ||
| to="https://pdsmoover.com/moover/npmx.social" | ||
| class="gap-2 px-4 py-2 text-sm font-medium rounded-md border border-border hover:border-border-hover bg-bg-muted hover:bg-bg" | ||
| no-underline | ||
| > | ||
| <span class="i-lucide:arrow-right-left w-4 h-4 text-fg-muted" aria-hidden="true" /> | ||
| {{ $t('pds.join.migrate') }} | ||
| </LinkBase> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div> | ||
| <h2 class="text-lg text-fg uppercase tracking-wider mb-4"> | ||
| {{ $t('pds.server.title') }} | ||
| </h2> | ||
| <ul class="space-y-3 text-fg-muted list-none p-0"> | ||
| <li class="flex items-start gap-3"> | ||
| <span | ||
| class="i-lucide:map-pin shrink-0 mt-1 w-4 h-4 text-fg-subtle" | ||
| aria-hidden="true" | ||
| /> | ||
| <span> | ||
| <strong class="text-fg">{{ $t('pds.server.location_label') }}</strong> | ||
| {{ $t('pds.server.location_value') }} | ||
| </span> | ||
| </li> | ||
| <li class="flex items-start gap-3"> | ||
| <span | ||
| class="i-lucide:server shrink-0 mt-1 w-4 h-4 text-fg-subtle" | ||
| aria-hidden="true" | ||
| /> | ||
| <span> | ||
| <strong class="text-fg">{{ $t('pds.server.infrastructure_label') }}</strong> | ||
| {{ $t('pds.server.infrastructure_value') }} | ||
| </span> | ||
| </li> | ||
| <li class="flex items-start gap-3"> | ||
| <span | ||
| class="i-lucide:shield-check shrink-0 mt-1 w-4 h-4 text-fg-subtle" | ||
| aria-hidden="true" | ||
| /> | ||
| <span> | ||
| <strong class="text-fg">{{ $t('pds.server.privacy_label') }}</strong> | ||
| {{ $t('pds.server.privacy_value') }} | ||
| </span> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| <div aria-labelledby="community-heading"> | ||
| <h2 id="community-heading" class="text-lg text-fg uppercase tracking-wider mb-4"> | ||
| {{ $t('pds.community.title') }} | ||
| </h2> | ||
| <p class="text-fg-muted leading-relaxed mb-6"> | ||
| {{ $t('pds.community.description') }} | ||
| </p> | ||
|
|
||
| <div v-if="pdsStatus === 'pending'" class="text-fg-subtle text-sm" role="status"> | ||
| {{ $t('pds.community.loading') }} | ||
| </div> | ||
| <div v-else-if="pdsStatus === 'error'" class="text-fg-subtle text-sm" role="alert"> | ||
| {{ $t('pds.community.error') }} | ||
| </div> | ||
| <div v-else-if="!usersWithAvatars.length" class="text-fg-subtle text-sm"> | ||
| {{ $t('pds.community.empty') }} | ||
| </div> | ||
| <ul | ||
| v-else | ||
| class="grid grid-cols-[repeat(auto-fill,48px)] justify-center gap-2 list-none p-0" | ||
| > | ||
| <li v-for="user in usersWithAvatars" :key="user.handle" class="block group relative"> | ||
| <a | ||
| :href="`https://bsky.app/profile/${user.handle}`" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| :aria-label="$t('pds.community.view_profile', { handle: user.handle })" | ||
| class="block rounded-lg" | ||
| > | ||
| <img | ||
| :src="user.avatar" | ||
| :alt="`${user.handle}'s avatar`" | ||
| @error="handleImageError(user.handle)" | ||
| width="48" | ||
| height="48" | ||
| class="w-12 h-12 rounded-lg ring-2 ring-transparent group-hover:ring-accent transition-all duration-200 ease-out group-hover:scale-125 will-change-transform" | ||
| loading="lazy" | ||
| /> | ||
| <span | ||
| class="pointer-events-none absolute -top-9 inset-is-1/2 -translate-x-1/2 whitespace-nowrap rounded-md bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900 text-xs px-2 py-1 shadow-lg opacity-0 scale-95 transition-all duration-150 group-hover:opacity-100 group-hover:scale-100" | ||
| dir="ltr" | ||
| role="tooltip" | ||
| > | ||
| @{{ user.handle }} | ||
| </span> | ||
| </a> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </section> | ||
| </article> | ||
| </main> | ||
| </template> | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1165,6 +1165,33 @@ | |||||
| "close_files_panel": "Close files panel", | ||||||
| "filter_files_label": "Filter files by change type" | ||||||
| }, | ||||||
| "pds": { | ||||||
| "title": "npmx.social", | ||||||
| "meta_description": "The official AT Protocol Personal Data Server (PDS) for the npmx community.", | ||||||
| "og_description": "The official **PDS** for the npmx community.", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use plain text in OG description metadata. On Line 1171, 💡 Suggested tweak- "og_description": "The official **PDS** for the npmx community.",
+ "og_description": "The official PDS for the npmx community.",📝 Committable suggestion
Suggested change
|
||||||
| "join": { | ||||||
| "title": "Join the Community", | ||||||
| "description": "Whether you are creating your first Bluesky account or migrating an existing one, you belong here. You can migrate your current account without losing your handle, your posts, or your followers.", | ||||||
|
fatfingers23 marked this conversation as resolved.
Outdated
|
||||||
| "migrate": "Migrate with PDS MOOver" | ||||||
| }, | ||||||
| "server": { | ||||||
| "title": "Server Details", | ||||||
| "location_label": "Location:", | ||||||
| "location_value": "Nuremberg, Germany", | ||||||
| "infrastructure_label": "Infrastructure:", | ||||||
| "infrastructure_value": "Hosted on Hetzner", | ||||||
| "privacy_label": "Privacy:", | ||||||
| "privacy_value": "Subject to strict EU Data Protection laws" | ||||||
| }, | ||||||
| "community": { | ||||||
| "title": "Who is here", | ||||||
| "description": "They are already calling npmx.social home.", | ||||||
| "loading": "Loading PDS community...", | ||||||
| "error": "Failed to load PDS community.", | ||||||
| "empty": "No community members to display.", | ||||||
| "view_profile": "View {handle}'s profile" | ||||||
| } | ||||||
| }, | ||||||
| "privacy_policy": { | ||||||
| "title": "privacy policy", | ||||||
| "last_updated": "Last updated: {date}", | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.