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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
165 lines · 6.1 KB Raw History
<script lang="ts">
	import type * as createPull from '#lib/lexicons/dev/knotless/createPull.ts';
	import type { Image } from '#lib/lexicons/dev/knotless/defs.ts';
	import { goto } from '$app/navigation';
	import { call } from '#lib/data.ts';
	import { sourceAt } from '#lib/diffs.ts';
	import { itemHref, plural, repoHref, subject } from '#lib/format.ts';
	import { LOGIN } from '#lib/login.ts';
	import Icon from '#lib/atoms/Icon.svelte';
	import CommitEvent from '#lib/organisms/CommitEvent.svelte';
	import Diff from '#lib/organisms/Diff.svelte';
	import OpenForm from '#lib/organisms/OpenForm.svelte';
	import OpenFacts from '#lib/organisms/OpenFacts.svelte';
	import RepoPage from '#lib/templates/RepoPage.svelte';
	import Empty from '#lib/molecules/Empty.svelte';
	let { data } = $props();
	const r = $derived(data.repo);
	const base = $derived(repoHref(data.authority, r.name));
	const me = $derived(data.viewer?.actor?.did);
	const ch = $derived(data.changes);
	/** Where its files are after it: at its branch's head, shown on that branch. */
	const source = $derived(ch?.head ? sourceAt(ch.repo, ch.head, data.branch) : undefined);
	/** The labels whoever is looking may put on it, and who may be asked to review it: the owner and collaborators, besides themselves. */
	const offered = $derived(r.viewer?.labels ?? []);
	const people = $derived([r.owner, ...(r.collaborators ?? [])].filter((p) => p.did !== me));
	let labels = $state<string[]>([]);
	let reviewers = $state<(typeof r.owner.did)[]>([]);
	/** Whether the form to open it shows: once asked for, or at once from Compare & pull. */
	let asked = $state(false);
	const writing = $derived(asked || data.expand);
	const uriOf = (h: { did: string; name: string }) => `at://${h.did}/dev.knotless.repo/${h.name}`;
	const from = $derived(data.heads.find((h) => uriOf(h) === data.head));
	const same = $derived(data.head === r.uri && data.branch === data.base);
	// A pull of one commit is titled as the commit is; of more, as its branch.
	const title = $derived(ch?.commits.length === 1 ? subject(ch.commits[0].message) : data.branch);
	/** Asks again for what is chosen; a repository chosen anew starts with no branch. */
	function chose(e: Event & { currentTarget: HTMLSelectElement }) {
		const form = e.currentTarget.form;
		if (!form) return;
		if (e.currentTarget.name === 'head')
			(form.elements.namedItem('branch') as HTMLSelectElement).value = '';
		form.requestSubmit();
	}
	async function open(title: string, body: string, images?: Image[], draft?: boolean) {
		const input = {
			repo: r.uri,
			base: data.base,
			head: data.head,
			branch: data.branch,
			title,
			body: body || undefined,
			images,
			draft: draft || undefined,
			labels: labels.length ? labels.map((name) => ({ name })) : undefined,
			reviewers: reviewers.length ? reviewers.map((did) => ({ did })) : undefined
		};
		const opened = await call<typeof createPull.main>('dev.knotless.createPull', input).catch(
			(e: Error) => e
		);
		if (opened instanceof Error) return opened.message;
		await goto(itemHref(base, opened.uri));
	}
</script>

<RepoPage repo={r} tab="pulls" title="New pull · {r.name}">
	<h2 class="form-title">Open a pull</h2>
	<p class="mute form-lead">
		Choose the branch to merge into, then the branch to bring in, from this repository or a fork of
		it.
	</p>
	<form class="box compare" method="get">
		<label
			><span>Into</span><select class="field" name="base" onchange={chose}
				>{#each data.bases as b (b)}<option value={b} selected={b === data.base}>{b}</option
					>{/each}</select
			></label
		>
		<Icon name="pull" />
		<label
			><span>From</span><select class="field" name="head" onchange={chose}
				>{#each data.heads as h (uriOf(h))}<option
						value={uriOf(h)}
						selected={uriOf(h) === data.head}
						>{h.did === me ? `${h.handle}/${h.name} (yours)` : `${h.handle}/${h.name}`}</option
					>{/each}</select
			></label
		>
		<label
			><span>Branch</span><select class="field" name="branch" onchange={chose}
				><option value="">Choose a branch</option>{#each data.branches as b (b)}<option
						value={b}
						selected={b === data.branch}>{b}</option
					>{/each}</select
			></label
		>
		<noscript><button class="btn" type="submit">Compare</button></noscript>
	</form>
	{#if !data.branch}
		<div class="box">
			<Empty title="No branch chosen">Choose a branch to see what it brings in.</Empty>
		</div>
	{:else if same}
		<div class="box">
			<Empty title="Nothing to compare"
				>A branch cannot be merged into itself. Choose another.</Empty
			>
		</div>
	{:else if !ch}
		<div class="box">
			<Empty title="Branch not found"
				>{`${from ? `${from.handle}/${from.name}` : 'That repository'} has no branch ${data.branch}, or ${r.name} no branch ${data.base}.`}</Empty
			>
		</div>
	{:else}
		{#if writing}
			{#key `${data.head} ${data.branch} ${data.base}`}
				<OpenForm
					repo={`at://${data.authority}/dev.knotless.repo/${r.name}`}
					in={!!me}
					label="Open pull"
					draft="Open as draft"
					{title}
					text={data.template}
					send={open}
				>
					{#snippet facts()}{#if me}<OpenFacts
								{offered}
								bind:labels
								{people}
								bind:reviewers
							/>{/if}{/snippet}
				</OpenForm>
			{/key}
		{:else if ch.commits.length}
			<div class="box compare-ready">
				<span
					>{`${plural(ch.commits.length, 'commit', 'commits')} and ${plural(ch.files.length, 'file', 'files')} changed, from ${data.branch} into ${data.base}.`}</span
				>
				{#if me}<button class="btn primary" type="button" onclick={() => (asked = true)}
						>Open pull</button
					>{:else}<button class="btn primary" type="button" popovertarget={LOGIN}
						>Log in to open a pull</button
					>{/if}
			</div>
		{/if}
		{#if ch.commits.length}
			<div class="timeline compared">
				<CommitEvent
					author={from?.handle ?? ''}
					commits={ch.commits}
					at={repoHref(ch.repo.handle, ch.repo.name)}
				/>
			</div>
		{:else}
			<div class="box">
				<Empty title="Nothing to compare"
					>{`${data.branch} has nothing ${data.base} does not.`}</Empty
				>
			</div>
		{/if}
		{#if ch.files.length}<section class="pull-diff">
				<Diff files={ch.files} {source} />
			</section>{/if}
	{/if}
</RepoPage>