Keep a room message in the composer and toast when sending it fails, instead of dropping it silently

This commit is contained in:
Jon Staab 2026-09-25 15:47:44 -07:00
parent 790700dd9f
commit 326fb33bca
2 changed files with 69 additions and 58 deletions

View file

@ -174,65 +174,64 @@
return
}
try {
const protect = await shouldProtect
const protect = await shouldProtect
if (eventToEdit) {
// Don't do anything if message hasn't changed
if (eventToEdit.content === content) {
return
}
// Delete the previous message, to be republished below with the same timestamp
const command = await $deletes.deleteEvent($state.snapshot(eventToEdit), w =>
w.setProtected(protect),
)
command.publishToRelays([url])
if (eventToEdit) {
// Don't do anything if message hasn't changed
if (eventToEdit.content === content) {
return clearEventToEdit()
}
// A share is a quote rather than a reply, so setParent prepends the reply's own reference ahead of it.
if (sharedEvent) {
;({content, tags} = await prependParent(sharedEvent, {content, tags}, url))
}
// Delete the previous message, to be republished below with the same timestamp
const command = await $deletes.deleteEvent($state.snapshot(eventToEdit), w =>
w.setProtected(protect),
)
const eventWriter = writer(Message)
.setContent(content)
.addTags(...tags)
.setProtected(protect)
if (h) {
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
if (parent) {
eventWriter.setParent(parent)
}
const delay = await loadSendDelay()
const thunk = $thunks.publish({
relays: [url],
event: stamp(await eventWriter.renderTemplate(), eventToEdit?.created_at),
delay,
})
if (delay) {
pushToast({
timeout: 30_000,
children: {
component: ThunkToast,
props: {thunk},
},
})
}
} finally {
clearParent()
clearShare()
clearEventToEdit()
command.publishToRelays([url])
}
// A share is a quote rather than a reply, so setParent prepends the reply's own reference ahead of it.
if (sharedEvent) {
;({content, tags} = await prependParent(sharedEvent, {content, tags}, url))
}
const eventWriter = writer(Message)
.setContent(content)
.addTags(...tags)
.setProtected(protect)
if (h) {
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
if (parent) {
eventWriter.setParent(parent)
}
const delay = await loadSendDelay()
const thunk = $thunks.publish({
relays: [url],
event: stamp(await eventWriter.renderTemplate(), eventToEdit?.created_at),
delay,
})
if (delay) {
pushToast({
timeout: 30_000,
children: {
component: ThunkToast,
props: {thunk},
},
})
}
// Only once it's sent, so a failure leaves the reply or edit in place to try again.
clearParent()
clearShare()
clearEventToEdit()
}
const getElementKey = (element: {id: string}) => element.id

View file

@ -1,6 +1,6 @@
<script lang="ts">
import {writable} from "svelte/store"
import type {Maybe} from "@welshman/lib"
import type {Maybe, MaybeAsync} from "@welshman/lib"
import {MESSAGE} from "@welshman/util"
import type {EventContent} from "@welshman/util"
import {escapeHtml, isMobile, preventDefault} from "@lib/html"
@ -20,6 +20,7 @@
import {makeEditor} from "@app/editor"
import {app} from "@app/core"
import {getDictation} from "@app/dictation"
import {pushToast} from "@app/toast"
import {DraftKey, type Draft} from "@app/drafts"
import type {Share} from "@app/share"
import {onDestroy, onMount} from "svelte"
@ -29,7 +30,7 @@
h?: string
onEscape?: () => void
onEditPrevious?: () => void
onSubmit: (event: EventContent) => void
onSubmit: (event: EventContent) => MaybeAsync<void>
initialValues?: Share
}
@ -100,7 +101,7 @@
const hidePopover = () => tippy?.hide()
const submit = async () => {
if ($uploading) {
if ($uploading || sending) {
return
}
@ -108,12 +109,23 @@
const content = ed.getText({blockSeparator: "\n"}).trim()
const tags = ed.storage.nostr.getEditorTags()
onSubmit({content, tags})
sending = true
try {
await onSubmit({content, tags})
} catch (error) {
console.error("Failed to send message", error)
return pushToast({theme: "error", message: "Failed to send your message."})
} finally {
sending = false
}
draftKey?.clear()
ed.chain().clearContent(true).run()
}
let sending = false
let tippy: Maybe<TippyController> = $state()
let content = $state(
initialValues?.type === "text" ? initialValues.value : (draftKey?.get()?.content ?? ""),