+
+
+
+
{@render leading?.()}
- {@render title?.()}
-
-
- {displayRelayUrl(url)}
+
+ {@render title?.()}
+
+ {displayRelayUrl(url)}
+
+
-
diff --git a/src/app/components/ThreadBoard.svelte b/src/app/components/ThreadBoard.svelte
new file mode 100644
index 00000000..9b0f5d8b
--- /dev/null
+++ b/src/app/components/ThreadBoard.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
+ {#if h}
+ #
+ {:else}
+ General
+ {/if}
+
+
+ {threads.length}
+ {threads.length === 1 ? "topic" : "topics"}
+
+
+
+ Topic
+ Author
+ Replies
+ Last post
+
+ {#each threads as event (event.id)}
+
+ {/each}
+
diff --git a/src/app/components/ThreadBoardItem.svelte b/src/app/components/ThreadBoardItem.svelte
new file mode 100644
index 00000000..6f2dc129
--- /dev/null
+++ b/src/app/components/ThreadBoardItem.svelte
@@ -0,0 +1,48 @@
+
+
+
+
+
{title || "Untitled thread"}
+
+ by
+
+
+
+
+ Replies ·
+ {replyCount}
+
+
+ Last ·
+ {formatTimestamp(lastActive)}
+
+
diff --git a/src/app/components/ThreadPagination.svelte b/src/app/components/ThreadPagination.svelte
new file mode 100644
index 00000000..25e07adf
--- /dev/null
+++ b/src/app/components/ThreadPagination.svelte
@@ -0,0 +1,66 @@
+
+
+
+
Page {page} of {pageCount}
+
+
+
+ {#each pages as p, i (p)}
+ {#if i > 0 && p - pages[i - 1] > 1}
+
+ {/if}
+
+ {/each}
+
+
+
+
diff --git a/src/app/components/ThreadPost.svelte b/src/app/components/ThreadPost.svelte
new file mode 100644
index 00000000..46e281e0
--- /dev/null
+++ b/src/app/components/ThreadPost.svelte
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+ {formatTimestamp(event.created_at)}
+
+
+
+ {#if isComment}
+
+ {:else}
+
+ {/if}
+
+
+
+ {#if isComment}
+
+ {:else}
+
+ {/if}
+
+
+
+
diff --git a/src/routes/spaces/[relay]/threads/+page.svelte b/src/routes/spaces/[relay]/threads/+page.svelte
index c36c3542..2750c56c 100644
--- a/src/routes/spaces/[relay]/threads/+page.svelte
+++ b/src/routes/spaces/[relay]/threads/+page.svelte
@@ -6,7 +6,6 @@
import {sortBy, partition, spec, max, pushToMapKey} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {THREAD, getTagValue} from "@welshman/util"
- import {fly} from "@lib/transition"
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
import Add from "@assets/icons/add.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
@@ -14,9 +13,10 @@
import PageContent from "@lib/components/PageContent.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import SpaceBar from "@app/components/SpaceBar.svelte"
- import ThreadItem from "@app/components/ThreadItem.svelte"
+ import ThreadBoard from "@app/components/ThreadBoard.svelte"
import ThreadCreate from "@app/components/ThreadCreate.svelte"
import {decodeRelay} from "@app/relays"
+ import {roomsByUrl} from "@app/groups"
import {makeCommentFilter} from "@app/content"
import {makeFeed} from "@app/feeds"
import {pushModal} from "@app/modal"
@@ -29,9 +29,9 @@
const createThread = () => pushModal(ThreadCreate, {url})
- const items = $derived.by(() => {
+ const threadFeed = $derived.by(() => {
const scores = new Map
()
- const [goals, comments] = partition(spec({kind: THREAD}), $events)
+ const [threads, comments] = partition(spec({kind: THREAD}), $events)
for (const comment of comments) {
const id = getTagValue("E", comment.tags)
@@ -41,7 +41,29 @@
}
}
- return sortBy(e => -max([...(scores.get(e.id) || []), e.created_at]), goals)
+ const items = sortBy(e => -max([...(scores.get(e.id) || []), e.created_at]), threads)
+
+ const byRoom = new Map()
+
+ for (const event of items) {
+ const h = getTagValue("h", event.tags) || ""
+ const roomThreads = byRoom.get(h) || []
+
+ roomThreads.push(event)
+ byRoom.set(h, roomThreads)
+ }
+
+ const roomOrder = new Map(($roomsByUrl.get(url) || []).map((room, index) => [room.h, index]))
+
+ const boards = sortBy(
+ ([h]) => roomOrder.get(h) ?? Number.MAX_SAFE_INTEGER,
+ sortBy(
+ ([h]) => (h ? 0 : 1),
+ sortBy(([h]) => h, Array.from(byRoom.entries())),
+ ),
+ )
+
+ return {items, boards}
})
onMount(() => {
@@ -77,17 +99,15 @@
{/snippet}
-
- {#each items as event (event.id)}
-
-
-
+
+ {#each threadFeed.boards as [h, threads] (h || "general")}
+
{/each}
{#if loading}
Looking for threads...
- {:else if items.length === 0}
+ {:else if threadFeed.items.length === 0}
No threads found.
{:else}
That's all!
diff --git a/src/routes/spaces/[relay]/threads/[id]/+page.svelte b/src/routes/spaces/[relay]/threads/[id]/+page.svelte
index 5196ed9b..39b7d203 100644
--- a/src/routes/spaces/[relay]/threads/[id]/+page.svelte
+++ b/src/routes/spaces/[relay]/threads/[id]/+page.svelte
@@ -1,26 +1,30 @@
-
+
{#snippet title()}
- {getTagValue("title", $event?.tags || []) || ""}
+
+
+ {getTagValue("title", $event?.tags || []) || ""}
+
+
+ {replyCount}
+ {replyCount === 1 ? "reply" : "replies"}
+ {#if h}
+ · #
+ {/if}
+
+
{/snippet}
-
+
{#if $event}
-
-
-
-
-
-
-
- {#if !showAll && $replies.length > 4}
-
-
-
- {/if}
- {#each $replies.slice(0, showAll ? undefined : 4) as reply (reply.id)}
-
-
-
-
-
-
+
+ {#each pagePosts as post, i (post.id)}
+ {@const number = (currentPage - 1) * POSTS_PER_PAGE + i + 1}
+
{/each}
- {#if showReply}
-
+ {#if pageCount > 1}
+
+ {/if}
+ {#if showReply && replyTo}
+
{:else}
-