This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / strings / StringForm.svelte
4.1 kB 137 lines
1<script lang="ts"> 2 import { untrack } from "svelte"; 3 import { now as tidNow } from "@atcute/tid"; 4 import Button from "$lib/components/ui/Button.svelte"; 5 import Spinner from "$lib/components/ui/Spinner.svelte"; 6 import ErrorAlert from "$lib/components/ui/Error.svelte"; 7 import ArrowUp from "$icon/arrow-up"; 8 import X from "$icon/x"; 9 import { getAuth } from "$lib/auth.svelte"; 10 import { putString } from "$lib/api/strings"; 11 import type { StringRecord } from "$lib/api/records"; 12 import { createAction } from "$lib/action.svelte"; 13 14 interface Props { 15 mode?: "create" | "edit"; 16 rkey?: string; 17 createdAt?: string; 18 filename?: string; 19 description?: string; 20 content?: string; 21 onsaved: (rkey: string) => void; 22 oncancel?: () => void; 23 } 24 25 let { 26 mode = "create", 27 rkey, 28 createdAt, 29 filename: initialFilename = "", 30 description: initialDescription = "", 31 content: initialContent = "", 32 onsaved, 33 oncancel 34 }: Props = $props(); 35 36 const auth = getAuth(); 37 38 let filename = $state(untrack(() => initialFilename)); 39 let description = $state(untrack(() => initialDescription)); 40 let content = $state(untrack(() => initialContent)); 41 42 const lineCount = $derived(content === "" ? 0 : content.split("\n").length); 43 const byteCount = $derived(new TextEncoder().encode(content).length); 44 45 const publish = createAction(async (event: SubmitEvent) => { 46 event.preventDefault(); 47 const agent = auth.agent; 48 if (!agent) return; 49 const targetRkey = rkey ?? tidNow(); 50 const record: StringRecord = { 51 $type: "sh.tangled.string", 52 filename, 53 description, 54 contents: content, 55 createdAt: createdAt ?? new Date().toISOString() 56 }; 57 await putString(agent, targetRkey, record); 58 onsaved(targetRkey); 59 }); 60</script> 61 62<div class="rounded-sm border border-border-default bg-background-default"> 63 <form onsubmit={publish.run}> 64 <div class="flex flex-col gap-2 p-4"> 65 <div class="flex flex-col gap-2 md:flex-row"> 66 <input 67 type="text" 68 id="filename" 69 name="filename" 70 placeholder="Filename" 71 required 72 bind:value={filename} 73 disabled={publish.loading} 74 class="rounded-sm border border-border-default bg-background-default p-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50 md:max-w-64" 75 /> 76 <input 77 type="text" 78 id="description" 79 name="description" 80 placeholder="Description ..." 81 maxlength={280} 82 bind:value={description} 83 disabled={publish.loading} 84 class="flex-1 rounded-sm border border-border-default bg-background-default p-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50" 85 /> 86 </div> 87 88 <textarea 89 id="content" 90 name="content" 91 placeholder="Paste your string here!" 92 required 93 rows={20} 94 spellcheck={false} 95 bind:value={content} 96 disabled={publish.loading} 97 class="w-full resize-y rounded-sm border border-border-default bg-background-default p-2 font-mono text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50" 98 ></textarea> 99 </div> 100 101 <div class="flex items-center justify-between px-4 py-3"> 102 <div class="text-sm text-foreground-subtle"> 103 <span>{lineCount} line{lineCount !== 1 ? "s" : ""}</span> 104 <span class="px-1 select-none">·</span> 105 <span>{byteCount} byte{byteCount !== 1 ? "s" : ""}</span> 106 </div> 107 108 <div class="flex items-center gap-2"> 109 {#if mode === "edit"} 110 <Button 111 type="button" 112 variant="default" 113 icon={X} 114 disabled={publish.loading} 115 onclick={oncancel} 116 > 117 Cancel 118 </Button> 119 {/if} 120 <Button 121 type="submit" 122 variant="primary" 123 icon={publish.loading ? Spinner : ArrowUp} 124 disabled={publish.loading} 125 > 126 Publish 127 </Button> 128 </div> 129 </div> 130 131 {#if publish.error} 132 <div class="px-4 pb-4"> 133 <ErrorAlert label={publish.error} /> 134 </div> 135 {/if} 136 </form> 137</div>