Files
DiscordClone/CLAUDE.md
Bryan1029384756 b0f889cb68
All checks were successful
Build and Release / build-and-release (push) Successful in 9m12s
feat: Implement core chat page with channel navigation, direct messages, and voice chat integration.
2026-02-11 17:44:50 -06:00

5.6 KiB

Update this file when making significant changes.

See also: CONVEX_RULES.md | CONVEX_EXAMPLES.md

Architecture

  • Backend: Convex (reactive database + serverless functions)
  • Frontend: React + Vite (Electron app)
  • Auth: Zero-knowledge custom auth via Convex mutations (getSalt, verifyUser, createUserWithProfile)
  • Real-time: Convex reactive queries (useQuery auto-updates all connected clients)
  • Voice/Video: LiveKit (token generation via Convex Node action)
  • E2E Encryption: Client-side via Electron IPC (window.cryptoAPI)
  • File Storage: Convex built-in storage (generateUploadUrl + getUrl)

Key Convex Files (convex/)

  • schema.ts - Full schema: userProfiles (with avatarStorageId, aboutMe, customStatus), categories (name, position), channels (with categoryId, topic, position), messages, messageReactions, channelKeys, roles, userRoles, invites, dmParticipants, typingIndicators, voiceStates, channelReadState
  • auth.ts - getSalt, verifyUser, createUserWithProfile, getPublicKeys (includes avatarUrl, aboutMe, customStatus), updateProfile, updateStatus
  • categories.ts - list, create, rename, remove, reorder
  • channels.ts - list, get, create (with categoryId/topic/position), rename, remove (cascade), updateTopic, moveChannel, reorderChannels
  • members.ts - getChannelMembers (includes isHoist on roles, avatarUrl, aboutMe, customStatus)
  • channelKeys.ts - uploadKeys, getKeysForUser
  • messages.ts - list (with reactions + username), send, remove
  • reactions.ts - add, remove
  • typing.ts - startTyping, stopTyping, getTyping, cleanExpired (scheduled)
  • dms.ts - openDM, listDMs
  • invites.ts - create, use, revoke
  • roles.ts - list, create, update, remove, listMembers, assign, unassign, getMyPermissions
  • voiceState.ts - join, leave, updateState, getAll
  • voice.ts - getToken (Node action, livekit-server-sdk)
  • files.ts - generateUploadUrl, getFileUrl
  • gifs.ts - search, categories (Node actions, Tenor API)
  • readState.ts - getReadState, markRead, getAllReadStates, getLatestMessageTimestamps (unread tracking)

Frontend Structure (Frontend/Electron/src/)

  • main.jsx - ConvexProvider + VoiceProvider + HashRouter
  • pages/Login.jsx - Convex auth (getSalt + verifyUser)
  • pages/Register.jsx - Convex auth (createUserWithProfile + invite flow)
  • pages/Chat.jsx - useQuery for channels, categories, channelKeys, DMs
  • components/ChatArea.jsx - Messages, typing, reactions via Convex queries/mutations
  • components/Sidebar.jsx - Channel/category creation, key distribution, invites, drag-and-drop reordering via @dnd-kit
  • contexts/VoiceContext.jsx - Voice state via Convex + LiveKit room management
  • components/ChannelSettingsModal.jsx - Channel rename/delete via Convex mutations
  • components/ServerSettingsModal.jsx - Role management via Convex queries/mutations
  • components/MessageItem.jsx - Individual message rendering with unread divider support
  • components/Avatar.jsx - Reusable avatar component (image or colored-initial fallback)
  • components/FriendsView.jsx - User list via Convex query
  • components/DMList.jsx - DM user picker via Convex query
  • components/GifPicker.jsx - GIF search via Convex action
  • components/VoiceRoom.jsx - LiveKit token via Convex action

Important Patterns

  • Channel IDs use Convex _id (not id) - all references use channel._id
  • Auth: client hashes DAK -> HAK before sending, server does string comparison
  • First user bootstrap: createUserWithProfile creates Owner + @everyone roles
  • Vite config uses envDir: '../../' to pick up root .env.local
  • socket.io-client fully removed, all socket refs replaced with Convex
  • No Express backend needed - Backend/ directory is legacy and can be deleted
  • Convex queries are reactive - no need for manual refresh or socket listeners
  • File uploads use Convex storage: generateUploadUrl -> POST blob -> getFileUrl
  • Typing indicators use scheduled functions for TTL cleanup
  • CSS uses Discord dark theme colors via :root variables (--bg-primary: #313338, --bg-secondary: #2b2d31, --bg-tertiary: #1e1f22)
  • Sidebar width is 312px (72px server strip + 240px channel panel)
  • Channels are grouped by categoryId (references categories table) with collapsible headers and drag-and-drop reordering (@dnd-kit)
  • Categories are first-class entities with position-based ordering; uncategorized channels show under "Channels" group
  • Members list groups by hoisted roles (isHoist) then Online/Offline
  • Avatar component supports both image URLs and colored-initial fallback
  • Title bar has back/forward navigation arrows
  • Chat header includes thread, pin, members, notification icons + channel topic
  • Voice connected panel includes elapsed time timer
  • Keyboard shortcuts: Ctrl+K (quick switcher), Ctrl+Shift+M (mute toggle)
  • Unread tracking: channelReadState table stores last-read timestamp per user/channel. ChatArea shows red "NEW" divider, Sidebar shows white dot on unread channels

Environment Variables

In .env.local at project root:

  • CONVEX_DEPLOYMENT - Convex deployment URL (set by npx convex dev)
  • VITE_CONVEX_URL - Convex URL for frontend (set by npx convex dev)
  • VITE_LIVEKIT_URL - LiveKit server URL
  • LIVEKIT_API_KEY - LiveKit API key (used in Convex Node action)
  • LIVEKIT_API_SECRET - LiveKit API secret (used in Convex Node action)
  • TENOR_API_KEY - Tenor GIF API key (used in Convex Node action)

Running the App

  1. npm install && npm run install:frontend
  2. npx convex dev (starts Convex backend, creates .env.local)
  3. In another terminal: cd Frontend/Electron && npm run dev (or npm run electron:dev)