diff --git a/cmd/server/main.go b/cmd/server/main.go index 1b14dec..17fec85 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -250,6 +250,19 @@ func main() { r.Post("/", calHandler.CreateEvent) }) + // Threads (forum posts) + threadHandler := channel.NewHandler(database.DB, permissionsChecker) + r.Route("/channels/{channelID}/threads", func(r chi.Router) { + r.Get("/", threadHandler.ListThreads) + r.Post("/", threadHandler.CreateThread) + }) + r.Patch("/threads/{threadID}", threadHandler.UpdateThread) + + // Forum tags + r.Get("/channels/{channelID}/forum-tags", threadHandler.ListForumTags) + r.Post("/channels/{channelID}/forum-tags", threadHandler.CreateForumTag) + r.Delete("/forum-tags/{tagID}", threadHandler.DeleteForumTag) + // Push notifications pushHandler.RegisterRoutes(r) diff --git a/internal/channel/handlers.go b/internal/channel/handlers.go index 721c7db..f9eb7ed 100644 --- a/internal/channel/handlers.go +++ b/internal/channel/handlers.go @@ -27,10 +27,7 @@ func (h *Handler) RegisterRoutes(r chi.Router) { r.Get("/{channelID}", h.Get) r.Patch("/{channelID}", h.Update) r.Delete("/{channelID}", h.Delete) - r.Post("/{channelID}/threads", h.CreateThread) - r.Get("/{channelID}/threads", h.ListThreads) - r.Patch("/threads/{threadID}", h.UpdateThread) - h.registerForumRoutes(r) + // ponytail: thread + forum routes registered at top level in main.go to match frontend paths h.RegisterCalendarRoutes(r) h.registerDocRoutes(r) h.registerListRoutes(r) diff --git a/web/src/components/ServerBar.tsx b/web/src/components/ServerBar.tsx index af55728..d497d32 100644 --- a/web/src/components/ServerBar.tsx +++ b/web/src/components/ServerBar.tsx @@ -1,8 +1,10 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useServerStore } from "../stores/server.ts"; import { useChannelStore } from "../stores/channel.ts"; import { useLayoutStore } from "../stores/layout.ts"; +import { useConversationStore } from "../stores/conversation.ts"; +import { useReadStatesStore } from "../stores/readStates.ts"; import { CreateServerModal } from "./CreateServerModal.tsx"; import { JoinServerModal } from "./JoinServerModal.tsx"; import { ServerSettingsModal } from "./ServerSettingsModal.tsx"; @@ -33,6 +35,18 @@ export function ServerBar() { const { showMenu, MenuPortal } = useContextMenu(); + // ponytail: compute if any DM conversation has unread messages + const conversations = useConversationStore((s) => s.conversations); + const messagesByConv = useConversationStore((s) => s.messagesByConversation); + const hasConvUnread = useReadStatesStore((s) => s.hasConvUnread); + const hasAnyDMUnread = useMemo(() => { + return conversations.some((conv) => { + const msgs = messagesByConv[conv.id] || []; + const latestId = msgs.length > 0 ? msgs[msgs.length - 1].id : undefined; + return hasConvUnread(conv.id, latestId); + }); + }, [conversations, messagesByConv, hasConvUnread]); + useEffect(() => { fetchServers().then(() => { // ponytail: restore last active server + channel from localStorage @@ -94,34 +108,43 @@ export function ServerBar() { return ( <>