Direct Message Domain
Direct messages provide local, offline-safe one-to-one messaging over the existing WebRTC data channel.
Structure
direct-message/
├── application/services/ DirectMessageService, OfflineMessageQueueService, FriendService, PeerDeliveryService
├── domain/ Direct message models and status-transition rules
├── infrastructure/ User-scoped local repositories
└── feature/ DM rail, chat view, message rows, user search, friend button
Flow
DirectMessageService.sendMessage()stores the message locally withQUEUED.PeerDeliveryServicetries to send adirect-messageP2P event to the recipient's current peer id.- If the peer is connected, the sender advances to
SENT; otherwise the message id remains inOfflineMessageQueueService. - The recipient persists the message as
DELIVEREDand sends adirect-message-statusevent back. - Opening the conversation marks incoming messages as
ACKNOWLEDGEDand emits a status event.
Status transitions are monotonic, so a stale SENT event cannot overwrite DELIVERED or ACKNOWLEDGED.
Chat View
The DM view reuses the chat domain's shared message list, composer, overlays, markdown renderer, link embeds, media players, and attachment controls. Direct-message records are mapped into the shared Message shape at the feature boundary so PMs keep the same date separators, replies, editing, deletion, reactions, image lightbox, audio playback, and video playback as server text channels.
Message edits, deletions, and reaction changes are stored locally and mirrored to the peer with direct-message-mutation events. Delivery state remains direct-message-owned and is exposed separately from the visible shared chat row UI.
GIFs
The DM composer reuses the chat domain's KLIPY integration. Availability and GIF search go through the configured signal server API, and selected GIFs are sent as markdown image messages so the same proxy-fallback image rendering path is used in DMs and server chat.
Avatars
Conversation participants keep avatar/profile metadata captured from user cards or room membership. When a PM is opened and the peer avatar is missing, the view asks the peer for the existing profile-avatar sync payload so downloaded user icons can be filled in without adding a DM-specific avatar transport.
Persistence
Repositories are user-scoped and stored locally under metoyou_direct_message_* keys. The storage is intentionally domain-owned so browser and Electron runtimes share the same renderer API without changing the existing chat-message database tables.