# 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

1. `DirectMessageService.sendMessage()` stores the message locally with `QUEUED`.
2. `PeerDeliveryService` tries to send a `direct-message` P2P event to the recipient's current peer id.
3. If the peer is connected, the sender advances to `SENT`; otherwise the message id remains in `OfflineMessageQueueService`.
4. The recipient persists the message as `DELIVERED` and sends a `direct-message-status` event back.
5. Opening the conversation marks incoming messages as `ACKNOWLEDGED` and 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.
