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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
109 lines · 3.5 KB Raw History
<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}