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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
95 lines · 3.1 KB Raw History
<script lang="ts" module>
	/** What a review of a pull found, if it is one. */
	export type Verdict = 'approve' | 'changes';
</script>

<script lang="ts">
	import type { Image } from '#lib/lexicons/dev/knotless/defs.ts';
	import type { Snippet } from 'svelte';
	import { untrack } from 'svelte';
	import type { AtUriString } from '@atproto/lex';
	import MarkdownField from '#lib/molecules/MarkdownField.svelte';
	/**
	 * Writing something in Markdown: a comment at the end of a thread, a
	 * pull's review, or a change to what was written, starting from `text`.
	 * `send` is given what was written, the pictures uploaded into it, and
	 * with `review` the verdict chosen, and answers why it failed, in the server's words, or nothing,
	 * when the box empties, or `cancel` closes it. `extra` goes beside the
	 * button. Its preview resolves relative links inside `repo`. Nothing for
	 * anyone not `in`. `ready`, it may be sent saying nothing, as a review
	 * whose line comments say it. `rows` is how tall its box starts.
	 */
	let {
		in: logged,
		label = 'Comment',
		text = '',
		placeholder = 'Leave a comment',
		review = false,
		ready = false,
		send,
		cancel,
		extra,
		repo,
		rows
	}: {
		in: boolean;
		label?: string;
		text?: string;
		placeholder?: string;
		review?: boolean;
		ready?: boolean;
		send: (body: string, verdict?: Verdict, images?: Image[]) => Promise<string | undefined>;
		cancel?: () => void;
		extra?: Snippet;
		repo?: AtUriString;
		rows?: number;
	} = $props();
	let body = $state(untrack(() => text));
	let verdict = $state<'' | Verdict>('');
	let images = $state<Image[]>([]);
	let busy = $state(false);
	let problem = $state('');
	// A review may say nothing past its verdict; anything else says something.
	const empty = $derived(!body.trim() && !verdict && !ready);
	async function submit(e: SubmitEvent) {
		e.preventDefault();
		busy = true;
		problem = (await send(body, verdict || undefined, images.length ? images : undefined)) ?? '';
		if (!problem && !cancel) {
			body = '';
			verdict = '';
			images = [];
		}
		busy = false;
	}
</script>

{#if logged}
	<form class="box composer" onsubmit={submit}>
		<MarkdownField label={placeholder} {placeholder} {rows} {repo} bind:value={body} bind:images />
		{#if review}
			<fieldset class="review">
				<legend>Review</legend>
				<label><input type="radio" name="verdict" value="" bind:group={verdict} />Comment</label>
				<label
					><input type="radio" name="verdict" value="approve" bind:group={verdict} />Approve</label
				>
				<label
					><input type="radio" name="verdict" value="changes" bind:group={verdict} />Request changes</label
				>
			</fieldset>
		{/if}
		{#if problem}<p class="problem" role="alert">{problem}</p>{/if}
		<div class="composer-end">
			{#if extra}{@render extra()}{/if}
			{#if cancel}<button class="btn" type="button" onclick={cancel}>Cancel</button>{/if}
			<button class="btn primary" type="submit" disabled={busy || empty} aria-busy={busy}
				>{verdict === 'approve'
					? 'Approve'
					: verdict === 'changes'
						? 'Request changes'
						: label}</button
			>
		</div>
	</form>
{/if}