mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Add docs for new non-WS message fetching and remove dead funcs
This commit is contained in:
@@ -228,6 +228,7 @@ All endpoints are prefixed with `/api` (e.g., `/api/health`).
|
||||
| POST | `/api/packets/maintenance` | Delete old packets (cleanup) |
|
||||
| POST | `/api/contacts/{key}/mark-read` | Mark contact conversation as read |
|
||||
| POST | `/api/channels/{key}/mark-read` | Mark channel as read |
|
||||
| GET | `/api/read-state/unreads` | Server-computed unread counts, mentions, last message times |
|
||||
| POST | `/api/read-state/mark-all-read` | Mark all conversations as read |
|
||||
| GET | `/api/settings` | Get app settings |
|
||||
| PATCH | `/api/settings` | Update app settings |
|
||||
@@ -266,7 +267,7 @@ Read state (`last_read_at`) is tracked **server-side** for consistency across de
|
||||
- Stored as Unix timestamp in `contacts.last_read_at` and `channels.last_read_at`
|
||||
- Updated via `POST /api/contacts/{key}/mark-read` and `POST /api/channels/{key}/mark-read`
|
||||
- Bulk update via `POST /api/read-state/mark-all-read`
|
||||
- Frontend compares `last_read_at` with message `received_at` to count unreads
|
||||
- Aggregated counts via `GET /api/read-state/unreads` (server-side computation)
|
||||
|
||||
**State Tracking Keys (Frontend)**: Generated by `getStateKey()` for message times (sidebar sorting):
|
||||
- Channels: `channel-{channel_key}`
|
||||
|
||||
@@ -35,7 +35,7 @@ app/
|
||||
├── channels.py # Channel CRUD, radio sync, mark-read
|
||||
├── messages.py # Message list and send (direct/channel)
|
||||
├── packets.py # Raw packet endpoints, historical decryption
|
||||
├── read_state.py # Bulk read state operations (mark-all-read)
|
||||
├── read_state.py # Read state: unread counts, mark-all-read
|
||||
├── settings.py # App settings (max_radio_contacts)
|
||||
└── ws.py # WebSocket endpoint at /api/ws
|
||||
```
|
||||
@@ -104,6 +104,10 @@ await ws_manager.broadcast("message", {"id": 1, "text": "Hello"})
|
||||
|
||||
Event types: `health`, `contacts`, `channels`, `message`, `contact`, `raw_packet`, `message_acked`, `error`
|
||||
|
||||
**Note:** The WebSocket initial connect only sends `health`. Contacts and channels are fetched
|
||||
via REST (`GET /api/contacts`, `GET /api/channels`) for faster parallel loading. The WS still
|
||||
broadcasts real-time `contacts`/`channels` updates when data changes.
|
||||
|
||||
Helper functions for common broadcasts:
|
||||
|
||||
```python
|
||||
@@ -490,6 +494,7 @@ All endpoints are prefixed with `/api`.
|
||||
- `DELETE /api/channels/{key}` - Delete channel
|
||||
|
||||
### Read State
|
||||
- `GET /api/read-state/unreads?name=X` - Server-computed unread counts, mention flags, and last message times
|
||||
- `POST /api/read-state/mark-all-read` - Mark all contacts and channels as read
|
||||
|
||||
### Messages
|
||||
|
||||
@@ -497,59 +497,6 @@ class MessageRepository:
|
||||
acked=row["acked"],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def get_bulk(
|
||||
conversations: list[dict],
|
||||
limit_per_conversation: int = 100,
|
||||
) -> dict[str, list["Message"]]:
|
||||
"""Fetch messages for multiple conversations in one query per conversation.
|
||||
|
||||
Args:
|
||||
conversations: List of {type: 'PRIV'|'CHAN', conversation_key: string}
|
||||
limit_per_conversation: Max messages to return per conversation
|
||||
|
||||
Returns:
|
||||
Dict mapping 'type:conversation_key' to list of messages
|
||||
"""
|
||||
result: dict[str, list[Message]] = {}
|
||||
|
||||
for conv in conversations:
|
||||
msg_type = conv.get("type")
|
||||
conv_key = conv.get("conversation_key")
|
||||
if not msg_type or not conv_key:
|
||||
continue
|
||||
|
||||
key = f"{msg_type}:{conv_key}"
|
||||
|
||||
cursor = await db.conn.execute(
|
||||
"""
|
||||
SELECT * FROM messages
|
||||
WHERE type = ? AND conversation_key LIKE ?
|
||||
ORDER BY received_at DESC
|
||||
LIMIT ?
|
||||
""",
|
||||
(msg_type, f"{conv_key}%", limit_per_conversation),
|
||||
)
|
||||
rows = await cursor.fetchall()
|
||||
result[key] = [
|
||||
Message(
|
||||
id=row["id"],
|
||||
type=row["type"],
|
||||
conversation_key=row["conversation_key"],
|
||||
text=row["text"],
|
||||
sender_timestamp=row["sender_timestamp"],
|
||||
received_at=row["received_at"],
|
||||
paths=MessageRepository._parse_paths(row["paths"]),
|
||||
txt_type=row["txt_type"],
|
||||
signature=row["signature"],
|
||||
outgoing=bool(row["outgoing"]),
|
||||
acked=row["acked"],
|
||||
)
|
||||
for row in rows
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
async def get_unread_counts(name: str | None = None) -> dict:
|
||||
"""Get unread message counts, mention flags, and last message times for all conversations.
|
||||
|
||||
@@ -38,19 +38,6 @@ async def list_messages(
|
||||
)
|
||||
|
||||
|
||||
@router.post("/bulk", response_model=dict[str, list[Message]])
|
||||
async def get_messages_bulk(
|
||||
conversations: list[dict],
|
||||
limit_per_conversation: int = Query(default=100, ge=1, le=1000),
|
||||
) -> dict[str, list[Message]]:
|
||||
"""Fetch messages for multiple conversations in one request.
|
||||
|
||||
Body should be a list of {type: 'PRIV'|'CHAN', conversation_key: string}.
|
||||
Returns a dict mapping 'type:conversation_key' to list of messages.
|
||||
"""
|
||||
return await MessageRepository.get_bulk(conversations, limit_per_conversation)
|
||||
|
||||
|
||||
@router.post("/direct", response_model=Message)
|
||||
async def send_direct_message(request: SendDirectMessageRequest) -> Message:
|
||||
"""Send a direct message to a contact."""
|
||||
|
||||
@@ -90,10 +90,13 @@ The `preferences_migrated` flag prevents duplicate migrations.
|
||||
|
||||
### State Flow
|
||||
|
||||
1. **WebSocket** pushes real-time updates (health, contacts, channels, messages)
|
||||
2. **REST API** fetches initial data and handles user actions
|
||||
1. **REST API** fetches initial data on mount in parallel (config, settings, channels, contacts, unreads)
|
||||
2. **WebSocket** pushes real-time updates (health, messages, contact changes, raw packets)
|
||||
3. **Components** receive state as props, call handlers to trigger changes
|
||||
|
||||
**Note:** Contacts and channels are loaded via REST on mount (not from WebSocket initial push).
|
||||
The WebSocket only sends health on initial connect, then broadcasts real-time updates.
|
||||
|
||||
### Conversation Header
|
||||
|
||||
For contacts, the header shows path information alongside "Last heard":
|
||||
@@ -229,8 +232,8 @@ interface Message {
|
||||
}
|
||||
|
||||
interface Conversation {
|
||||
type: 'contact' | 'channel' | 'raw' | 'map';
|
||||
id: string; // PublicKey for contacts, ChannelKey for channels, 'raw'/'map' for special views
|
||||
type: 'contact' | 'channel' | 'raw' | 'map' | 'visualizer';
|
||||
id: string; // PublicKey for contacts, ChannelKey for channels, 'raw'/'map'/'visualizer' for special views
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -397,11 +400,8 @@ for local state tracking, while `conversation_key` is the raw database field.
|
||||
Unread tracking uses server-side `last_read_at` timestamps for cross-device consistency:
|
||||
|
||||
```typescript
|
||||
// Contacts and channels include last_read_at from server
|
||||
interface Contact {
|
||||
// ...
|
||||
last_read_at: number | null; // Unix timestamp when conversation was last read
|
||||
}
|
||||
// Fetch aggregated unread counts from server (replaces bulk message fetch + client-side counting)
|
||||
await api.getUnreads(myName); // Returns { counts, mentions, last_message_times }
|
||||
|
||||
// Mark as read via API (called automatically when viewing conversation)
|
||||
await api.markContactRead(publicKey);
|
||||
@@ -409,7 +409,9 @@ await api.markChannelRead(channelKey);
|
||||
await api.markAllRead(); // Bulk mark all as read
|
||||
```
|
||||
|
||||
Unread count = messages where `received_at > last_read_at`.
|
||||
The `useUnreadCounts` hook fetches counts from `GET /api/read-state/unreads` on mount and
|
||||
when channels/contacts change. Real-time increments are still tracked client-side via WebSocket
|
||||
message events. The server computes unread counts using `last_read_at` vs `received_at`.
|
||||
|
||||
## Utility Functions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user