johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
<script lang="ts">
import type { RepoViewDetailed } from '#lib/lexicons/dev/knotless/defs.ts';
import type * as forkRepo from '#lib/lexicons/dev/knotless/forkRepo.ts';
import { goto } from '$app/navigation';
import { call } from '#lib/data.ts';
import { repoHref } from '#lib/format.ts';
import { LOGIN } from '#lib/login.ts';
import Icon from '#lib/atoms/Icon.svelte';
import NameCheck from '#lib/molecules/NameCheck.svelte';
/**
* Forking `repo` into the account of whoever is logged in, `me` by
* handle, under its name or another, as `knotless repo fork` does, then on
* to the fork. One's own is not forked, so its button is disabled. For
* anyone else, the way to log in first. Beside it, how many forks of
* it there are, where the server counts them, as Star's count is.
*/
let { repo, me }: { repo: RepoViewDetailed; me?: string } = $props();
let open = $state(false);
let name = $state('');
let problem = $state('');
let busy = $state(false);
let blocked = $state(false);
const mine = $derived(me === repo.owner.handle);
const count = $derived(repo.forkCount);
// The count as a few characters, 1.2K or 3M; its title keeps every digit.
const short = new Intl.NumberFormat('en', { notation: 'compact' });
// Each time it opens, it starts from the repository's own name.
$effect(() => {
if (open) {
name = repo.name;
problem = '';
}
});
async function submit(e: SubmitEvent) {
e.preventDefault();
if (!me) return;
busy = true;
problem = '';
try {
const made = await call<typeof forkRepo.main>('dev.knotless.forkRepo', {
repo: repo.uri,
name
});
open = false;
await goto(repoHref(me, made.name));
} catch (err) {
problem = (err as Error).message;
}
busy = false;
}
</script>
{#snippet fork()}
{#if mine}
<button
class="btn"
type="button"
disabled
title="You own this repository: a fork is of someone else's"><Icon name="fork" />Fork</button
>
{:else if me}
<details class="popover fork-menu" data-held={busy || undefined} bind:open>
<summary class="btn"><Icon name="fork" />Fork</summary>
<form class="menu form" onsubmit={submit}>
<div class="menu-head">Fork into your account</div>
<label for="fork-name">Name</label>
<div class="owned">
<span class="mute">{me} /</span><input
id="fork-name"
name="name"
maxlength="100"
autocapitalize="none"
spellcheck="false"
required
bind:value={name}
/>
</div>
<!-- Asked only while it is open, not on every page with the button. -->
{#if open}<NameCheck owner={me} {name} bind:blocked />{/if}
<p class="hint">
{repo.private
? `Every branch and tag is copied. The fork is private too, in a space of its own, and ${repo.owner.handle} may read it, to take pulls from it.`
: 'Every branch and tag is copied, and the fork names where it came from.'}
</p>
{#if problem}<p class="problem" role="alert">{problem}</p>{/if}
<div>
<button
class="btn primary"
type="submit"
disabled={busy || (open && blocked)}
aria-busy={busy}>{busy ? 'Forking…' : 'Create fork'}</button
>
</div>
</form>
</details>
{:else}
<button class="btn" type="button" popovertarget={LOGIN}><Icon name="fork" />Fork</button>
{/if}
{/snippet}
{#if count === undefined}
{@render fork()}
{:else}
<div class="counted">
{@render fork()}<span class="count" title="{count} {count === 1 ? 'fork' : 'forks'}"
>{short.format(count)}</span
>
</div>
{/if}