johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts" module>
export type Kind = 'all' | 'sources' | 'forks' | 'shared';
export type Order = 'pushed' | 'created' | 'name';
</script>
<script lang="ts">
import type { RepoView } from '#lib/lexicons/dev/knotless/defs.ts';
import type * as getActorActivity from '#lib/lexicons/dev/knotless/getActorActivity.ts';
import { ago, date, repoHref } from '#lib/format.ts';
import { RECENT, weekly } from '#lib/activity.ts';
import Avatar from '#lib/atoms/Avatar.svelte';
import Icon from '#lib/atoms/Icon.svelte';
import Language from '#lib/atoms/Language.svelte';
import PrivateTag from '#lib/atoms/PrivateTag.svelte';
import Spark from '#lib/atoms/Spark.svelte';
import Time from '#lib/atoms/Time.svelte';
import FilterMenu from '#lib/molecules/FilterMenu.svelte';
import type { Snippet } from 'svelte';
import Empty from '#lib/molecules/Empty.svelte';
/**
* All of an account's repositories, those whose name or description
* holds `q`, of `kind`, in `order`: each with what it is, the language
* it is mostly written in, when it was last pushed to or made, and,
* when `pushes` says, its pushes each week of the last few to `today`;
* or, as `shared`, those of others shared with it, by owner and name.
* The filters are links from `base`.
*/
let {
repos,
shared = [],
pushes,
today,
q = '',
kind = 'all',
order = 'pushed',
base,
empty
}: {
repos: RepoView[];
shared?: RepoView[];
pushes: getActorActivity.RepoActivity[] | null;
today: string;
q?: string;
kind?: Kind;
order?: Order;
base: string;
empty: Snippet;
} = $props();
// Pushes are the account's own: none are counted for what is shared.
const pushed = $derived(new Map(kind === 'shared' ? [] : (pushes ?? []).map((p) => [p.name, p])));
const last = (r: RepoView) => pushed.get(r.name)?.pushedAt ?? r.createdAt;
const listed = $derived(kind === 'shared' ? shared : repos);
// What is shared has no pushes of the account's to sort by: newest first.
const sort = $derived(kind === 'shared' && order === 'pushed' ? 'created' : order);
const shown = $derived.by(() => {
const words = q.trim().toLowerCase();
const found = listed.filter(
(r) =>
(kind === 'all' || kind === 'shared' || (kind === 'forks') === Boolean(r.forkOf)) &&
(!words || `${r.name} ${r.description ?? ''}`.toLowerCase().includes(words))
);
const by = {
pushed: (a: RepoView, b: RepoView) => last(b).localeCompare(last(a)),
created: (a: RepoView, b: RepoView) => b.createdAt.localeCompare(a.createdAt),
name: (a: RepoView, b: RepoView) => a.name.localeCompare(b.name)
}[sort];
return found.sort(by);
});
const href = (to: { kind?: Kind; order?: Order; q?: string }) => {
const k = to.kind ?? kind;
const o = to.order ?? order;
const words = to.q ?? q;
const params = new URLSearchParams({ tab: 'repos' });
if (words) params.set('q', words);
if (k !== 'all') params.set('kind', k);
if (o !== 'pushed') params.set('sort', o);
return `${base}?${params}`;
};
const orders: [Order, string][] = [
['pushed', 'Recently pushed'],
['created', 'Newest'],
['name', 'Name']
];
const sorts = $derived(
orders
.filter(([o]) => kind !== 'shared' || o !== 'pushed')
.map(([o, name]) => ({ name, href: href({ order: o }), on: o === sort }))
);
const weeks = (name: string) =>
weekly(pushed.get(name)?.days ?? [], today, RECENT, (d) => d.pushes);
</script>
<form class="query" role="search" action={base}>
<Icon name="search" /><input type="hidden" name="tab" value="repos" /><input
type="search"
name="q"
value={q}
placeholder="Find a repository"
aria-label="Find a repository"
/>
{#if kind !== 'all'}<input
type="hidden"
name="kind"
value={kind}
/>{/if}{#if order !== 'pushed'}<input type="hidden" name="sort" value={order} />{/if}
</form>
<div class="box items">
<div class="boxhead" data-sveltekit-reset="false">
<nav class="filter">
<a href={href({ kind: 'all' })} aria-current={kind === 'all' ? 'page' : undefined}>All</a>
<a href={href({ kind: 'sources' })} aria-current={kind === 'sources' ? 'page' : undefined}
>Sources</a
>
<a href={href({ kind: 'forks' })} aria-current={kind === 'forks' ? 'page' : undefined}
>Forks</a
>
{#if shared.length || kind === 'shared'}<a
href={href({ kind: 'shared' })}
aria-current={kind === 'shared' ? 'page' : undefined}>Shared</a
>{/if}
</nav>
<div class="menus"><FilterMenu title="Sort" head="Sort by" options={sorts} /></div>
</div>
<ul class="list">
{#each shown as r (`${r.owner.did}/${r.name}`)}
{@const p = pushed.get(r.name)}
<li>
{#if kind === 'shared'}<Avatar
name={r.owner.handle}
size="lg"
did={r.owner.did}
src={r.owner.avatar}
/>{/if}
<div class="grow">
<a class="title" href={repoHref(r.owner.handle, r.name)}
>{#if kind === 'shared'}<span class="mute">{r.owner.handle}/</span>{/if}{r.name}</a
>{#if r.private}{' '}<PrivateTag />{/if}
<div class="meta">{r.description ?? 'No description'}</div>
{#if r.forkOf}<div class="meta">
<Icon name="fork" />Forked from
<a href={repoHref(r.forkOf.handle, r.forkOf.name)}
>{`${r.forkOf.handle}/${r.forkOf.name}`}</a
>
</div>{/if}
<div class="meta">
{#if r.language}<Language language={r.language} />{/if}{#if p}<span
>{'Pushed '}<Time at={p.pushedAt} show={ago} /></span
>{:else}<span>{'Created '}<Time at={r.createdAt} show={date} /></span>{/if}
</div>
</div>
{#if pushes && kind !== 'shared'}<span class="hide-sm"
><Spark
values={weeks(r.name)}
label="Pushes each week, the last {RECENT} weeks"
/></span
>{/if}
</li>
{:else}
<li class="blank">
{#if listed.length && q.trim()}
<Empty title="No repositories match"><a href={href({ q: '' })}>Clear the search</a></Empty
>
{:else if listed.length}
<Empty title={kind === 'forks' ? 'No forks yet' : 'No sources yet'}
><a href={href({ kind: 'all' })}>See all repositories</a></Empty
>
{:else if kind === 'shared'}
<Empty title="No repositories shared with you yet" />
{:else}
{@render empty()}
{/if}
</li>
{/each}
</ul>
</div>