Allow editing previous messages in channel chat

This commit is contained in:
Matthew Remmel
2025-10-15 10:00:23 -04:00
committed by hodlbod
parent 7334cd26f8
commit 5b22d6ac01
5 changed files with 159 additions and 14 deletions
+47 -4
View File
@@ -4,8 +4,8 @@
import {onMount, onDestroy} from "svelte"
import {page} from "$app/stores"
import type {Readable} from "svelte/store"
import {now, formatTimestampAsDate} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
import {now, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
import {request} from "@welshman/net"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {
@@ -52,11 +52,13 @@
canEnforceNip70,
removeRoomMembership,
prependParent,
publishDelete,
} from "@app/core/commands"
import {PROTECTED} from "@app/core/state"
import {makeFeed} from "@app/core/requests"
import {popKey} from "@lib/implicit"
import {pushToast} from "@app/util/toast"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
const {room, relay} = $page.params as MakeNonOptional<typeof $page.params>
const mounted = now()
@@ -115,6 +117,10 @@
share = undefined
}
const clearEventToEdit = () => {
eventToEdit = undefined
}
const onSubmit = async ({content, tags}: EventContent) => {
tags.push(["h", room])
@@ -122,7 +128,13 @@
tags.push(PROTECTED)
}
let template = {content, tags}
let template: EventContent & {created_at?: number} = {content, tags}
if (eventToEdit) {
// Delete previous message, to be republished with same timestamp
template.created_at = eventToEdit.created_at
publishDelete({relays: [url], event: eventToEdit, protect: await shouldProtect})
}
if (share) {
template = prependParent(share, template)
@@ -150,6 +162,7 @@
clearParent()
clearShare()
clearEventToEdit()
}
const onScroll = () => {
@@ -188,6 +201,7 @@
let cleanup: () => void
let events: Readable<TrustedEvent[]> = $state(readable([]))
let compose: ChannelCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state()
const elements = $derived.by(() => {
const elements = []
@@ -266,6 +280,23 @@
cleanup = feed.cleanup
}
const canEditEvent = (event: TrustedEvent) =>
event.pubkey === $pubkey && event.created_at >= ago(5, MINUTE)
const onEditEvent = (event: TrustedEvent) => {
clearParent()
clearShare()
eventToEdit = event
}
const onEditPrevious = () => {
const prev = $events.find(e => e.pubkey === $pubkey)
if (prev && canEditEvent(prev)) {
onEditEvent(prev)
}
}
onMount(() => {
const controller = new AbortController()
@@ -402,7 +433,9 @@
{url}
{replyTo}
event={$state.snapshot(value as TrustedEvent)}
{showPubkey} />
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{/if}
{/each}
@@ -446,8 +479,18 @@
{#if share}
<ChannelComposeParent event={share} clear={clearShare} verb="Sharing" />
{/if}
{#if eventToEdit}
<ChannelComposeEdit clear={clearEventToEdit} />
{/if}
</div>
<ChannelCompose bind:this={compose} {onSubmit} {url} />
{#key eventToEdit}
<ChannelCompose
bind:this={compose}
content={eventToEdit?.content}
{onSubmit}
{url}
{onEditPrevious} />
{/key}
{/if}
</div>
+48 -6
View File
@@ -3,7 +3,7 @@
import {page} from "$app/stores"
import type {Readable} from "svelte/store"
import {readable} from "svelte/store"
import {now, formatTimestampAsDate} from "@welshman/lib"
import {now, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {makeEvent, getTag, MESSAGE, DELETE} from "@welshman/util"
import {pubkey, publishThunk} from "@welshman/app"
@@ -28,11 +28,12 @@
PROTECTED,
REACTION_KINDS,
} from "@app/core/state"
import {prependParent, canEnforceNip70} from "@app/core/commands"
import {prependParent, canEnforceNip70, publishDelete} from "@app/core/commands"
import {setChecked, checked} from "@app/util/notifications"
import {pushToast} from "@app/util/toast"
import {makeFeed} from "@app/core/requests"
import {popKey} from "@lib/implicit"
import ChannelComposeEdit from "@src/app/components/ChannelComposeEdit.svelte"
const mounted = now()
const lastChecked = $checked[$page.url.pathname]
@@ -49,17 +50,27 @@
parent = undefined
}
const clearEventToEdit = () => {
eventToEdit = undefined
}
const clearShare = () => {
share = undefined
}
const onSubmit = async ({content, tags}: EventContent) => {
let template: EventContent & {created_at?: number} = {content, tags}
if (eventToEdit) {
// Delete previous message, to be republished with same timestamp
template.created_at = eventToEdit.created_at
publishDelete({relays: [url], event: eventToEdit, protect: await shouldProtect})
}
if (await shouldProtect) {
tags.push(PROTECTED)
}
let template = {content, tags}
if (share) {
template = prependParent(share, template)
}
@@ -86,6 +97,7 @@
clearParent()
clearShare()
clearEventToEdit()
}
const onScroll = () => {
@@ -122,6 +134,7 @@
let cleanup: () => void
let events: Readable<TrustedEvent[]> = $state(readable([]))
let compose: ChannelCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state()
const elements = $derived.by(() => {
const elements = []
@@ -184,6 +197,23 @@
return elements
})
const canEditEvent = (event: TrustedEvent) =>
event.pubkey === $pubkey && event.created_at >= ago(5, MINUTE)
const onEditEvent = (event: TrustedEvent) => {
clearParent()
clearShare()
eventToEdit = event
}
const onEditPrevious = () => {
const prev = $events.find(e => e.pubkey === $pubkey)
if (prev && canEditEvent(prev)) {
onEditEvent(prev)
}
}
onMount(() => {
const controller = new AbortController()
@@ -261,7 +291,9 @@
{url}
{replyTo}
event={$state.snapshot(value as TrustedEvent)}
{showPubkey} />
{showPubkey}
canEdit={canEditEvent}
onEdit={onEditEvent} />
</div>
{/if}
{/each}
@@ -282,8 +314,18 @@
{#if share}
<ChannelComposeParent event={share} clear={clearShare} verb="Sharing" />
{/if}
{#if eventToEdit}
<ChannelComposeEdit clear={clearEventToEdit} />
{/if}
</div>
<ChannelCompose bind:this={compose} {onSubmit} {url} />
{#key eventToEdit}
<ChannelCompose
bind:this={compose}
content={eventToEdit?.content}
{onSubmit}
{url}
{onEditPrevious} />
{/key}
</div>
{#if showScrollButton}