Route and protect space content through toSpace and publishToSpace instead of repeating the NIP-70 and room checks in every form

This commit is contained in:
Jon Staab 2026-09-25 15:55:39 -07:00
parent d67385632b
commit ba03406795
14 changed files with 109 additions and 193 deletions

View file

@ -2,8 +2,6 @@
import type {Snippet} from "svelte" import type {Snippet} from "svelte"
import {writable} from "svelte/store" import {writable} from "svelte/store"
import {randomId} from "@welshman/lib" import {randomId} from "@welshman/lib"
import {relay} from "@welshman/util"
import {publish} from "@welshman/app"
import {TimeEvent} from "@welshman/domain" import {TimeEvent} from "@welshman/domain"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import GallerySend from "@assets/icons/gallery-send.svg?dataurl" import GallerySend from "@assets/icons/gallery-send.svg?dataurl"
@ -18,11 +16,11 @@
import ModalBody from "@lib/components/ModalBody.svelte" import ModalBody from "@lib/components/ModalBody.svelte"
import DateTimeRangeInput from "@lib/components/DateTimeRangeInput.svelte" import DateTimeRangeInput from "@lib/components/DateTimeRangeInput.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {publishRoomQuote} from "@app/rooms" import {publishToSpace} from "@app/rooms"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
type Values = { type Values = {
d: string d: string
@ -61,8 +59,6 @@
initialValues = draftKey.get() initialValues = draftKey.get()
} }
const shouldProtect = $relays.hasNip(url, 70)
const uploading = writable(false) const uploading = writable(false)
const back = () => history.back() const back = () => history.back()
@ -101,7 +97,6 @@
loading = true loading = true
try { try {
const protect = await shouldProtect
const eventWriter = writer(TimeEvent) const eventWriter = writer(TimeEvent)
.setIdentifier(d) .setIdentifier(d)
.setTitle(title) .setTitle(title)
@ -109,17 +104,9 @@
.setStart(start) .setStart(start)
.setEnd(end) .setEnd(end)
.setContent(content) .setContent(content)
.setProtected(protect)
.addTags(...ed.storage.nostr.getEditorTags()) .addTags(...ed.storage.nostr.getEditorTags())
if (h) { const {error} = await publishToSpace(eventWriter, {url, h, shareToChat})
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
const calendarThunk = await command(eventWriter).then(publish)
const error = await calendarThunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -127,11 +114,6 @@
draftKey?.clear() draftKey?.clear()
history.back() history.back()
if (shareToChat) {
publishRoomQuote({url, h, parent: calendarThunk.options.event, protect})
}
pushToast({message: "Your event has been saved!"}) pushToast({message: "Your event has been saved!"})
} finally { } finally {
loading = false loading = false

View file

@ -2,8 +2,6 @@
import type {Snippet} from "svelte" import type {Snippet} from "svelte"
import cx from "classnames" import cx from "classnames"
import {removeUndefined, randomId, uniq, toggle} from "@welshman/lib" import {removeUndefined, randomId, uniq, toggle} from "@welshman/lib"
import {relay} from "@welshman/util"
import {publish} from "@welshman/app"
import {Classified} from "@welshman/domain" import {Classified} from "@welshman/domain"
import {isMobile, preventDefault} from "@lib/html" import {isMobile, preventDefault} from "@lib/html"
import {normalizeTopic} from "@lib/util" import {normalizeTopic} from "@lib/util"
@ -20,9 +18,9 @@
import CurrencyInput from "@app/components/CurrencyInput.svelte" import CurrencyInput from "@app/components/CurrencyInput.svelte"
import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte" import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {publishRoomQuote} from "@app/rooms" import {publishToSpace} from "@app/rooms"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {compressFileForUpload, uploadFile} from "@app/uploads" import {compressFileForUpload, uploadFile} from "@app/uploads"
@ -100,8 +98,6 @@
} }
} }
const protect = await $relays.hasNip(url, 70)
const eventWriter = writer(Classified) const eventWriter = writer(Classified)
.setIdentifier(d) .setIdentifier(d)
.setContent(content) .setContent(content)
@ -111,17 +107,9 @@
.setStatus(status) .setStatus(status)
.setTopics(topics) .setTopics(topics)
.setImages(imageUrls) .setImages(imageUrls)
.setProtected(protect)
.addTags(...ed.storage.nostr.getEditorTags()) .addTags(...ed.storage.nostr.getEditorTags())
if (h) { const {error} = await publishToSpace(eventWriter, {url, h, shareToChat})
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
const classifiedThunk = await command(eventWriter).then(publish)
const error = await classifiedThunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -129,10 +117,6 @@
draftKey?.clear() draftKey?.clear()
history.back() history.back()
if (shareToChat) {
publishRoomQuote({url, h, parent: classifiedThunk.options.event, protect})
}
} finally { } finally {
loading = false loading = false
} }

View file

@ -9,9 +9,10 @@
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {relays, thunks, writer} from "@app/core" import {thunks, writer} from "@app/core"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {toSpace} from "@app/rooms"
import {loadSendDelay} from "@app/settings" import {loadSendDelay} from "@app/settings"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
@ -41,17 +42,15 @@
// A reply to a room event is a NIP-22 comment, and a reply to a kind 1 note is a NIP-10 note. // A reply to a room event is a NIP-22 comment, and a reply to a kind 1 note is a NIP-10 note.
const renderReply = async (content: string, tags: string[][]) => { const renderReply = async (content: string, tags: string[][]) => {
if (url) { if (url) {
const eventWriter = writer(Comment) const eventWriter = await toSpace(
writer(Comment)
.setContent(content) .setContent(content)
.addTags(...tags) .addTags(...tags)
.setRootFromEvent(event) .setRootFromEvent(event)
.setParentFromEvent(parent ?? event) .setParentFromEvent(parent ?? event),
.setProtected(await $relays.hasNip(url, 70)) url,
h,
// An untagged comment isn't visible to the group, so the relay neither gates nor deletes it with the room. )
if (h) {
eventWriter.setRoom(url, h)
}
return {relays: [url], event: await eventWriter.renderTemplate()} return {relays: [url], event: await eventWriter.renderTemplate()}
} }

View file

@ -12,11 +12,11 @@
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import ComposeParent from "@app/components/ComposeParent.svelte" import ComposeParent from "@app/components/ComposeParent.svelte"
import {prependParent} from "@app/rooms" import {prependParent, toSpace} from "@app/rooms"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {relays, thunks, writer} from "@app/core" import {thunks, writer} from "@app/core"
import {loadSendDelay} from "@app/settings" import {loadSendDelay} from "@app/settings"
type Values = { type Values = {
@ -61,17 +61,15 @@
;({content, tags} = await prependParent(parent, {content, tags}, url)) ;({content, tags} = await prependParent(parent, {content, tags}, url))
} }
const eventWriter = writer(Comment) const eventWriter = await toSpace(
writer(Comment)
.setContent(content) .setContent(content)
.addTags(...tags) .addTags(...tags)
.setRootFromEvent(event) .setRootFromEvent(event)
.setParentFromEvent(event) .setParentFromEvent(event),
.setProtected(await $relays.hasNip(url, 70)) url,
h,
// An untagged comment isn't visible to the group, so the relay neither gates nor deletes it with the room. )
if (h) {
eventWriter.setRoom(url, h)
}
const thunk = $thunks.publish({ const thunk = $thunks.publish({
relays: [url], relays: [url],

View file

@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
import {writable} from "svelte/store" import {writable} from "svelte/store"
import {now} from "@welshman/lib" import {now} from "@welshman/lib"
import {relay, toMsats} from "@welshman/util" import {toMsats} from "@welshman/util"
import {ZapGoal} from "@welshman/domain" import {ZapGoal} from "@welshman/domain"
import {publish} from "@welshman/app"
import {isMobile, preventDefault} from "@lib/html" import {isMobile, preventDefault} from "@lib/html"
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl" import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
import Bolt from "@assets/icons/bolt.svg?dataurl" import Bolt from "@assets/icons/bolt.svg?dataurl"
@ -22,9 +21,9 @@
import Modal from "@lib/components/Modal.svelte" import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte" import ModalBody from "@lib/components/ModalBody.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {publishRoomQuote} from "@app/rooms" import {publishToSpace} from "@app/rooms"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {compressFileForUpload, uploadFile} from "@app/uploads" import {compressFileForUpload, uploadFile} from "@app/uploads"
@ -49,8 +48,6 @@
const initialValues = draftKey.get() const initialValues = draftKey.get()
const shouldProtect = $relays.hasNip(url, 70)
const uploading = writable(false) const uploading = writable(false)
const back = () => history.back() const back = () => history.back()
@ -103,15 +100,12 @@
imageUrl = result?.url imageUrl = result?.url
} }
const protect = await shouldProtect
const eventWriter = writer(ZapGoal) const eventWriter = writer(ZapGoal)
.setTitle(title) .setTitle(title)
.setSummary(content) .setSummary(content)
.setAmount(toMsats(amount)) .setAmount(toMsats(amount))
.setUrls([url]) .setUrls([url])
.setProtected(protect)
.addTags(...ed.storage.nostr.getEditorTags()) .addTags(...ed.storage.nostr.getEditorTags())
.forceRoutes(relay(url))
if (imageUrl) { if (imageUrl) {
eventWriter.setImage(imageUrl) eventWriter.setImage(imageUrl)
@ -121,12 +115,7 @@
eventWriter.setClosedAt(closedAt) eventWriter.setClosedAt(closedAt)
} }
if (h) { const {error} = await publishToSpace(eventWriter, {url, h, shareToChat})
eventWriter.setRoom(url, h)
}
const goalThunk = await command(eventWriter).then(publish)
const error = await goalThunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -134,10 +123,6 @@
draftKey.clear() draftKey.clear()
history.back() history.back()
if (shareToChat) {
publishRoomQuote({url, h, parent: goalThunk.options.event, protect})
}
} finally { } finally {
loading = false loading = false
} }

View file

@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import {relay} from "@welshman/util"
import {Pin} from "@welshman/domain" import {Pin} from "@welshman/domain"
import {publish} from "@welshman/app" import {publish} from "@welshman/app"
import PinForm, {type PinFormValues} from "@app/components/PinForm.svelte" import PinForm, {type PinFormValues} from "@app/components/PinForm.svelte"
import {command, relays, writer} from "@app/core" import {command, writer} from "@app/core"
import {setPinReference} from "@app/pinboards" import {setPinReference} from "@app/pinboards"
import {toSpace} from "@app/rooms"
type Props = { type Props = {
url: string url: string
@ -15,11 +15,7 @@
const {url, address, reference}: Props = $props() const {url, address, reference}: Props = $props()
const submit = async ({title, topics, value, content}: PinFormValues) => { const submit = async ({title, topics, value, content}: PinFormValues) => {
const eventWriter = writer(Pin) const eventWriter = await toSpace(writer(Pin).setIdentifier().addBoard(address), url)
.setIdentifier()
.addBoard(address)
.setProtected(await $relays.hasNip(url, 70))
.forceRoutes(relay(url))
if (!setPinReference(eventWriter, value)) { if (!setPinReference(eventWriter, value)) {
return "Please enter a valid URL or nostr link." return "Please enter a valid URL or nostr link."

View file

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import {relay} from "@welshman/util"
import {Pin} from "@welshman/domain" import {Pin} from "@welshman/domain"
import {publish} from "@welshman/app" import {publish} from "@welshman/app"
import type {PinReader} from "@welshman/domain" import type {PinReader} from "@welshman/domain"
import PinForm, {type PinFormValues} from "@app/components/PinForm.svelte" import PinForm, {type PinFormValues} from "@app/components/PinForm.svelte"
import {command, relays, writer} from "@app/core" import {command, writer} from "@app/core"
import {pinToReference, setPinReference} from "@app/pinboards" import {pinToReference, setPinReference} from "@app/pinboards"
import {toSpace} from "@app/rooms"
type Props = { type Props = {
url: string url: string
@ -15,9 +15,7 @@
const {url, pin}: Props = $props() const {url, pin}: Props = $props()
const submit = async ({title, topics, value, content}: PinFormValues) => { const submit = async ({title, topics, value, content}: PinFormValues) => {
const eventWriter = writer(Pin, pin) const eventWriter = await toSpace(writer(Pin, pin), url)
.setProtected(await $relays.hasNip(url, 70))
.forceRoutes(relay(url))
if (!setPinReference(eventWriter, value)) { if (!setPinReference(eventWriter, value)) {
return "Please enter a valid URL or nostr link." return "Please enter a valid URL or nostr link."

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import {navigate} from "@app/modal" import {navigate} from "@app/modal"
import {getAddress, relay} from "@welshman/util" import {getAddress} from "@welshman/util"
import {publish} from "@welshman/app" import {publish} from "@welshman/app"
import {Pinboard} from "@welshman/domain" import {Pinboard} from "@welshman/domain"
import type {PinboardReader} from "@welshman/domain" import type {PinboardReader} from "@welshman/domain"
@ -16,7 +16,8 @@
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte" import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import RelayName from "@app/components/RelayName.svelte" import RelayName from "@app/components/RelayName.svelte"
import {command, relays, writer} from "@app/core" import {command, writer} from "@app/core"
import {toSpace} from "@app/rooms"
import {makeLibraryPath} from "@app/routes" import {makeLibraryPath} from "@app/routes"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
@ -33,11 +34,10 @@
loading = true loading = true
try { try {
const eventWriter = writer(Pinboard, board) const eventWriter = await toSpace(
.setTitle(title) writer(Pinboard, board).setTitle(title).setDescription(description),
.setDescription(description) url,
.setProtected(await $relays.hasNip(url, 70)) )
.forceRoutes(relay(url))
if (!board) { if (!board) {
eventWriter.setIdentifier().setCollaborative(true) eventWriter.setIdentifier().setCollaborative(true)

View file

@ -1,7 +1,5 @@
<script lang="ts"> <script lang="ts">
import {insertAt, now, randomId, removeAt, removeUndefined, spec} from "@welshman/lib" import {insertAt, now, randomId, removeAt, removeUndefined, spec} from "@welshman/lib"
import {relay} from "@welshman/util"
import {publish} from "@welshman/app"
import {Poll} from "@welshman/domain" import {Poll} from "@welshman/domain"
import type {PollType} from "@welshman/domain" import type {PollType} from "@welshman/domain"
import {isMobile, preventDefault} from "@lib/html" import {isMobile, preventDefault} from "@lib/html"
@ -21,9 +19,9 @@
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import Modal from "@lib/components/Modal.svelte" import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte" import ModalBody from "@lib/components/ModalBody.svelte"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
import {publishRoomQuote} from "@app/rooms" import {publishToSpace} from "@app/rooms"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
type Option = { type Option = {
@ -123,12 +121,7 @@
loading = true loading = true
try { try {
const protect = await $relays.hasNip(url, 70) const eventWriter = writer(Poll).setTitle(title.trim()).setPollType(pollType).setUrls([url])
const eventWriter = writer(Poll)
.setTitle(title.trim())
.setPollType(pollType)
.setUrls([url])
.setProtected(protect)
for (const option of nonEmptyOptions) { for (const option of nonEmptyOptions) {
eventWriter.addOption(option) eventWriter.addOption(option)
@ -138,14 +131,7 @@
eventWriter.setEndsAt(endsAt) eventWriter.setEndsAt(endsAt)
} }
if (h) { const {error} = await publishToSpace(eventWriter, {url, h, shareToChat})
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
const pollThunk = await command(eventWriter).then(publish)
const error = await pollThunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -153,10 +139,6 @@
draftKey.clear() draftKey.clear()
history.back() history.back()
if (shareToChat) {
publishRoomQuote({url, h, parent: pollThunk.options.event, protect})
}
} finally { } finally {
loading = false loading = false
} }

View file

@ -9,7 +9,7 @@
import {now, ifLet, ago, spec, MINUTE} from "@welshman/lib" import {now, ifLet, ago, spec, MINUTE} from "@welshman/lib"
import type {Maybe} from "@welshman/lib" import type {Maybe} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util" import type {TrustedEvent, EventContent} from "@welshman/util"
import {relay, stamp, MESSAGE, RELAY_ADD_MEMBER, ROOM_ADD_MEMBER} from "@welshman/util" import {stamp, MESSAGE, RELAY_ADD_MEMBER, ROOM_ADD_MEMBER} from "@welshman/util"
import {Message} from "@welshman/domain" import {Message} from "@welshman/domain"
import {MembershipStatus} from "@welshman/app" import {MembershipStatus} from "@welshman/app"
import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl" import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl"
@ -42,6 +42,7 @@
getRoomType, getRoomType,
groupRoomMessages, groupRoomMessages,
prependParent, prependParent,
toSpace,
} from "@app/rooms" } from "@app/rooms"
import {loadSendDelay} from "@app/settings" import {loadSendDelay} from "@app/settings"
import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds" import {isFeedLoading, makeFeed, makeFeedContext, makeScrollLoader} from "@app/feeds"
@ -174,14 +175,14 @@
return return
} }
const protect = await shouldProtect
if (eventToEdit) { if (eventToEdit) {
// Don't do anything if message hasn't changed // Don't do anything if message hasn't changed
if (eventToEdit.content === content) { if (eventToEdit.content === content) {
return clearEventToEdit() return clearEventToEdit()
} }
const protect = await shouldProtect
// Delete the previous message, to be republished below with the same timestamp // Delete the previous message, to be republished below with the same timestamp
const command = await $deletes.deleteEvent($state.snapshot(eventToEdit), w => const command = await $deletes.deleteEvent($state.snapshot(eventToEdit), w =>
w.setProtected(protect), w.setProtected(protect),
@ -195,16 +196,13 @@
;({content, tags} = await prependParent(sharedEvent, {content, tags}, url)) ;({content, tags} = await prependParent(sharedEvent, {content, tags}, url))
} }
const eventWriter = writer(Message) const eventWriter = await toSpace(
writer(Message)
.setContent(content) .setContent(content)
.addTags(...tags) .addTags(...tags),
.setProtected(protect) url,
h,
if (h) { )
eventWriter.setRoom(url, h)
} else {
eventWriter.forceRoutes(relay(url))
}
if (parent) { if (parent) {
eventWriter.setParent(parent) eventWriter.setParent(parent)

View file

@ -1,8 +1,6 @@
<script lang="ts"> <script lang="ts">
import {writable} from "svelte/store" import {writable} from "svelte/store"
import {relay} from "@welshman/util"
import {Thread} from "@welshman/domain" import {Thread} from "@welshman/domain"
import {publish} from "@welshman/app"
import {isMobile, preventDefault} from "@lib/html" import {isMobile, preventDefault} from "@lib/html"
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl" import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl" import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
@ -17,10 +15,10 @@
import Modal from "@lib/components/Modal.svelte" import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte" import ModalBody from "@lib/components/ModalBody.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {displayRoom, publishRoomQuote} from "@app/rooms" import {displayRoom, publishToSpace} from "@app/rooms"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
type Values = { type Values = {
@ -39,7 +37,6 @@
const {url, h, shareToChat = false, initialValues}: Props = $props() const {url, h, shareToChat = false, initialValues}: Props = $props()
const draftKey = new DraftKey<Values>(`thread:${url}:${h ?? ""}`) const draftKey = new DraftKey<Values>(`thread:${url}:${h ?? ""}`)
const draft = draftKey.get() const draft = draftKey.get()
const shouldProtect = $relays.hasNip(url, 70)
const room = h ?? initialValues?.h ?? draft?.h ?? "" const room = h ?? initialValues?.h ?? draft?.h ?? ""
const uploading = writable(false) const uploading = writable(false)
@ -73,20 +70,12 @@
loading = true loading = true
try { try {
const protect = await shouldProtect
const eventWriter = writer(Thread) const eventWriter = writer(Thread)
.setContent(content) .setContent(content)
.setTitle(title) .setTitle(title)
.setProtected(protect)
.addTags(...ed.storage.nostr.getEditorTags()) .addTags(...ed.storage.nostr.getEditorTags())
.forceRoutes(relay(url))
if (room) { const {error} = await publishToSpace(eventWriter, {url, h: room, shareToChat})
eventWriter.setRoom(url, room)
}
const thunk = await command(eventWriter).then(publish)
const error = await thunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -94,10 +83,6 @@
draftKey.clear() draftKey.clear()
history.back() history.back()
if (shareToChat) {
publishRoomQuote({url, h: room, parent: thunk.options.event, protect})
}
} finally { } finally {
loading = false loading = false
} }

View file

@ -1,9 +1,10 @@
import {derived} from "svelte/store" import {derived} from "svelte/store"
import {APP_DATA, relay, sortEventsDesc, tagSpec, tagValues} from "@welshman/util" import {APP_DATA, sortEventsDesc, tagSpec, tagValues} from "@welshman/util"
import {AppData} from "@welshman/domain" import {AppData} from "@welshman/domain"
import {Relays, publish} from "@welshman/app" import {Relays, publish} from "@welshman/app"
import {command, fromApp, relays, writer} from "@app/core" import {command, fromApp, writer} from "@app/core"
import {deriveEventsForUrl} from "@app/repository" import {deriveEventsForUrl} from "@app/repository"
import {toSpace} from "@app/rooms"
// NIP-78 app data from the pubkey the space's NIP-11 names, one ["content", <value>] tag per entry. // NIP-78 app data from the pubkey the space's NIP-11 names, one ["content", <value>] tag per entry.
export const FEATURED_CONTENT_D = "flotilla/featured-content" export const FEATURED_CONTENT_D = "flotilla/featured-content"
@ -29,9 +30,7 @@ export const setFeaturedContent = async (url: string, content: string[]) => {
const eventWriter = writer(AppData) const eventWriter = writer(AppData)
.setIdentifier(FEATURED_CONTENT_D) .setIdentifier(FEATURED_CONTENT_D)
.setProtected(await relays.get().hasNip(url, 70))
.forceRoutes(relay(url))
.addTags(...tags) .addTags(...tags)
return command(eventWriter).then(publish) return command(await toSpace(eventWriter, url)).then(publish)
} }

View file

@ -15,10 +15,12 @@ import {
} from "@welshman/util" } from "@welshman/util"
import type {EventContent, TrustedEvent} from "@welshman/util" import type {EventContent, TrustedEvent} from "@welshman/util"
import {Message} from "@welshman/domain" import {Message} from "@welshman/domain"
import type {BaseEventReader, EventWriter} from "@welshman/domain"
import {MembershipStatus, RoomLists, makeRoomKey, createSearch, publish} from "@welshman/app" import {MembershipStatus, RoomLists, makeRoomKey, createSearch, publish} from "@welshman/app"
import type {Room, RoomMeta} from "@welshman/app" import type {Room, RoomMeta} from "@welshman/app"
import { import {
deriveUserItem, deriveUserItem,
command,
relayManagement, relayManagement,
relayMemberLists, relayMemberLists,
roomLists, roomLists,
@ -101,20 +103,12 @@ export const prependParent = async (
return {content, tags} return {content, tags}
} }
export const publishRoomQuote = async ({ export const toSpace = async <W extends EventWriter<BaseEventReader>>(
url, eventWriter: W,
h, url: string,
parent, h?: string,
protect, ) => {
delay, eventWriter.setProtected(await relays.get().hasNip(url, 70))
}: {
url: string
h?: string
parent: TrustedEvent
protect: boolean
delay?: number
}) => {
const eventWriter = writer(Message).setParent(parent).setProtected(protect)
if (h) { if (h) {
eventWriter.setRoom(url, h) eventWriter.setRoom(url, h)
@ -122,6 +116,22 @@ export const publishRoomQuote = async ({
eventWriter.forceRoutes(relay(url)) eventWriter.forceRoutes(relay(url))
} }
return eventWriter
}
export const publishRoomQuote = async ({
url,
h,
parent,
delay,
}: {
url: string
h?: string
parent: TrustedEvent
delay?: number
}) => {
const eventWriter = await toSpace(writer(Message).setParent(parent), url, h)
return thunks.get().publish({ return thunks.get().publish({
relays: [url], relays: [url],
event: await eventWriter.renderTemplate(), event: await eventWriter.renderTemplate(),
@ -129,6 +139,20 @@ export const publishRoomQuote = async ({
}) })
} }
export const publishToSpace = async (
eventWriter: EventWriter<BaseEventReader>,
{url, h, shareToChat}: {url: string; h?: string; shareToChat?: boolean},
) => {
const thunk = await command(await toSpace(eventWriter, url, h)).then(publish)
const error = await thunk.waitForError()
if (shareToChat && !error) {
publishRoomQuote({url, h, parent: thunk.options.event})
}
return {thunk, error}
}
// User // User
export const userRoomList = deriveUserItem(RoomLists) export const userRoomList = deriveUserItem(RoomLists)

View file

@ -4,9 +4,8 @@
import {page} from "$app/stores" import {page} from "$app/stores"
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {now, randomId} from "@welshman/lib" import {now, randomId} from "@welshman/lib"
import {getAddress, relay} from "@welshman/util" import {getAddress} from "@welshman/util"
import {Article} from "@welshman/domain" import {Article} from "@welshman/domain"
import {publish} from "@welshman/app"
import {isMobile} from "@lib/html" import {isMobile} from "@lib/html"
import Paperclip from "@assets/icons/paperclip-2.svg?dataurl" import Paperclip from "@assets/icons/paperclip-2.svg?dataurl"
import DocumentText from "@assets/icons/document-text.svg?dataurl" import DocumentText from "@assets/icons/document-text.svg?dataurl"
@ -20,11 +19,11 @@
import SpaceBar from "@app/components/SpaceBar.svelte" import SpaceBar from "@app/components/SpaceBar.svelte"
import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte" import TopicMultiSelect from "@app/components/TopicMultiSelect.svelte"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {command, relays, writer} from "@app/core" import {writer} from "@app/core"
import {DraftKey} from "@app/drafts" import {DraftKey} from "@app/drafts"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
import {decodeRelay} from "@app/relays" import {decodeRelay} from "@app/relays"
import {publishRoomQuote} from "@app/rooms" import {publishToSpace} from "@app/rooms"
import {makeArticlePath} from "@app/routes" import {makeArticlePath} from "@app/routes"
import {compressFileForUpload, uploadFile} from "@app/uploads" import {compressFileForUpload, uploadFile} from "@app/uploads"
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
@ -48,7 +47,6 @@
const draftKey = new DraftKey<Values>(`article:${url}:${h ?? ""}`) const draftKey = new DraftKey<Values>(`article:${url}:${h ?? ""}`)
const initialValues = draftKey.get() const initialValues = draftKey.get()
const shouldProtect = $relays.hasNip(url, 70)
const coverInputId = randomId() const coverInputId = randomId()
const uploading = writable(false) const uploading = writable(false)
@ -114,16 +112,13 @@
loading = true loading = true
try { try {
const protect = await shouldProtect
const eventWriter = writer(Article) const eventWriter = writer(Article)
.setIdentifier(d) .setIdentifier(d)
.setContent(content) .setContent(content)
.setTitle(title) .setTitle(title)
.setTopics(topics) .setTopics(topics)
.setPublishedAt(now()) .setPublishedAt(now())
.setProtected(protect)
.addTags(...ed.storage.nostr.getEditorTags()) .addTags(...ed.storage.nostr.getEditorTags())
.forceRoutes(relay(url))
if (summary) { if (summary) {
eventWriter.setSummary(summary) eventWriter.setSummary(summary)
@ -133,12 +128,7 @@
eventWriter.setImage(image) eventWriter.setImage(image)
} }
if (h) { const {thunk, error} = await publishToSpace(eventWriter, {url, h, shareToChat})
eventWriter.setRoom(url, h)
}
const thunk = await command(eventWriter).then(publish)
const error = await thunk.waitForError()
if (error) { if (error) {
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
@ -146,10 +136,6 @@
draftKey.clear() draftKey.clear()
goto(makeArticlePath(url, getAddress(thunk.options.event))) goto(makeArticlePath(url, getAddress(thunk.options.event)))
if (shareToChat) {
publishRoomQuote({url, h, parent: thunk.options.event, protect})
}
} finally { } finally {
loading = false loading = false
} }