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