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