Coves frontend - a photon fork
1<script lang="ts">
2 import { coves } from '$lib/api/client.svelte'
3 import type { CreateCommentOutput, StrongRef } from '$lib/api/coves/types'
4 import { profile } from '$lib/app/auth.svelte'
5 import { errorMessage } from '$lib/app/error'
6 import { t } from '$lib/app/i18n'
7 import Markdown from '$lib/app/markdown/Markdown.svelte'
8 import MarkdownEditor from '$lib/app/markdown/MarkdownEditor.svelte'
9 import { placeholders } from '$lib/app/util.svelte'
10 import { Button, toast } from 'mono-svelte'
11 import { Icon, XMark } from 'svelte-hero-icons/dist'
12 import type { ClassValue, HTMLTextareaAttributes } from 'svelte/elements'
13
14 interface Props extends Omit<HTMLTextareaAttributes, 'oncancel'> {
15 postRef: StrongRef
16 parentRef?: StrongRef | undefined
17 locked?: boolean
18 banned?: boolean
19 rows?: number
20 placeholder?: string | undefined
21 value?: string
22 actions?: boolean
23 tools?: boolean
24 preview?: boolean
25 class?: ClassValue
26 required?: boolean
27 id?: string
28 label?: string
29 editing?: boolean
30 oncomment?: (output: CreateCommentOutput, content: string) => void
31 onconfirm?: (value: string) => void
32 oncancel?: (cancel: boolean) => void
33 }
34
35 let {
36 postRef,
37 parentRef = undefined,
38 locked = false,
39 banned = false,
40 rows = 7,
41 placeholder = undefined,
42 value = $bindable(''),
43 actions = true,
44 preview: previewAction = true,
45 editing = false,
46 oncancel,
47 oncomment,
48 ...rest
49 }: Props = $props()
50
51 let loading = $state(false)
52 let preview = false
53
54 async function submit() {
55 if (!profile.current?.jwt) {
56 toast({ content: $t('toast.loginVoteGate'), type: 'warning' })
57 return
58 }
59 if (value.trim() === '') return
60 if (editing) return
61
62 loading = true
63
64 try {
65 const response = await coves().createComment({
66 reply: {
67 root: postRef,
68 parent: parentRef ?? postRef,
69 },
70 content: value,
71 })
72 oncomment?.(response, value)
73
74 value = ''
75 } catch (err) {
76 console.error(err)
77 toast({
78 content: errorMessage(err),
79 type: 'error',
80 })
81 }
82
83 loading = false
84 }
85</script>
86
87<form
88 onsubmit={(e) => {
89 e.preventDefault()
90 submit()
91 }}
92 class="flex flex-col gap-2 relative"
93>
94 {#if preview}
95 <div
96 class="bg-slate-100 dark:bg-zinc-900 px-3 py-2.5 h-64 border
97 border-slate-300 dark:border-zinc-700 rounded-md overflow-auto text-sm resize-y"
98 >
99 <Markdown source={value} />
100 </div>
101 {:else}
102 <MarkdownEditor
103 {...rest}
104 {rows}
105 placeholder={locked
106 ? $t('comment.locked')
107 : banned
108 ? $t('comment.banned')
109 : (placeholder ?? placeholders.get('comment'))}
110 bind:value
111 disabled={locked || loading || banned}
112 previewButton={previewAction}
113 >
114 <div class="flex-1"></div>
115 {#if actions}
116 <Button
117 size="custom"
118 title={$t('common.cancel')}
119 onclick={() => oncancel?.(true)}
120 color="tertiary"
121 class="w-8 h-8"
122 rounding="xl"
123 >
124 <Icon
125 src={XMark}
126 size="16"
127 micro
128 class="text-slate-600 dark:text-zinc-400"
129 />
130 </Button>
131 <Button
132 submit
133 color="primary"
134 rounding="xl"
135 {loading}
136 disabled={locked || loading || banned}
137 >
138 {$t('form.submit')}
139 </Button>
140 {/if}
141 </MarkdownEditor>
142 {/if}
143</form>