chore: redesign threads as a linear phpBB-style forum view #300
Reference in New Issue
Block a user
Delete Branch "userAdityaa/flotilla:243-redesign-threads"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements the approved thread redesign from #243. The thread detail view replaces stacked cards with a flat, phpBB-style layout: each post shows profile info, OP badges, per-post replies, reactions/actions, and #post-N permalinks, with pagination for longer threads. The header keeps the title, reply count, and room visible while scrolling. The thread list groups topics by room as boards, with uncategorized threads under General.
Screenshots of the changes
d74d07ab39tofe9fef60b5@hodlbod, could you please take a look at the design for now? I'll clean up and refactor the code afterward 🙂
I think the mockups look great, let me know when the code is ready to review.
fe9fef60b5to71071b54c6@hodlbod, this should be ready for an initial review now.
@@ -23,0 +25,4 @@leading,title,action,hideRelay = false,What's the reasoning behind this? I think having the relay visible is probably still important context
hideRelaywas only to avoid a cramped/tall mobile header after adding the two-line thread title (title + reply count/room).but yes, we have to keep the relay context, I shall revert it back.
Looks like this:

Yeah, that is kind of cramped. What about
meta.spaces.coracle.social * #Development * 4 replies? But that wouldn't work well on mobile. Hmmm. We can punt it for later, but let's align the space icon to the top.sure, here it is:

@@ -0,0 +16,4 @@lastActive: Map<string, number>}const {url, h, threads, replyCounts, lastActive}: Props = $props()Instead of threading replyCounts and lastActive through, create a new ThreadBoardItem which calculates them locally. You can derive stuff from the repository using one of the
deriveEventsutilities with anEtag. This shouldn't cause performance problems compared to iterating over all comments.@@ -0,0 +28,4 @@const {url, threadId, event, number, threadPubkey, onReply}: Props = $props()const relays = removeUndefined([url])url should never be undefined right?
@@ -0,0 +38,4 @@const copyPermalink = () => {const path = makeSpacePath(url, "threads", threadId)const link = `${window.location.origin}${path}#post-${number}`origin=localhost on android, use PLATFORM_URL
Also, let's do
base/nevent. We can then rely on goToEvent in routes.ts. This will be easier for other clients to parse as well if they want to.@@ -45,0 +75,4 @@if (aOrder !== bOrder) return aOrder - bOrderreturn a.localeCompare(b)})sortBy?71071b54c6toae209615d9ae209615d9tod3abc817d4@@ -37,3 +37,3 @@{@render leading?.()}</div>{@render title?.()}<div class="min-w-0 leading-none">leading-none clips letters than hang down, like
g@@ -0,0 +73,4 @@class="flex flex-wrap items-center justify-between gap-2 border-b border-base-content/10 bg-base-200/40 px-3 py-2 text-xs sm:px-4 sm:text-sm"><span class="opacity-75">{formatTimestamp(event.created_at)}</span><Button class="btn btn-ghost btn-xs h-auto min-h-0 gap-1 px-1 py-0" onclick={copyPermalink}>Post #{number}Numbered posts won't be reliable because events can always get backdated or republished. We should just change this to
[link icon] permalinkor something.@@ -45,0 +50,4 @@const roomThreads = byRoom.get(h) || []roomThreads.push(event)byRoom.set(h, roomThreads)You can do
pushToMapKey(byRoom, h, event)Or even
const byRoom = groupBy(e => getTagValue("h", e.tags))@@ -45,0 +53,4 @@byRoom.set(h, roomThreads)}const roomOrder = new Map(($roomsByUrl.get(url) || []).map((room, index) => [room.h, index]))This seems like it would vary. Why not sort lexically by room name?
@@ -34,0 +70,4 @@const search = params.toString()goto(`${$page.url.pathname}${search ? `?${search}` : ""}`, {replaceState: true,I feel like we should not replace state, but I'm not sure.
@@ -34,2 +79,4 @@const openReply = (post: TrustedEvent) => {replyTo = postshowReply = true}Similar to how chat replies work, let's prepend the compose input with a quote of the event the user clicked reply on.
@@ -93,0 +160,4 @@<ThreadPagination page={currentPage} {pageCount} onPage={setPage} />{/if}{#if showReply && replyTo}<EventReply {url} event={replyTo} onClose={closeReply} onSubmit={closeReply} />This actually changes how threads work and doesn't follow the spec. Replies must always refer to the root
kind 11, not the comments. Replies to comments should be done the same way as chat replies — useqand embed the parent in the quote reply. This matches how old forums used to work too.d3abc817d4tocf238c6bffcf238c6bfftof085b6a510@@ -47,0 +133,4 @@setPage(targetPage)}setTimeout(() => goToEvent(posts[index]!), 100)I think this could be
scrollToEventinstead ofgoToEventsince we're handling the routing here.f085b6a510to823b2c65f3823b2c65f3tod595bdeae8