diff --git a/web/src/lib/components/repo/issues/IssueCard.stories.svelte b/web/src/lib/components/repo/issues/IssueCard.stories.svelte
index 3a462985..2191e863 100644
--- a/web/src/lib/components/repo/issues/IssueCard.stories.svelte
+++ b/web/src/lib/components/repo/issues/IssueCard.stories.svelte
@@ -29,3 +29,4 @@
+
diff --git a/web/src/lib/components/repo/issues/IssueCard.svelte b/web/src/lib/components/repo/issues/IssueCard.svelte
index a3617442..0df24590 100644
--- a/web/src/lib/components/repo/issues/IssueCard.svelte
+++ b/web/src/lib/components/repo/issues/IssueCard.svelte
@@ -1,47 +1,27 @@
-
+
+
+
diff --git a/web/src/lib/components/repo/issues/IssueCardContent.svelte b/web/src/lib/components/repo/issues/IssueCardContent.svelte
new file mode 100644
index 00000000..1911d052
--- /dev/null
+++ b/web/src/lib/components/repo/issues/IssueCardContent.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
diff --git a/web/src/lib/components/settings/KeyCard.stories.svelte b/web/src/lib/components/settings/KeyCard.stories.svelte
new file mode 100644
index 00000000..95273e42
--- /dev/null
+++ b/web/src/lib/components/settings/KeyCard.stories.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/web/src/lib/components/settings/KeyCard.svelte b/web/src/lib/components/settings/KeyCard.svelte
index 87a0f7e8..befcc902 100644
--- a/web/src/lib/components/settings/KeyCard.svelte
+++ b/web/src/lib/components/settings/KeyCard.svelte
@@ -1,9 +1,6 @@
-
-
-
-
- {name}
-
- {#if fingerprint}
-
{fingerprint}
- {/if}
-
added {relativeTime(createdAt)}
-
-
-
+
+
+
diff --git a/web/src/lib/components/settings/KeyCardContent.svelte b/web/src/lib/components/settings/KeyCardContent.svelte
new file mode 100644
index 00000000..e1e0d2de
--- /dev/null
+++ b/web/src/lib/components/settings/KeyCardContent.svelte
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {name}
+
+ {#if fingerprint}
+
{fingerprint}
+ {/if}
+
added {relativeTime(createdAt)}
+
+
+
diff --git a/web/src/lib/components/timeline/TimelineCard.svelte b/web/src/lib/components/timeline/TimelineCard.svelte
new file mode 100644
index 00000000..d5cf52d6
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineCard.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+ {@render header()}
+
+
+ {@render children()}
+
+
diff --git a/web/src/lib/components/timeline/TimelineFollowEvent.stories.svelte b/web/src/lib/components/timeline/TimelineFollowEvent.stories.svelte
new file mode 100644
index 00000000..ffeed555
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineFollowEvent.stories.svelte
@@ -0,0 +1,38 @@
+
+
+
+ {#snippet template(args)}
+
+
+
+ {/snippet}
+
diff --git a/web/src/lib/components/timeline/TimelineFollowEvent.svelte b/web/src/lib/components/timeline/TimelineFollowEvent.svelte
new file mode 100644
index 00000000..54df1f63
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineFollowEvent.svelte
@@ -0,0 +1,21 @@
+
+
+
+ {#snippet header()}
+
+ followed
+
+ {relativeTime(eventAt)}
+ {/snippet}
+
+
+
diff --git a/web/src/lib/components/timeline/TimelineRepoEvent.stories.svelte b/web/src/lib/components/timeline/TimelineRepoEvent.stories.svelte
new file mode 100644
index 00000000..2f81839e
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineRepoEvent.stories.svelte
@@ -0,0 +1,56 @@
+
+
+
+ {#snippet template(args)}
+
+
+
+ {/snippet}
+
+
+
+ {#snippet template(args)}
+
+
+
+ {/snippet}
+
diff --git a/web/src/lib/components/timeline/TimelineRepoEvent.svelte b/web/src/lib/components/timeline/TimelineRepoEvent.svelte
new file mode 100644
index 00000000..fb35ab83
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineRepoEvent.svelte
@@ -0,0 +1,34 @@
+
+
+
+ {#snippet header()}
+
+ {#if event.source}
+ forked
+ {event.source.owner.handle}/{event.source.slug}
+ to {event.repo.slug}
+ {:else}
+ created {event.repo.slug}
+ {/if}
+ {relativeTime(eventAt)}
+ {/snippet}
+
+
+
diff --git a/web/src/lib/components/timeline/TimelineStarEvent.stories.svelte b/web/src/lib/components/timeline/TimelineStarEvent.stories.svelte
new file mode 100644
index 00000000..8264c528
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineStarEvent.stories.svelte
@@ -0,0 +1,41 @@
+
+
+
+ {#snippet template(args)}
+
+
+
+ {/snippet}
+
diff --git a/web/src/lib/components/timeline/TimelineStarEvent.svelte b/web/src/lib/components/timeline/TimelineStarEvent.svelte
new file mode 100644
index 00000000..66efda70
--- /dev/null
+++ b/web/src/lib/components/timeline/TimelineStarEvent.svelte
@@ -0,0 +1,29 @@
+
+
+
+ {#snippet header()}
+
+ starred
+ {event.repo.owner.handle}/{event.repo.slug}
+ {relativeTime(eventAt)}
+ {/snippet}
+
+
+
diff --git a/web/src/lib/components/timeline/mappers.ts b/web/src/lib/components/timeline/mappers.ts
new file mode 100644
index 00000000..ead8b47d
--- /dev/null
+++ b/web/src/lib/components/timeline/mappers.ts
@@ -0,0 +1,26 @@
+import { resolve } from "$app/paths";
+import { rkeyFromUri } from "$lib/api/uri";
+import type { RepoCardData, PersonData } from "$lib/components/profile/types";
+import type * as ShTangledRepoDefs from "$lib/api/lexicons/types/sh/tangled/repo/defs";
+import type * as ShTangledActorDefs from "$lib/api/lexicons/types/sh/tangled/actor/defs";
+
+export const toRepoCard = (repo: ShTangledRepoDefs.RepoViewBasic): RepoCardData => ({
+ name: repo.slug,
+ repoDid: repo.did,
+ ownerHandle: repo.owner.handle,
+ description: repo.description,
+ createdAt: repo.createdAt,
+ stars: repo.starCount,
+ viewerStarRkey: repo.viewer?.star ? rkeyFromUri(repo.viewer.star) : null
+});
+
+export const toPerson = (profile: ShTangledActorDefs.ProfileViewDetailed): PersonData => ({
+ did: profile.did,
+ handle: profile.handle,
+ followers: profile.followersCount,
+ following: profile.followsCount,
+ viewerFollowRkey: profile.viewer?.following ? rkeyFromUri(profile.viewer.following) : null
+});
+
+export const repoHref = (repo: ShTangledRepoDefs.RepoViewBasic) =>
+ resolve(`/${repo.owner.handle}/${repo.slug}` as "/");
diff --git a/web/src/lib/components/ui/Card.stories.svelte b/web/src/lib/components/ui/Card.stories.svelte
new file mode 100644
index 00000000..b131d615
--- /dev/null
+++ b/web/src/lib/components/ui/Card.stories.svelte
@@ -0,0 +1,39 @@
+
+
+
+
Card content
+
Bordered card with shadow
+
+ Borderless, shadowless, background-less
+
+
+
+
+ Bordered, no shadow (profile / settings)
+ Bordered + shadow (elevated)
+ Plain (timeline)
+
+
diff --git a/web/src/lib/components/ui/Card.svelte b/web/src/lib/components/ui/Card.svelte
new file mode 100644
index 00000000..e11d37f0
--- /dev/null
+++ b/web/src/lib/components/ui/Card.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+ {#if children}
+ {@render children()}
+ {/if}
+
diff --git a/web/src/routes/timeline/+page.svelte b/web/src/routes/timeline/+page.svelte
index 4cf45608..14b9507b 100644
--- a/web/src/routes/timeline/+page.svelte
+++ b/web/src/routes/timeline/+page.svelte
@@ -4,39 +4,12 @@
import Button from "$lib/components/ui/Button.svelte";
import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte";
import EmptyState from "$lib/components/ui/EmptyState.svelte";
- import User from "$lib/components/ui/User.svelte";
- import RepoCard from "$lib/components/repo/RepoCard.svelte";
- import FollowCard from "$lib/components/profile/FollowCard.svelte";
- import { rkeyFromUri } from "$lib/api/uri";
- import { relativeTime } from "$lib/format";
- import type { RepoCardData, PersonData } from "$lib/components/profile/types";
- import type * as ShTangledRepoDefs from "$lib/api/lexicons/types/sh/tangled/repo/defs";
- import type * as ShTangledActorDefs from "$lib/api/lexicons/types/sh/tangled/actor/defs";
+ import TimelineRepoEvent from "$lib/components/timeline/TimelineRepoEvent.svelte";
+ import TimelineStarEvent from "$lib/components/timeline/TimelineStarEvent.svelte";
+ import TimelineFollowEvent from "$lib/components/timeline/TimelineFollowEvent.svelte";
import type { PageData } from "./$types";
let { data }: { data: PageData } = $props();
-
- const toRepoCard = (repo: ShTangledRepoDefs.RepoViewBasic): RepoCardData => ({
- name: repo.slug,
- repoDid: repo.did,
- ownerHandle: repo.owner.handle,
- description: repo.description,
- createdAt: repo.createdAt,
- stars: repo.starCount,
- viewerStarRkey: repo.viewer?.star ? rkeyFromUri(repo.viewer.star) : null
- });
-
- const toPerson = (profile: ShTangledActorDefs.ProfileViewDetailed): PersonData => ({
- did: profile.did,
- handle: profile.handle,
- avatar: profile.avatar,
- followers: profile.followersCount,
- following: profile.followsCount,
- viewerFollowRkey: profile.viewer?.following ? rkeyFromUri(profile.viewer.following) : null
- });
-
- const repoHref = (repo: ShTangledRepoDefs.RepoViewBasic) =>
- resolve(`/${repo.owner.handle}/${repo.slug}` as "/");
@@ -65,39 +38,13 @@
{#each data.feed as item (item.event.uri)}
{@const event = item.event}
-
-
- {#if event.$type === "sh.tangled.feed.getTimeline#repoEvent"}
-
- {#if event.source}
-
forked
-
{event.source.owner.handle}/{event.source.slug}
-
to {event.repo.slug}
- {:else}
-
created {event.repo.slug}
- {/if}
- {:else if event.$type === "sh.tangled.feed.getTimeline#starEvent"}
-
-
starred
-
{event.repo.owner.handle}/{event.repo.slug}
- {:else if event.$type === "sh.tangled.feed.getTimeline#followEvent"}
-
-
followed
-
- {/if}
-
{relativeTime(item.eventAt)}
-
-
- {#if event.$type === "sh.tangled.feed.getTimeline#followEvent"}
-
- {:else}
-
- {/if}
-
+ {#if event.$type === "sh.tangled.feed.getTimeline#repoEvent"}
+
+ {:else if event.$type === "sh.tangled.feed.getTimeline#starEvent"}
+
+ {:else if event.$type === "sh.tangled.feed.getTimeline#followEvent"}
+
+ {/if}
{:else}
{
const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch });
const viewer = parent.auth?.did;
- // the following feed needs a viewer to resolve the followed set; bobbin 400s without one
const followingOnly = event.url.searchParams.get("following") === "true" && viewer !== undefined;
try {
- // output key is `feed`, not `items`, so the pagination.ts helpers don't apply
const { feed } = await jsonGet(
ctx,
"sh.tangled.feed.getTimeline",