Files
Toju/instructions.md
2025-12-28 08:23:30 +01:00

19 KiB

User Stories for P2P Chat Application

Reference: webrtc.org

The following user stories describe a peer-to-peer chat and voice application (Discord-like) built with Electron and Angular (using RxJS and NgRx) with a local SQLite store (via SQL.js). A central server only provides a directory of active rooms and users; all message and audio/video data is exchanged directly between peers (no port forwarding needed). Stories are grouped by role and include acceptance criteria and technical notes.


Regular User

Search and Join Chat Server

User Story: As a Regular User, I want to search for available chat servers (rooms) by name or topic so that I can find and join active communities.

Acceptance Criteria:

  • A search input allows queries against the central directory of servers.
  • Matching servers are returned and listed with details (name, current user count, etc.).
  • I can select a server from the list and send a join request.
  • If the server exists and is active, I am connected to that server's peer network.
  • Error is shown if the server is not found or unavailable.

Technical Notes:

  • Use Angular's HttpClient to call the central REST API (e.g. GET /servers?search={query}), which returns an Observable.
  • Use RxJS operators (e.g. debounceTime) to handle user input.
  • Use Angular components/forms for the search UI and display results. Use async pipe in templates for Observables.
  • Store search results and selected server info in an NgRx store or Angular Signals to manage state and reactively update UI.

Create Chatroom (Become Host)

User Story: As a Regular User, I want to create a new chatroom so that I can start my own voice/text server; I should automatically become the room's host.

Acceptance Criteria:

  • I can create a named room; the central server registers the new room.
  • I join the newly created room and see myself listed as the host/owner.
  • The host can set an optional password or topic for the room.
  • Other users searching for rooms see this new room available.
  • If I disconnect without others online, the room is removed from the server directory.

Technical Notes:

  • Use Angular forms to input room details and NgRx actions to handle creation. Dispatch an action that posts to the central server API (e.g. POST /servers) and upon success updates the local store.
  • In the NgRx state, mark the creator as host: true. Follow NgRx best practices (immutable state, action-driven updates).
  • No code style notes here beyond following lint rules (e.g. prefer const, no var, 2-space indent).
  • After creation, initiate the P2P mesh: the host opens listening sockets (WebRTC or TCP sockets) and waits for peers (see "P2P Connectivity" story).

Join Existing Chatroom

User Story: As a Regular User, I want to join an existing chatroom so that I can participate in it.

Acceptance Criteria:

  • I can select an available room and request to join.
  • The host is notified of my request and (if approved) I connect to the room.
  • If no approval step, I immediately join and synchronize chat history and participants.
  • Upon joining, I receive the current chat history (text and possibly voice stream) from one or more peers.

Technical Notes:

  • Use Angular Router or services to handle navigation/joining logic.
  • When joining, use a WebRTC signaling channel (via the central server or direct peer signaling) to exchange ICE candidates (STUN/TURN).
  • Once connected, use WebRTC DataChannels for chat data and Streams for audio/video.
  • On the host side, use NgRx Effects to handle incoming join events and broadcast initial state (messages and user list) to new peer.

Send, Edit, Delete, and React to Messages

User Story: As a Regular User, I want to send text messages, and edit or delete my own messages (and react to any message), so that I can communicate effectively and correct mistakes.

Acceptance Criteria:

  • I can type and send a chat message, which is immediately delivered to all peers.
  • I can edit or delete any of my own previously sent messages; these edits/deletions update on all peers in real time.
  • I can react (e.g. add emoji) to any message; reactions are displayed next to the message for all users.
  • All message operations (send/edit/delete/react) are performed instantly (optimistic UI) and confirmed or synchronized with peers.
  • Deleted messages are removed from the UI for all users.

Technical Notes:

  • Use WebRTC RTCDataChannel (via a library like simple-peer or native API) for sending JSON-encoded message events between peers. Data channels provide reliable, ordered delivery for text.
  • Maintain a local message store (NgRx store or Signals) that holds all chat messages and reactions. Update the store immutably and broadcast updates to other peers.
  • Persist messages locally using SQL.js: run SQLite in-memory and after significant changes call db.export() to save state (or write to a file via Electron). On join, import/export to synchronize history (SQL.js can load an existing Uint8Array DB).
  • Implement send/edit/delete/react actions and reducers/effects in NgRx. Ensure all reducers treat state immutably and use NgRx Effects for any asynchronous broadcasts.
  • Follow lint rules for event handlers and functions (no-unused-vars, consistent naming).

Voice Chat (Push-to-Talk)

User Story: As a Regular User, I want to join a voice channel within the chatroom so that I can talk to other participants (e.g. using push-to-talk or a switch).

Acceptance Criteria:

  • I can enable my microphone and connect to the voice channel.
  • Other users hear my voice with low latency, and I hear theirs.
  • Only one person needs to be host; no central server relaying audio.
  • If I mute or leave, others no longer hear me.
  • Voice quality adapts to network (e.g. uses Opus codec).

Technical Notes:

  • Use WebRTC MediaStreams for audio: call navigator.mediaDevices.getUserMedia({ audio: true }) to capture microphone audio. Add this audio track to the peer connection.
  • On receiving side, use a <audio> element or Web Audio API to play incoming audio streams.
  • Use an ICE/STUN configuration to establish P2P UDP voice channels. This avoids needing port forwarding.
  • Manage audio tracks in NgRx or a service for mute/unmute state. No messages need SQLite storage.
  • Consider using Electron's contextIsolation and secure context for permissions.

Screen Sharing (with Game Capture)

User Story: As a Regular User, I want to share my screen or a game window with others so that I can show my gameplay or desktop.

Acceptance Criteria:

  • I can start screen sharing (full screen, a window, or browser tab).
  • Other users see a video stream of my screen.
  • Shared video uses hardware-accelerated decoding (GPU) for performance.
  • Sharing a game window is possible (capturing fullscreen games).
  • I can stop sharing and return to voice-only.

Technical Notes:

  • Use navigator.mediaDevices.getDisplayMedia() to capture the screen or window as a MediaStream. For game capture, Electron's desktopCapturer or getDisplayMedia() with a game window source can be used.
  • Combine the screen stream with WebRTC peer connections so that peers receive video as part of the call.
  • Configure the WebRTC codec to a hardware-accelerated codec (e.g. VP8/VP9 with GPU decoding) if supported. Electron/Chromium typically handles GPU decode automatically for WebRTC.
  • Ensure to handle multiple video tracks (microphone and screen) in the peer connection.
  • Follow ESLint rules for function naming and spacing in all new components.

Local Chat History (Offline Access)

User Story: As a Regular User, I want the chat history to be saved locally in the app's database so that I can scroll through recent messages even after restarting the app or going offline.

Acceptance Criteria:

  • All sent/received messages in each room are saved in a local SQLite (via SQL.js) database.
  • When I reopen the app or reconnect to a room, previous messages are loaded from the local DB.
  • The database updates dynamically as new messages arrive or are edited/deleted.
  • The DB file (or exported data) persists between app restarts (using Electron's filesystem).

Technical Notes:

  • Initialize sql.js on app startup: load the WASM file (e.g. sql-wasm.wasm) by configuring locateFile in initSqlJs.
  • Create a database (new SQL.Database()) to hold tables like messages and users. Use SQL for durable local storage.
  • On each chat event, insert or update records (INSERT/UPDATE/DELETE). Query the DB to load history.
  • After updates, call db.export() and save the byte array to a file in Electron's app data directory. On restart, load that file into SQL.js to restore state.
  • Keep SQL code tidy and run queries through a service or NgRx effect, in line with TypeScript ESLint rules (no unused vars, semicolons, etc.).

Admin

Moderate Users

User Story: As an Admin (room moderator), I want to remove or ban disruptive users so that I can keep the chatroom safe.

Acceptance Criteria:

  • I see a list of all users in the room and can select a user to kick or ban.
  • If I kick a user, that user is immediately disconnected from the room and cannot rejoin unless invited.
  • If I ban a user, their address/user ID is added to a room-specific ban list; they cannot rejoin even if they try.
  • Kicked or banned users cannot send any messages or voice.
  • A record of the action (who was kicked/banned) is logged in the chat (for transparency).

Technical Notes:

  • On the Angular UI, display user list in an admin dashboard component. Use NgRx store for active users.
  • When "kick/ban" is invoked, dispatch an NgRx action. An NgRx Effect can broadcast a "force-disconnect" signal to that peer's client.
  • The target client, upon receiving this signal, shows a message and disconnects. The room's peers update their state to remove that user.
  • Maintain a ban list (in local state/SQL.js); on new join attempts check this list to reject banned users.
  • Implementation parallels Discord's model: admins can ban/remove members. Follow lint rules and ensure any admin actions update state immutably.

Moderate Chat Content

User Story: As an Admin, I want to delete inappropriate messages (even if sent by others) so that harmful content is removed immediately.

Acceptance Criteria:

  • I see a delete option on any message in the chat history.
  • Deleting a message removes it from the view for all participants in real time.
  • Deleted message history is purged from local DB for all users.
  • A placeholder (e.g. "Message deleted") is shown so others know a message was removed (optional).

Technical Notes:

  • In the message list component, show a delete button for admins on every message.
  • On deletion, dispatch an action; use an NgRx Effect to broadcast a "delete-message" event on the data channel to all peers.
  • Each peer then removes that message from its NgRx store and local SQL DB.
  • Ensure the deletion logic updates the state immutably and follows ESLint rules (e.g. no empty interfaces, one statement per line).

Manage Chatroom / Server Info

User Story: As an Admin, I want to update room settings (name, description, rules) so that I can keep the room information current.

Acceptance Criteria:

  • I can change the room's display name, topic, or description.
  • Changes propagate to all users immediately (they see the new name/topic).
  • If the room is private, I can update the password or invite list.
  • All users see an "updated by admin" notification.

Technical Notes:

  • Use an Angular form or settings dialog for editing room info. Update NgRx store and broadcast changes to peers.
  • Store room metadata in NgRx; use a dedicated slice for "room settings."
  • On update, send a "update-room-info" message over the data channel. Peers update their local UI accordingly.
  • Persist settings in SQL.js so that if the host restarts, the settings remain.

Assign / Revoke Moderator Role (Optional)

User Story: As an Admin, I want to designate other users as moderators or revoke their privileges so that management can be delegated.

Acceptance Criteria:

  • I can promote a user to moderator (grant them admin rights) and demote them.
  • Moderators get the same capabilities as Admin for moderation tasks.
  • The UI reflects their moderator status (e.g., a badge).
  • Role changes are synced to all clients.

Technical Notes:

  • Add a "roles" field in user state (e.g. user = { id, name, role }).
  • Implement an Admin action to change a user's role; broadcast this over the P2P channel.
  • All clients update their NgRx state so that role-based UI (e.g. delete buttons) appear accordingly.
  • Use NgRx to enforce role logic in reducers, following immutability rules.

System

Automatic Host Reassignment

User Story: As a System, I need to automatically elect a new host if the current host leaves so that the room continues operating smoothly.

Acceptance Criteria:

  • If the host disconnects unexpectedly, the system selects another participant as the new host.
  • Selection criteria might be: longest connected, lowest ping, or first joined. (Define one policy.)
  • All clients update to mark the new host (e.g. show a host badge).
  • The new host gains any host privileges (can kick/ban, etc.).
  • The central directory is updated if needed (new host can keep room alive).

Technical Notes:

  • Implement a leader-election algorithm in the P2P mesh: e.g., use Lamport timestamps or simply choose the next-in-list user.
  • On host disconnect event (detected via data channel/peerconnection onclose), dispatch a "host-left" action. An NgRx Effect can determine new host ID and broadcast a "new-host" event.
  • Update NgRx state hostId and any UI accordingly. This follows NgRx unidirectional flow and immutability.
  • Ensure any pending room changes (like last chat state) transfer seamlessly. The local SQLite DB is already consistent across peers, so no data is lost.

Peer-to-Peer Connectivity & NAT Traversal

User Story: As a System, I want all peers to connect directly (audio/data) without requiring users to configure port forwarding.

Acceptance Criteria:

  • Peers automatically discover routes to each other (using STUN/TURN) and establish direct connections.
  • No manual network configuration is needed by users.
  • Voice and data streams work across typical home/office NATs.
  • If a direct path fails, relay via TURN.

Technical Notes:

  • Use WebRTC's ICE framework: supply STUN (and optionally TURN) servers in the RTCPeerConnection configuration.
  • Exchange ICE candidates via the central server signaling (or existing peer connections).
  • The WebRTC API inherently avoids port forwarding by NAT traversal.
  • If TURN is needed, use a public TURN (or self-hosted) relay. This ensures compliance with "no port forwarding" requirement.
  • Manage connections in NgRx/Services; create Effects to handle ICE candidate events and re-dispatch as needed.

Central Server Directory Endpoints

User Story: As a System, I will provide REST endpoints so that clients can search for servers and get lists of active users.

Acceptance Criteria:

  • A GET /servers endpoint returns current active rooms with metadata (name, user count, host).
  • A GET /users endpoint returns currently online users for a room.
  • Clients can POST /join-request to signal intent to join (if such a flow is needed).
  • Data is updated in real time as rooms/users come and go (clients may poll or use WebSocket if implemented).

Technical Notes:

  • Implement server APIs (e.g., with Node.js/Express) that track room and user registrations in memory or a lightweight DB.
  • The Angular app uses HttpClient to call these endpoints.
  • Use RxJS on the frontend to poll or listen (via WebSocket or Server-Sent Events) for updates, updating the NgRx store on arrival.
  • Adhere to API error handling best practices in Angular (interceptors, error messages).

Initiate P2P Signaling

User Story: As a System, I need to facilitate the initial WebRTC handshake between peers so that they can establish direct connections.

Acceptance Criteria:

  • The host and new peers exchange session descriptions (SDP) and ICE candidates via a signaling channel.
  • Signaling can be done over the existing P2P network or via the central server (just for metadata exchange, not media).
  • Once ICE handshake is complete, direct peer connections are established for chat.

Technical Notes:

  • Use the central server or a temporary WebSocket channel for exchanging SDP offers/answers (as per WebRTC spec).
  • Implement an NgRx Effect that listens for signaling messages (e.g. via a WebSocket service) and creates/sets local/remote descriptions on RTCPeerConnection.
  • All signaling messages (offer, answer, ICE candidates) should be sent as JSON over a lightweight channel.
  • Once WebRTC onicecandidate fires, send each candidate to the peer.

Data Synchronization

User Story: As a System, I need to keep each peer's local database in sync so that all users see the same chat history and user list.

Acceptance Criteria:

  • When a new user joins, they receive the full current chat history and user list from an existing peer.
  • All peers agree on the same final state for edits/deletions; conflicts (rare) are resolved consistently (e.g. last-write-wins).
  • The local SQL databases converge to the same data for the same room.
  • After reconnections or partitioning, peers resynchronize missing events.

Technical Notes:

  • On join, the host (or longest-staying peer) can send a "state dump" (all messages and user list) to the newcomer via the data channel, then replay new events.
  • Use timestamps or message IDs to order events. Ensure RxJS streams or NgRx Effects handle out-of-order gracefully.
  • Persist full chat log and user list in SQLite; on conflict (e.g., duplicate message IDs) merge by timestamp.
  • Leverage NgRx selectors to produce sorted message lists, ensuring UI consistency across clients.

Code Quality Guidelines

Note: All code should follow the project's TypeScript and Angular style guidelines.

The ESLint flat config enforces rules like:

  • no-var
  • prefer-const
  • 2-space indentation
  • Angular ESLint for templates

Use these lint rules and Prettier formatting (as noted in config) to maintain code quality. Each component, service, and NgRx element should comply with the attached ESLint configuration.


References

  • WebRTC: webrtc.org - ICE/STUN/TURN, P2P connectivity
  • Angular: angular.dev - HttpClient, Observables, async pipe
  • NgRx: Immutable state management, action-driven updates, Effects
  • SQL.js: github.com/sql-js/sql.js - SQLite in browser/Electron
  • Discord Moderation: discord.com - Moderation model reference