~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
219 lines · 8.0 KB Raw History
<script lang="ts">
	import type * as getFeed from '#lib/lexicons/dev/knotless/getFeed.ts';
	import type { Entry } from '#lib/feed.ts';
	import type { IconName } from '#lib/icons.ts';
	import { stateIcon } from '#lib/icons.ts';
	import { span } from '#lib/feed.ts';
	import {
		changed,
		commits,
		excerpt,
		itemHref,
		kindOf,
		repoHref,
		said,
		shortRef,
		subject,
		urlPath
	} from '#lib/format.ts';
	import Avatar from '#lib/atoms/Avatar.svelte';
	import BotMark from '#lib/atoms/BotMark.svelte';
	import Icon from '#lib/atoms/Icon.svelte';
	import Ref from '#lib/atoms/Ref.svelte';
	import Time from '#lib/atoms/Time.svelte';
	import RepoLink from './RepoLink.svelte';
	import CommitList from './CommitList.svelte';
	import Crowd from './Crowd.svelte';
	/**
	 * A line of the feed: who did it, by picture and name, with a mark for
	 * what kind of thing it was; a sentence; and what it brought or said.
	 * Every line names its repository, so it reads alone however lines of
	 * other repositories fall between; on the account's own page, `own`,
	 * its picture and name are left out.
	 */
	let { entry: x, own }: { entry: Entry; own: boolean } = $props();
	// The kinds this page knows; any other falls to the switch's default, as
	// its $type matches none of theirs.
	type Known = Extract<getFeed.EventView['action'], { $type: `dev.knotless.getFeed#${string}` }>;
	const a = $derived(x.kind === 'event' ? (x.event.action as Known) : null);
	const repo = $derived(repoHref(x.repo.handle, x.repo.name));
	// A branch is often gone once its pull lands, so its log is of the
	// commit the push left it at, which stays.
	const log = (b: { ref: string; to?: string }) =>
		`${repo}/commits/${b.to ?? urlPath(shortRef(b.ref))}`;

	/**
	 * A lone event's sentence after who did it: words, then what it was done
	 * to, then whether the repository follows. Each run of words is one
	 * text node, so it is shaped as one.
	 */
	type Thing =
		| { ref: string }
		| { item: string; title: string }
		| { fork: { handle: string; name: string } }
		| { repo: true }
		| null;
	const sentence = $derived.by((): [string, Thing, boolean] => {
		switch (a?.$type) {
			case 'dev.knotless.getFeed#created':
				return a.forkOf
					? ['forked ', { fork: a.forkOf }, false]
					: ['created ', { repo: true }, false];
			case 'dev.knotless.getFeed#deleted':
				return ['deleted ', { ref: shortRef(a.ref) }, true];
			case 'dev.knotless.getFeed#opened':
				return [`opened ${kindOf(a.subject)} `, { item: a.subject, title: a.title }, true];
			case 'dev.knotless.getFeed#commented':
				return [
					`${said(a.verdict)} ${kindOf(a.subject)} `,
					{ item: a.subject, title: a.title },
					true
				];
			case 'dev.knotless.getFeed#changed':
				return [
					`${changed(a.state)} ${kindOf(a.subject)} `,
					{ item: a.subject, title: a.title },
					true
				];
			default:
				// A kind of event this page does not know yet.
				return ['did something ', null, true];
		}
	});
	const [lead, thing, on] = $derived(sentence);

	const mark = $derived.by((): [IconName, string] => {
		if (x.kind === 'pushes') return ['commit', ''];
		if (x.kind === 'merged') return ['merge', 'merged'];
		if (x.kind === 'starred') return ['star', 'starred'];
		switch (a?.$type) {
			case 'dev.knotless.getFeed#created':
				return [a.forkOf ? 'fork' : 'repo', ''];
			case 'dev.knotless.getFeed#deleted':
				return ['branch', ''];
			case 'dev.knotless.getFeed#opened':
				return [kindOf(a.subject) === 'pull' ? 'pull' : 'issue', 'open'];
			case 'dev.knotless.getFeed#commented':
				return ['chat', ''];
			case 'dev.knotless.getFeed#changed':
				return [stateIcon(kindOf(a.subject), a.state), a.state === 'open' ? 'open' : ''];
			default:
				return ['running', ''];
		}
	});
	const [icon, tone] = $derived(mark);
	const others = $derived(x.kind === 'starred' ? x.actors.length - 1 : 0);
	// One push: its ref, alone, in the sentence.
	const one = $derived(x.kind === 'pushes' && x.branches.length + x.caught.length === 1 ? x : null);
	const pushed = $derived(one?.branches[0]);
</script>

{#snippet where()}{' on '}<RepoLink owner={x.repo.handle} name={x.repo.name} />{/snippet}
{#snippet who()}{#if !own}<a class="who" href="/{urlPath(x.actor.handle)}">{x.actor.handle}</a
		>{#if x.actor.bot}<BotMark />{/if}{/if}{own ? '' : ' '}{/snippet}
{#snippet rest(
	b: { count: number; merges: number; elsewhere: number; ref: string; to?: string },
	listed: number
)}<p class="feed-rest">
		{#if b.count > listed}<span class="feed-badge">+{b.count - listed} more</span
			>{/if}{#if b.merges}<span class="feed-badge"
				>{b.merges === 1 ? '1 merge' : `${b.merges} merges`}</span
			>{/if}{#if b.elsewhere}<span class="feed-badge">{`+${b.elsewhere} from other branches`}</span
			>{/if}<a class="feed-log" href={log(b)}>Commits →</a>
	</p>{/snippet}

<li>
	<Time at={x.createdAt} show={(at) => span(x.since, at)} />
	<span class="feed-node" class:own
		>{#if !own}<Avatar name={x.actor.handle} did={x.actor.did} src={x.actor.avatar} />{/if}<span
			class="feed-kind {tone}"><Icon name={icon} /></span
		></span
	>
	<div class="grow">
		{#if x.kind === 'pushes' && one && pushed}
			<p>
				{@render who()}{pushed.forced
					? 'force-pushed '
					: pushed.created
						? 'created '
						: `pushed ${commits(pushed.count)} to `}<Ref
					name={shortRef(pushed.ref)}
				/>{@render where()}
			</p>
			<CommitList commits={pushed.commits} {repo} />
			{@render rest(pushed, Math.min(pushed.commits.length, 5))}
		{:else if x.kind === 'pushes' && one}
			<p>
				{@render who()}brought <Ref name={shortRef(one.caught[0])} /> up to date{@render where()}
			</p>
		{:else if x.kind === 'pushes'}
			<p>
				{@render who()}{x.branches.length
					? `pushed to ${x.branches.length + x.caught.length} branches`
					: `brought ${x.caught.length} branches up to date`}{@render where()}
			</p>
			<ul class="feed-branches">
				{#each x.branches as b, i (i)}<li>
						<Ref name={shortRef(b.ref)} /><span class="clip"
							>{b.commits[0] ? subject(b.commits[0].message) : ''}</span
						><span class="feed-end"
							><span class="feed-count">{b.created ? `new · ${b.count}` : b.count}</span><a
								class="feed-log"
								href={log(b)}>Commits →</a
							></span
						>
					</li>{/each}
				{#each x.caught as ref, i (i)}<li class="caught">
						<Ref name={shortRef(ref)} /><span class="clip">brought up to date</span>
					</li>{/each}
			</ul>
		{:else if x.kind === 'merged'}
			<p>
				{@render who()}merged pull
				<a href={itemHref(repo, x.subject)}>{x.title}</a>{@render where()}
			</p>
			{#if x.landed}<p class="feed-rest">
					{`${commits(x.landed.count)} to `}<Ref name={shortRef(x.landed.ref)} /><a
						class="feed-log"
						href={log(x.landed)}>Commits →</a
					>
				</p>{/if}
		{:else if x.kind === 'starred' && x.actors.length > 1}
			<Crowd actors={x.actors} />
			<p>
				<a class="who" href="/{urlPath(x.actor.handle)}">{x.actor.handle}</a
				>{` and ${others === 1 ? '1 other' : `${others} others`} starred `}<RepoLink
					owner={x.repo.handle}
					name={x.repo.name}
				/>
			</p>
		{:else if x.kind === 'starred'}
			<p>
				{@render who()}{x.repos.length > 1
					? `starred ${x.repos.length} repositories: `
					: 'starred '}{#each x.repos as r, i (i)}{i ? ', ' : ''}<RepoLink
						owner={r.handle}
						name={r.name}
					/>{/each}
			</p>
		{:else}
			<p>
				{@render who()}{lead}{#if thing && 'ref' in thing}<Ref
						name={thing.ref}
					/>{:else if thing && 'item' in thing}<a href={itemHref(repo, thing.item)}>{thing.title}</a
					>{:else if thing && 'fork' in thing}<RepoLink
						owner={thing.fork.handle}
						name={thing.fork.name}
					/> to <RepoLink owner={x.repo.handle} name={x.repo.name} />{:else if thing}<RepoLink
						owner={x.repo.handle}
						name={x.repo.name}
					/>{/if}{#if on}{@render where()}{/if}
			</p>
			{#if a?.$type === 'dev.knotless.getFeed#created' && a.description}
				<p class="feed-note">{a.description}</p>
			{:else if a?.$type === 'dev.knotless.getFeed#commented'}
				<p class="feed-quote">“{excerpt(a.text, 120)}”</p>
			{/if}
		{/if}
	</div>
</li>