fix(chat): publish kind 9 quote after room content creation for cross-client interoperability

This commit is contained in:
2026-04-10 22:56:48 +05:45
parent 1d5f91fb6c
commit d4293063d2
11 changed files with 168 additions and 43 deletions
@@ -7,12 +7,13 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
}
const {url, h}: Props = $props()
const {url, h, shareToChat = false}: Props = $props()
</script>
<CalendarEventForm {url} {h}>
<CalendarEventForm {url} {h} {shareToChat}>
{#snippet header()}
<ModalHeader>
<ModalTitle>Create an Event</ModalTitle>
+25 -6
View File
@@ -3,7 +3,7 @@
import {writable} from "svelte/store"
import {randomId, HOUR} from "@welshman/lib"
import {makeEvent, EVENT_TIME} from "@welshman/util"
import {publishThunk} from "@welshman/app"
import {publishThunk, waitForThunkError} from "@welshman/app"
import {preventDefault} from "@lib/html"
import {daysBetween} from "@lib/util"
import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
@@ -22,7 +22,7 @@
import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/util/drafts"
import {pushToast} from "@app/util/toast"
import {canEnforceNip70} from "@app/core/commands"
import {canEnforceNip70, publishRoomQuote} from "@app/core/commands"
type Values = {
d: string
@@ -36,11 +36,12 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
header: Snippet
initialValues?: Values
}
let {url, h, header, initialValues}: Props = $props()
let {url, h, shareToChat = false, header, initialValues}: Props = $props()
const draftKey = new DraftKey<Values>(`calendar:${url}:${h ?? ""}`)
@@ -92,7 +93,9 @@
...ed.storage.nostr.getEditorTags(),
]
if (await shouldProtect) {
const protect = await shouldProtect
if (protect) {
tags.push(PROTECTED)
}
@@ -101,11 +104,27 @@
}
const event = makeEvent(EVENT_TIME, {content, tags})
const calendarThunk = publishThunk({event, relays: [url]})
history.back()
const error = await waitForThunkError(calendarThunk)
if (error) {
return pushToast({theme: "error", message: error})
}
if (shareToChat) {
const quoteThunk = publishRoomQuote({url, h, parent: calendarThunk.event, protect})
const quoteError = await waitForThunkError(quoteThunk)
if (quoteError) {
pushToast({theme: "error", message: quoteError})
}
}
pushToast({message: "Your event has been saved!"})
publishThunk({event, relays: [url]})
draftKey.clear()
history.back()
}
const d = $state(initialValues?.d ?? randomId())
+3 -2
View File
@@ -7,12 +7,13 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
}
const {url, h}: Props = $props()
const {url, h, shareToChat = false}: Props = $props()
</script>
<ClassifiedForm {url} {h}>
<ClassifiedForm {url} {h} {shareToChat}>
{#snippet header()}
<ModalHeader>
<ModalTitle>Create a Classified Listing</ModalTitle>
+25 -6
View File
@@ -2,7 +2,7 @@
import type {Snippet} from "svelte"
import {removeUndefined, randomId, uniq} from "@welshman/lib"
import {makeEvent, CLASSIFIED} from "@welshman/util"
import {publishThunk} from "@welshman/app"
import {publishThunk, waitForThunkError} from "@welshman/app"
import {isMobile, preventDefault} from "@lib/html"
import {normalizeTopic} from "@lib/util"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
@@ -21,7 +21,7 @@
import {PROTECTED} from "@app/core/state"
import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/util/drafts"
import {canEnforceNip70, uploadFile} from "@app/core/commands"
import {canEnforceNip70, publishRoomQuote, uploadFile} from "@app/core/commands"
type Values = {
d: string
@@ -37,11 +37,12 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
header: Snippet
initialValues?: Values
}
let {url, h, header, initialValues}: Props = $props()
let {url, h, shareToChat = false, header, initialValues}: Props = $props()
const draftKey = new DraftKey<Values>(`classified:${url}:${h ?? ""}`)
@@ -87,7 +88,9 @@
tags.push(["t", topic])
}
if (await shouldProtect) {
const protect = await shouldProtect
if (protect) {
tags.push(PROTECTED)
}
@@ -114,13 +117,29 @@
}
}
publishThunk({
const classifiedThunk = publishThunk({
relays: [url],
event: makeEvent(CLASSIFIED, {content, tags}),
})
draftKey.clear()
history.back()
const error = await waitForThunkError(classifiedThunk)
if (error) {
return pushToast({theme: "error", message: error})
}
if (shareToChat) {
const quoteThunk = publishRoomQuote({url, h, parent: classifiedThunk.event, protect})
const quoteError = await waitForThunkError(quoteThunk)
if (quoteError) {
pushToast({theme: "error", message: quoteError})
}
}
draftKey.clear()
} finally {
loading = false
}
+5 -5
View File
@@ -22,15 +22,15 @@
const {url, h, onClick}: Props = $props()
const createGoal = () => pushModal(GoalCreate, {url, h})
const createGoal = () => pushModal(GoalCreate, {url, h, shareToChat: true})
const createCalendarEvent = () => pushModal(CalendarEventCreate, {url, h})
const createCalendarEvent = () => pushModal(CalendarEventCreate, {url, h, shareToChat: true})
const createThread = () => pushModal(ThreadCreate, {url, h})
const createThread = () => pushModal(ThreadCreate, {url, h, shareToChat: true})
const createClassified = () => pushModal(ClassifiedCreate, {url, h})
const createClassified = () => pushModal(ClassifiedCreate, {url, h, shareToChat: true})
const createPoll = () => pushModal(PollCreate, {url, h})
const createPoll = () => pushModal(PollCreate, {url, h, shareToChat: true})
let ul: Element
+25 -6
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import {writable} from "svelte/store"
import {makeEvent, ZAP_GOAL} from "@welshman/util"
import {publishThunk} from "@welshman/app"
import {publishThunk, waitForThunkError} from "@welshman/app"
import {isMobile, preventDefault} from "@lib/html"
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
import Bolt from "@assets/icons/bolt.svg?dataurl"
@@ -21,7 +21,7 @@
import {PROTECTED} from "@app/core/state"
import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/util/drafts"
import {canEnforceNip70} from "@app/core/commands"
import {canEnforceNip70, publishRoomQuote} from "@app/core/commands"
type Values = {
title: string
@@ -33,9 +33,10 @@
url: string
h?: string
initialValues?: Values
shareToChat?: boolean
}
let {url, h, initialValues}: Props = $props()
let {url, h, initialValues, shareToChat = false}: Props = $props()
const draftKey = new DraftKey<Values>(`goal:${url}:${h ?? ""}`)
@@ -78,7 +79,9 @@
["relays", url],
]
if (await shouldProtect) {
const protect = await shouldProtect
if (protect) {
tags.push(PROTECTED)
}
@@ -86,13 +89,29 @@
tags.push(["h", h])
}
publishThunk({
const goalThunk = publishThunk({
relays: [url],
event: makeEvent(ZAP_GOAL, {content: title, tags}),
})
draftKey.clear()
history.back()
const error = await waitForThunkError(goalThunk)
if (error) {
return pushToast({theme: "error", message: error})
}
if (shareToChat) {
const quoteThunk = publishRoomQuote({url, h, parent: goalThunk.event, protect})
const quoteError = await waitForThunkError(quoteThunk)
if (quoteError) {
pushToast({theme: "error", message: quoteError})
}
}
draftKey.clear()
}
let title = $state(initialValues?.title ?? "")
+25 -6
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import {insertAt, now, randomId, removeAt, removeUndefined} from "@welshman/lib"
import {makeEvent} from "@welshman/util"
import {publishThunk} from "@welshman/app"
import {publishThunk, waitForThunkError} from "@welshman/app"
import {Poll} from "nostr-tools/kinds"
import {isMobile, preventDefault} from "@lib/html"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
@@ -21,7 +21,7 @@
import ModalBody from "@lib/components/ModalBody.svelte"
import {pushToast} from "@app/util/toast"
import {PROTECTED} from "@app/core/state"
import {canEnforceNip70} from "@app/core/commands"
import {canEnforceNip70, publishRoomQuote} from "@app/core/commands"
import {DraftKey} from "@app/util/drafts"
import type {PollType} from "@app/util/polls"
@@ -40,9 +40,10 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
}
const {url, h}: Props = $props()
const {url, h, shareToChat = false}: Props = $props()
const draftKey = new DraftKey<Values>(`poll:${url}:${h ?? ""}`)
const initialValues = draftKey.get()
@@ -130,17 +131,35 @@
tags.push(["h", h])
}
if (await shouldProtect) {
const protect = await shouldProtect
if (protect) {
tags.push(PROTECTED)
}
publishThunk({
const pollThunk = publishThunk({
relays: [url],
event: makeEvent(Poll, {content: title.trim(), tags}),
})
draftKey.clear()
history.back()
const error = await waitForThunkError(pollThunk)
if (error) {
return pushToast({theme: "error", message: error})
}
if (shareToChat) {
const quoteThunk = publishRoomQuote({url, h, parent: pollThunk.event, protect})
const quoteError = await waitForThunkError(quoteThunk)
if (quoteError) {
pushToast({theme: "error", message: quoteError})
}
}
draftKey.clear()
}
let draggedOptionId = $state<string | undefined>()
+25 -6
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import {writable} from "svelte/store"
import {makeEvent, THREAD} from "@welshman/util"
import {publishThunk} from "@welshman/app"
import {publishThunk, waitForThunkError} from "@welshman/app"
import {isMobile, preventDefault} from "@lib/html"
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
@@ -19,7 +19,7 @@
import {PROTECTED} from "@app/core/state"
import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/util/drafts"
import {canEnforceNip70} from "@app/core/commands"
import {canEnforceNip70, publishRoomQuote} from "@app/core/commands"
type Values = {
content?: string | object
@@ -29,9 +29,10 @@
type Props = {
url: string
h?: string
shareToChat?: boolean
}
const {url, h}: Props = $props()
const {url, h, shareToChat = false}: Props = $props()
const draftKey = new DraftKey<Values>(`thread:${url}:${h ?? ""}`)
const initialValues = draftKey.get()
const shouldProtect = canEnforceNip70(url)
@@ -64,7 +65,9 @@
const tags = [...ed.storage.nostr.getEditorTags(), ["title", title]]
if (await shouldProtect) {
const protect = await shouldProtect
if (protect) {
tags.push(PROTECTED)
}
@@ -72,13 +75,29 @@
tags.push(["h", h])
}
publishThunk({
const threadThunk = publishThunk({
relays: [url],
event: makeEvent(THREAD, {content, tags}),
})
draftKey.clear()
history.back()
const error = await waitForThunkError(threadThunk)
if (error) {
return pushToast({theme: "error", message: error})
}
if (shareToChat) {
const quoteThunk = publishRoomQuote({url, h, parent: threadThunk.event, protect})
const quoteError = await waitForThunkError(quoteThunk)
if (quoteError) {
pushToast({theme: "error", message: quoteError})
}
}
draftKey.clear()
}
let title = $state(initialValues?.title ?? "")
+29
View File
@@ -22,6 +22,7 @@ import {PollResponse} from "nostr-tools/kinds"
import {
DELETE,
REPORT,
MESSAGE,
PROFILE,
MESSAGING_RELAYS,
RELAYS,
@@ -122,6 +123,34 @@ export const prependParent = (
return {content, tags}
}
export const publishRoomQuote = ({
url,
h,
parent,
protect,
delay,
}: {
url: string
h?: string
parent: TrustedEvent
protect: boolean
delay?: number
}) => {
const tags: string[][] = []
if (h) {
tags.push(["h", h])
}
if (protect) {
tags.push(PROTECTED)
}
const event = makeEvent(MESSAGE, prependParent(parent, {content: "", tags}, url))
return publishThunk({relays: [url], event, delay})
}
// Synchronization
export const broadcastUserData = async (relays: string[]) => {
+1 -2
View File
@@ -37,7 +37,6 @@
deriveRoom,
deriveUserRoomMembershipStatus,
getRoomType,
MESSAGE_KINDS,
MembershipStatus,
PROTECTED,
RoomType,
@@ -360,7 +359,7 @@
url,
at: at || now(),
element: element!,
filters: [{kinds: [...MESSAGE_KINDS, ROOM_ADD_MEMBER], "#h": [h]}],
filters: [{kinds: [MESSAGE, ROOM_ADD_MEMBER], "#h": [h]}],
onBackwardExhausted: () => {
loadingBackward = false
},
+2 -2
View File
@@ -25,7 +25,7 @@
import RoomCompose from "@app/components/RoomCompose.svelte"
import RoomComposeEdit from "@src/app/components/RoomComposeEdit.svelte"
import RoomComposeParent from "@app/components/RoomComposeParent.svelte"
import {userSettingsValues, decodeRelay, PROTECTED, MESSAGE_KINDS} from "@app/core/state"
import {userSettingsValues, decodeRelay, PROTECTED} from "@app/core/state"
import {prependParent, canEnforceNip70, publishDelete} from "@app/core/commands"
import {checked} from "@app/util/notifications"
import {pushToast} from "@app/util/toast"
@@ -252,7 +252,7 @@
url,
at: at || now(),
element: element!,
filters: [{kinds: [...MESSAGE_KINDS, RELAY_ADD_MEMBER]}],
filters: [{kinds: [MESSAGE, RELAY_ADD_MEMBER]}],
onBackwardExhausted: () => {
loadingBackward = false
},