Let the threads page choose which board a new thread goes to
This commit is contained in:
parent
5cf598d2ac
commit
7e82429a1a
4 changed files with 50 additions and 8 deletions
|
|
@ -669,7 +669,7 @@ Acceptance:
|
||||||
- Creating a thread from a room's compose menu files it under that room's board
|
- Creating a thread from a room's compose menu files it under that room's board
|
||||||
and posts a quote of it into the room's chat.
|
and posts a quote of it into the room's chat.
|
||||||
- Creating a thread from the top-level Threads page files it under a "General"
|
- Creating a thread from the top-level Threads page files it under a "General"
|
||||||
board.
|
board, or under whichever board is picked from its board selector.
|
||||||
- Each board row shows the topic title, author, reply count, and last-post time.
|
- Each board row shows the topic title, author, reply count, and last-post time.
|
||||||
|
|
||||||
### US-043 — Reply to a thread and to a specific post
|
### US-043 — Reply to a thread and to a specific post
|
||||||
|
|
|
||||||
|
|
@ -691,6 +691,20 @@ test("US-042 start a thread and see it filed under its room", async ({seed, as})
|
||||||
.filter({has: page.getByRole("heading", {name: "General", exact: true})})
|
.filter({has: page.getByRole("heading", {name: "General", exact: true})})
|
||||||
|
|
||||||
await expect(general.getByRole("row").filter({hasText: "Open floor"})).toBeVisible()
|
await expect(general.getByRole("row").filter({hasText: "Open floor"})).toBeVisible()
|
||||||
|
|
||||||
|
// The threads page has no room of its own, so the form offers one.
|
||||||
|
await pageBar(page).getByRole("button", {name: "Create", exact: true}).click()
|
||||||
|
|
||||||
|
const toLounge = modal(page, "Create a Thread")
|
||||||
|
|
||||||
|
await toLounge.getByRole("combobox").selectOption({label: "Lounge"})
|
||||||
|
await toLounge.getByPlaceholder("What is this thread about?").fill("Carpet swatches")
|
||||||
|
await editorOf(toLounge).pressSequentially("Beige is a choice.")
|
||||||
|
await toLounge.getByRole("button", {name: "Create Thread"}).click()
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", {name: "Create a Thread"})).toHaveCount(0)
|
||||||
|
|
||||||
|
await expect(lounge.getByRole("row").filter({hasText: "Carpet swatches"})).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("US-043 reply to a thread and to a specific post", async ({seed, as}) => {
|
test("US-043 reply to a thread and to a specific post", async ({seed, as}) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
|
import {sortBy} from "@welshman/lib"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
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"
|
||||||
|
|
@ -21,26 +22,37 @@
|
||||||
import {command, relays, writer} from "@app/core"
|
import {command, relays, 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 {publishRoomQuote} from "@app/rooms"
|
import {
|
||||||
|
deriveOtherRooms,
|
||||||
|
deriveUserRooms,
|
||||||
|
displayRoom,
|
||||||
|
publishRoomQuote,
|
||||||
|
roomComparator,
|
||||||
|
} from "@app/rooms"
|
||||||
import {pushToast} from "@app/toast"
|
import {pushToast} from "@app/toast"
|
||||||
|
|
||||||
type Values = {
|
type Values = {
|
||||||
content?: string | object
|
content?: string | object
|
||||||
title?: string
|
title?: string
|
||||||
|
h?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
url: string
|
url: string
|
||||||
h?: string
|
h?: string
|
||||||
|
selectRoom?: boolean
|
||||||
shareToChat?: boolean
|
shareToChat?: boolean
|
||||||
quote?: TrustedEvent
|
quote?: TrustedEvent
|
||||||
initialValues?: Values
|
initialValues?: Values
|
||||||
}
|
}
|
||||||
|
|
||||||
const {url, h, shareToChat = false, quote, initialValues}: Props = $props()
|
const {url, h, selectRoom = false, 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)
|
||||||
|
const userRooms = deriveUserRooms(url)
|
||||||
|
const otherRooms = deriveOtherRooms(url)
|
||||||
|
const roomOptions = $derived(sortBy(roomComparator(url), [...$userRooms, ...$otherRooms]))
|
||||||
|
|
||||||
const uploading = writable(false)
|
const uploading = writable(false)
|
||||||
|
|
||||||
|
|
@ -79,8 +91,8 @@
|
||||||
.addTags(...ed.storage.nostr.getEditorTags())
|
.addTags(...ed.storage.nostr.getEditorTags())
|
||||||
.forceRoutes(relay(url))
|
.forceRoutes(relay(url))
|
||||||
|
|
||||||
if (h) {
|
if (room) {
|
||||||
eventWriter.setRoom(url, h)
|
eventWriter.setRoom(url, room)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quote) {
|
if (quote) {
|
||||||
|
|
@ -98,7 +110,7 @@
|
||||||
history.back()
|
history.back()
|
||||||
|
|
||||||
if (shareToChat) {
|
if (shareToChat) {
|
||||||
publishRoomQuote({url, h, parent: thunk.event, protect})
|
publishRoomQuote({url, h: room, parent: thunk.event, protect})
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
|
|
@ -109,6 +121,7 @@
|
||||||
|
|
||||||
let title = $state(initialValues?.title ?? draft?.title ?? "")
|
let title = $state(initialValues?.title ?? draft?.title ?? "")
|
||||||
let content = $state(initialValues?.content ?? draft?.content ?? "")
|
let content = $state(initialValues?.content ?? draft?.content ?? "")
|
||||||
|
let room = $state(h ?? initialValues?.h ?? draft?.h ?? "")
|
||||||
|
|
||||||
const onChange = (json: object) => {
|
const onChange = (json: object) => {
|
||||||
content = json
|
content = json
|
||||||
|
|
@ -124,7 +137,7 @@
|
||||||
})
|
})
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
draftKey.update({title, content})
|
draftKey.update({title, content, h: room})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -151,6 +164,21 @@
|
||||||
</label>
|
</label>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Field>
|
</Field>
|
||||||
|
{#if selectRoom && roomOptions.length > 0}
|
||||||
|
<Field>
|
||||||
|
{#snippet label()}
|
||||||
|
<p>Board</p>
|
||||||
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<select class="select input w-full" bind:value={room}>
|
||||||
|
<option value="">General</option>
|
||||||
|
{#each roomOptions as option (option)}
|
||||||
|
<option value={option}>{displayRoom(url, option)}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{/snippet}
|
||||||
|
</Field>
|
||||||
|
{/if}
|
||||||
<Field>
|
<Field>
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
<p>Message*</p>
|
<p>Message*</p>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
let element: HTMLElement | undefined = $state()
|
let element: HTMLElement | undefined = $state()
|
||||||
let events: Readable<TrustedEvent[]> = $state(readable([]))
|
let events: Readable<TrustedEvent[]> = $state(readable([]))
|
||||||
|
|
||||||
const createThread = () => pushModal(ThreadCreate, {url})
|
const createThread = () => pushModal(ThreadCreate, {url, selectRoom: true})
|
||||||
|
|
||||||
const threadFeed = $derived.by(() => {
|
const threadFeed = $derived.by(() => {
|
||||||
const scores = new Map<string, number[]>()
|
const scores = new Map<string, number[]>()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue