Clear errors on settings tab switch, streamline message mark-as-read, don't resort on render

This commit is contained in:
Jack Kingsman
2026-02-07 21:14:58 -08:00
parent d48595e082
commit 5cd28bd1d9
4 changed files with 30 additions and 7 deletions

View File

@@ -183,6 +183,11 @@ class ContactRepository:
await db.conn.commit()
return cursor.rowcount > 0
@staticmethod
async def mark_all_read(timestamp: int) -> None:
"""Mark all contacts as read at the given timestamp."""
await db.conn.execute("UPDATE contacts SET last_read_at = ?", (timestamp,))
@staticmethod
async def get_by_pubkey_first_byte(hex_byte: str) -> list[Contact]:
"""Get contacts whose public key starts with the given hex byte (2 chars)."""
@@ -269,6 +274,11 @@ class ChannelRepository:
await db.conn.commit()
return cursor.rowcount > 0
@staticmethod
async def mark_all_read(timestamp: int) -> None:
"""Mark all channels as read at the given timestamp."""
await db.conn.execute("UPDATE channels SET last_read_at = ?", (timestamp,))
class MessageRepository:
@staticmethod

View File

@@ -7,7 +7,7 @@ from fastapi import APIRouter, Query
from app.database import db
from app.models import UnreadCounts
from app.repository import MessageRepository
from app.repository import ChannelRepository, ContactRepository, MessageRepository
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/read-state", tags=["read-state"])
@@ -35,9 +35,8 @@ async def mark_all_read() -> dict:
"""
now = int(time.time())
# Update all contacts and channels in one transaction
await db.conn.execute("UPDATE contacts SET last_read_at = ?", (now,))
await db.conn.execute("UPDATE channels SET last_read_at = ?", (now,))
await ContactRepository.mark_all_read(now)
await ChannelRepository.mark_all_read(now)
await db.conn.commit()
logger.info("Marked all contacts and channels as read at %d", now)

View File

@@ -1,4 +1,12 @@
import { useEffect, useLayoutEffect, useRef, useCallback, useState, type ReactNode } from 'react';
import {
useEffect,
useLayoutEffect,
useRef,
useCallback,
useMemo,
useState,
type ReactNode,
} from 'react';
import type { Contact, Message, MessagePath, RadioConfig } from '../types';
import { CONTACT_TYPE_REPEATER } from '../types';
import { formatTime, parseSenderFromText } from '../utils/messageParser';
@@ -296,7 +304,10 @@ export function MessageList({
// Sort messages by received_at ascending (oldest first)
// Note: Deduplication is handled by useConversationMessages.addMessageIfNew()
// and the database UNIQUE constraint on (type, conversation_key, text, sender_timestamp)
const sortedMessages = [...messages].sort((a, b) => a.received_at - b.received_at);
const sortedMessages = useMemo(
() => [...messages].sort((a, b) => a.received_at - b.received_at),
[messages]
);
// Helper to get a unique sender key for grouping messages
const getSenderKey = (msg: Message, sender: string | null): string => {

View File

@@ -485,7 +485,10 @@ export function SettingsModal({
) : (
<Tabs
value={activeTab}
onValueChange={(v) => setActiveTab(v as SettingsTab)}
onValueChange={(v) => {
setActiveTab(v as SettingsTab);
setError('');
}}
className="w-full"
>
<TabsList className="grid w-full grid-cols-5">