Show the thread created from a message on the message
This commit is contained in:
parent
caafda9553
commit
5cf598d2ac
6 changed files with 31 additions and 3 deletions
|
|
@ -709,6 +709,7 @@ Acceptance:
|
||||||
- "Create a Thread" from a room message's menu opens the thread composer
|
- "Create a Thread" from a room message's menu opens the thread composer
|
||||||
pre-filled with a quote of that message.
|
pre-filled with a quote of that message.
|
||||||
- Publishing files the thread under that room's board.
|
- Publishing files the thread under that room's board.
|
||||||
|
- The message it was created from carries a link to the thread, which opens it.
|
||||||
- Opening the thread shows the quoted original message as part of the opening
|
- Opening the thread shows the quoted original message as part of the opening
|
||||||
post.
|
post.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -948,6 +948,7 @@ test("US-045 turn a chat message into a thread", async ({seed, as}) => {
|
||||||
await composer.getByRole("button", {name: "Create Thread"}).click()
|
await composer.getByRole("button", {name: "Create Thread"}).click()
|
||||||
|
|
||||||
await expect(page.getByRole("heading", {name: "Create a Thread"})).toHaveCount(0)
|
await expect(page.getByRole("heading", {name: "Create a Thread"})).toHaveCount(0)
|
||||||
|
await expect(message.getByRole("link", {name: "Deploy failures"})).toBeVisible()
|
||||||
|
|
||||||
await page.goto(`${spacePath(url)}/threads`)
|
await page.goto(`${spacePath(url)}/threads`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {
|
import {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
|
THREAD,
|
||||||
getCommentFiltersForParent,
|
getCommentFiltersForParent,
|
||||||
getIdOrAddress,
|
getIdOrAddress,
|
||||||
matchTag,
|
matchTag,
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
tagValue,
|
tagValue,
|
||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import {isMobile} from "@lib/html"
|
import {isMobile} from "@lib/html"
|
||||||
|
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||||
import Pen from "@assets/icons/pen.svg?dataurl"
|
import Pen from "@assets/icons/pen.svg?dataurl"
|
||||||
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
import Reply from "@assets/icons/reply-2.svg?dataurl"
|
||||||
import ReplyAlt from "@assets/icons/reply.svg?dataurl"
|
import ReplyAlt from "@assets/icons/reply.svg?dataurl"
|
||||||
|
|
@ -43,7 +45,7 @@
|
||||||
import {ENABLE_ZAPS} from "@app/env"
|
import {ENABLE_ZAPS} from "@app/env"
|
||||||
import type {FeedContext} from "@app/feeds"
|
import type {FeedContext} from "@app/feeds"
|
||||||
import {deriveEvent, deriveEventsForUrl} from "@app/repository"
|
import {deriveEvent, deriveEventsForUrl} from "@app/repository"
|
||||||
import {makeContentPath} from "@app/routes"
|
import {makeContentPath, makeThreadPath} from "@app/routes"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -85,6 +87,8 @@
|
||||||
$innerEvent && makeContentPath(url, $innerEvent.kind, getIdOrAddress($innerEvent)),
|
$innerEvent && makeContentPath(url, $innerEvent.kind, getIdOrAddress($innerEvent)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const threads = deriveEventsForUrl(url, [{kinds: [THREAD], "#q": [event.id]}])
|
||||||
|
|
||||||
const commenterDisplays = $derived(
|
const commenterDisplays = $derived(
|
||||||
deriveDisplaysByPubkey(uniq($innerComments.map(e => e.pubkey)), url),
|
deriveDisplaysByPubkey(uniq($innerComments.map(e => e.pubkey)), url),
|
||||||
)
|
)
|
||||||
|
|
@ -154,6 +158,16 @@
|
||||||
{createReaction}
|
{createReaction}
|
||||||
reactionClass="tip-right"
|
reactionClass="tip-right"
|
||||||
innerEvent={$innerEvent} />
|
innerEvent={$innerEvent} />
|
||||||
|
{#each $threads as thread (thread.id)}
|
||||||
|
<Link
|
||||||
|
href={makeThreadPath(url, thread.id)}
|
||||||
|
class="button button-xs button-neutral gap-1 rounded-full">
|
||||||
|
<Icon icon={NotesMinimalistic} />
|
||||||
|
<span class="max-w-48 truncate">
|
||||||
|
{tagValue(tagSpec("title"), thread.tags) || "Thread"}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
{/each}
|
||||||
{#if path && $innerComments.length > 0}
|
{#if path && $innerComments.length > 0}
|
||||||
{@const pubkeys = $innerComments.map(e => e.pubkey)}
|
{@const pubkeys = $innerComments.map(e => e.pubkey)}
|
||||||
{@const isOwn = pubkeys.includes($user.pubkey)}
|
{@const isOwn = pubkeys.includes($user.pubkey)}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
pushModal(ThreadCreate, {
|
pushModal(ThreadCreate, {
|
||||||
url,
|
url,
|
||||||
h,
|
h,
|
||||||
|
quote: event,
|
||||||
initialValues: {content: toNostrURI(nip19.neventEncode({...event, relays: [url]}))},
|
initialValues: {content: toNostrURI(nip19.neventEncode({...event, relays: [url]}))},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,12 @@
|
||||||
const createThread = () =>
|
const createThread = () =>
|
||||||
pushModal(
|
pushModal(
|
||||||
ThreadCreate,
|
ThreadCreate,
|
||||||
{url, h, initialValues: {content: toNostrURI(nip19.neventEncode({...event, relays: [url]}))}},
|
{
|
||||||
|
url,
|
||||||
|
h,
|
||||||
|
quote: event,
|
||||||
|
initialValues: {content: toNostrURI(nip19.neventEncode({...event, relays: [url]}))},
|
||||||
|
},
|
||||||
{replaceState: true},
|
{replaceState: true},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
|
import type {TrustedEvent} from "@welshman/util"
|
||||||
import {relay} from "@welshman/util"
|
import {relay} from "@welshman/util"
|
||||||
import {Thread} from "@welshman/domain"
|
import {Thread} from "@welshman/domain"
|
||||||
import {publish} from "@welshman/app"
|
import {publish} from "@welshman/app"
|
||||||
|
|
@ -32,10 +33,11 @@
|
||||||
url: string
|
url: string
|
||||||
h?: string
|
h?: string
|
||||||
shareToChat?: boolean
|
shareToChat?: boolean
|
||||||
|
quote?: TrustedEvent
|
||||||
initialValues?: Values
|
initialValues?: Values
|
||||||
}
|
}
|
||||||
|
|
||||||
const {url, h, shareToChat = false, initialValues}: Props = $props()
|
const {url, h, shareToChat = false, quote, 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 shouldProtect = $relays.hasNip(url, 70)
|
||||||
|
|
@ -81,6 +83,10 @@
|
||||||
eventWriter.setRoom(url, h)
|
eventWriter.setRoom(url, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quote) {
|
||||||
|
eventWriter.addQuote(quote)
|
||||||
|
}
|
||||||
|
|
||||||
const thunk = await command(eventWriter).then(publish)
|
const thunk = await command(eventWriter).then(publish)
|
||||||
const error = await thunk.waitForError()
|
const error = await thunk.waitForError()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue