This repository has no description
0

Configure Feed

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

core / web / src / routes / strings / new / +page.svelte
4.2 kB 131 lines
1<script lang="ts"> 2 import { goto } from "$app/navigation"; 3 import { ok } from "@atcute/client"; 4 import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord"; 5 import type { Nsid } from "@atcute/lexicons/syntax"; 6 import { getAuth } from "$lib/auth.svelte"; 7 import { createClient } from "$lib/auth/agent"; 8 import { rkeyFromUri } from "$lib/api/uri"; 9 import Button from "$lib/components/ui/Button.svelte"; 10 import Spinner from "$lib/components/ui/Spinner.svelte"; 11 import ErrorAlert from "$lib/components/ui/Error.svelte"; 12 import ArrowUp from "$icon/arrow-up"; 13 14 const auth = getAuth(); 15 const user = $derived(auth.currentUser); 16 17 let filename = $state(""); 18 let description = $state(""); 19 let content = $state(""); 20 let isPublishing = $state(false); 21 let error = $state<string | null>(null); 22 23 const lineCount = $derived(content === "" ? 0 : content.split("\n").length); 24 const byteCount = $derived(new TextEncoder().encode(content).length); 25 26 const handleSubmit = async (e: SubmitEvent) => { 27 e.preventDefault(); 28 if (!auth.agent) return; 29 isPublishing = true; 30 error = null; 31 try { 32 const rpc = createClient(auth.agent); 33 const result = await ok( 34 rpc.call(createRecordSchema, { 35 input: { 36 repo: auth.agent.sub, 37 collection: "sh.tangled.string" as Nsid, 38 record: { 39 $type: "sh.tangled.string", 40 filename, 41 description, 42 contents: content, 43 createdAt: new Date().toISOString() 44 } 45 } 46 }) 47 ); 48 const rkey = rkeyFromUri(result.uri); 49 await goto(`/strings/${user?.handle}/${rkey}`); 50 } catch (err) { 51 error = err instanceof Error ? err.message : "Failed to publish string"; 52 } finally { 53 isPublishing = false; 54 } 55 }; 56</script> 57 58<svelte:head> 59 <title>New string &middot; Tangled</title> 60</svelte:head> 61 62<div class="mx-auto w-full max-w-screen-lg px-4 py-12"> 63 <div class="px-2 py-2"> 64 <h1 class="text-xl font-bold text-foreground-default">Create a new string</h1> 65 <p class="text-sm text-foreground-subtle">Store and share code snippets with ease.</p> 66 </div> 67 68 <div class="mt-4 rounded border border-border-default bg-background-default"> 69 <form onsubmit={handleSubmit}> 70 <div class="flex flex-col gap-2 p-4"> 71 <div class="flex flex-col gap-2 md:flex-row"> 72 <input 73 type="text" 74 id="filename" 75 name="filename" 76 placeholder="Filename" 77 required 78 bind:value={filename} 79 disabled={isPublishing} 80 class="rounded border border-border-default bg-background-default px-2 py-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" 81 /> 82 <input 83 type="text" 84 id="description" 85 name="description" 86 placeholder="Description ..." 87 maxlength={280} 88 bind:value={description} 89 disabled={isPublishing} 90 class="flex-1 rounded border border-border-default bg-background-default px-2 py-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50" 91 /> 92 </div> 93 94 <textarea 95 id="content" 96 name="content" 97 placeholder="Paste your string here!" 98 required 99 rows={20} 100 spellcheck={false} 101 bind:value={content} 102 disabled={isPublishing} 103 class="w-full resize-y rounded border border-border-default bg-background-default px-2 py-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" 104 ></textarea> 105 </div> 106 107 <div class="flex items-center justify-between px-4 py-3"> 108 <div class="text-sm text-foreground-subtle"> 109 <span>{lineCount} line{lineCount !== 1 ? "s" : ""}</span> 110 <span class="px-1 select-none">·</span> 111 <span>{byteCount} byte{byteCount !== 1 ? "s" : ""}</span> 112 </div> 113 114 <Button 115 type="submit" 116 variant="primary" 117 icon={isPublishing ? Spinner : ArrowUp} 118 disabled={isPublishing} 119 > 120 Publish 121 </Button> 122 </div> 123 124 {#if error} 125 <div class="px-4 pb-4"> 126 <ErrorAlert label={error} /> 127 </div> 128 {/if} 129 </form> 130 </div> 131</div>