Wired existing typing store (backend WS broadcast + frontend
_handleTypingEvent with 3s auto-expiry) into ChatArea and DMChat.
Typing events throttled to once per 3s. Shows 'X is typing...' or
'X and N others are typing...' below the messages, above the input.
Both ChatArea and DMChat now detect when you scroll near the top
and fetch the next 50 older messages. Scroll position is preserved
so you don't lose your place. Works for both server channels and
DM conversations.
Added [OVERVIEW] tab to ChannelSettingsModal with an editable
channel name field. The ⚙ button on each channel opens the modal.
Users can rename channels without needing to know about the
right-click context menu.
The notification bell and settings gear spans had opacity-0 but
pointer-events were still active. Their stopPropagation handlers
prevented the parent button's onClick from firing when clicking
in the icon area. Added pointer-events-none to the container,
re-enabled on group-hover.
Invalid HTML: <button> inside <button> causes the browser to close
the outer button prematurely, so clicking the channel name never
fires setActiveChannel. Changed inner elements to <span role=button>.
The [S] button only called setDM(false) without navigating away from
/chat area. Since the notes-to-self feature now calls navigate('/dm/:id'),
the URL stayed at /dm/... and ChatArea never re-mounted.
[S] now navigates to /, [@] navigates to /dm.
ConversationList was only setting activeConversationId in the store
but never navigating. If the Outlet was still showing ChatArea from
a server channel route, DMChat never mounted. Now every click
navigates to /dm/:conversationId so the router mounts DMChat.
- Input field no longer nested inside a button/clickable div
- Snapshot state before clearing editing mode to avoid stale closures
- Log errors instead of silently swallowing them
ContextMenu: reusable hook-based component that positions a menu at
the cursor and auto-closes on outside click or Escape.
ChannelList:
- Right-click channel -> Edit Name, Permissions, Delete Channel
- Right-click group header -> Edit Name, Create Channel Here, Delete Group
- Inline rename: replacing name text with an input, commits on Enter/blur
- CreateChannelModal now accepts defaultGroupId to pre-select a group
ServerBar:
- Right-click server icon -> Server Settings, Invite People, Leave Server
- Leave calls DELETE /servers/{id}/members/me
Backend:
- Added LeaveServer handler (DELETE /servers/{serverID}/members/me)
- Server owner cannot leave; must transfer or delete
gif.url is the giphy.com page link, gif.images.fixed_height.url is
the actual media CDN URL. react-markdown was rendering a dead link
instead of an embedded image.
- ForgotPasswordPage: email input, sends reset request, shows success
- ResetPasswordPage: reads token from ?token= query param, password
+ confirm inputs, validates min length, shows success with login link
- LoginForm: added [FORGOT PASSWORD?] link below the form
- App.tsx: /forgot-password and /reset-password routes (public, no auth)
- auth store: requestPasswordReset + resetPassword actions
- Backend routes already existed at POST /auth/request-password-reset
and POST /auth/reset-password
Phase 7.1 — Per-channel notification settings
- New notification_settings table (user_id, channel_id, level)
- GET/PUT/DELETE handlers under /channels/{channelID}/notifications
- Push dispatch and @mention loops filtered by notification level
- Frontend: bell icon per channel cycling all/mentions/none
Phase 7.4 — Read receipts
- New read_states table (user_id, channel_id, last_read_message_id)
- PUT /channels/{channelID}/read and GET /users/me/read-states
- Auto-mark-read when messages load in ChatArea
- Unread dot for channels never opened
Also: favicon/icon refresh in index.html
- New VideoGrid component: live camera feeds (local + remote) in VoicePanel
- Camera toggle in VoiceControls: enable/disable video per participant
- Noise suppression toggle in VoiceControls + voice store
- VOICE_WHISPER WS event: backend routes whisper to target user only
- Whisper UI: per-participant whisper button, notification toasts with dismiss
- Fix circular import between voice.ts and ws.ts (use lazy accessor)
Backend:
- Login handler accepts username OR email via the same field
- New PUT /auth/me/password endpoint (verifies current password,
requires min 8 chars for new password)
Frontend:
- Login form shows EMAIL OR USERNAME field (single field for both)
- Auth store updated with changePassword function
- UserSettings has new CHANGE PASSWORD section
Backend:
- Add GET /api/v1/servers/{serverID}/members endpoint
- Wire MemberHandler routes in main.go
Frontend:
- Create member store (fetchMembers by server ID)
- MemberList now fetches members from API instead of empty props
- ChatArea: show channel name instead of ID, add error display,
add flex-1 + min-w-0 to input for proper sizing, error handling
on sendMessage
Frontend:
- Settings link in header next to logout
- [+] button to create server (name + optional icon)
- [#] button to join server via invite code
- [+] button to create channel (name, type, category)
- Server name shown instead of UUID in channel list header
- /invites/:code route wired for JoinServer component
Backend:
- Wire role CRUD + member-role assignment routes (MANAGE_SERVER gated)
- Seed @everyone default role on server creation