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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
91 lines · 2.5 KB Raw History
<script lang="ts">
	import type { Image } from '#lib/lexicons/dev/knotless/defs.ts';
	import { untrack } from 'svelte';
	import type { AtUriString } from '@atproto/lex';
	import type { Snippet } from 'svelte';
	import Icon from '#lib/atoms/Icon.svelte';
	import MarkdownField from '#lib/molecules/MarkdownField.svelte';
	/**
	 * Opening a pull or an issue: its title, from `title`, and its body in
	 * Markdown, from `text`, as the repository's template asks. `send` is
	 * given both, with the pictures uploaded into the body, and answers why it failed, in the server's words, or
	 * nothing. Its preview resolves relative links inside `repo`. Anyone
	 * not `in` has no button to send it. With `draft`, a second button
	 * of that name sends it as a draft. `facts`, drawn beside the buttons,
	 * is what it will carry as it opens.
	 */
	let {
		in: logged,
		label,
		draft,
		title = '',
		text = '',
		send,
		repo,
		facts
	}: {
		in: boolean;
		label: string;
		draft?: string;
		title?: string;
		text?: string;
		send: (
			title: string,
			body: string,
			images?: Image[],
			draft?: boolean
		) => Promise<string | undefined>;
		repo?: AtUriString;
		facts?: Snippet;
	} = $props();
	let heading = $state(untrack(() => title));
	let body = $state(untrack(() => text));
	let images = $state<Image[]>([]);
	/** Which button sent it, while it is under way: 'open' or 'draft'. */
	let busy = $state('');
	let problem = $state('');
	async function submit(e: SubmitEvent) {
		e.preventDefault();
		const drafted = (e.submitter as HTMLButtonElement | null)?.value === 'draft';
		busy = drafted ? 'draft' : 'open';
		problem = (await send(heading, body, images.length ? images : undefined, drafted)) ?? '';
		busy = '';
	}
</script>

<form class="box composer open-form" onsubmit={submit}>
	<input
		class="field title"
		name="title"
		aria-label="Title"
		placeholder="Title"
		required
		bind:value={heading}
	/>
	<MarkdownField
		label="Description"
		placeholder="What it is about, in Markdown"
		rows={14}
		{repo}
		bind:value={body}
		bind:images
	/>
	{#if problem}<p class="problem" role="alert">{problem}</p>{/if}
	<div class="composer-end">
		{@render facts?.()}
		{#if logged && draft}<button
				class="btn"
				type="submit"
				value="draft"
				disabled={!!busy || !heading.trim()}
				aria-busy={busy === 'draft'}><Icon name="pullDraft" />{draft}</button
			>{/if}
		{#if logged}<button
				class="btn primary"
				type="submit"
				value="open"
				disabled={!!busy || !heading.trim()}
				aria-busy={busy === 'open'}>{label}</button
			>{/if}
	</div>
</form>