johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<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>