Mark all as read

This commit is contained in:
Jack Kingsman
2026-01-07 17:42:25 -08:00
parent 4df8b89fc4
commit 8ef617fe29
3 changed files with 40 additions and 4 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RemoteTerm for MeshCore</title> <title>RemoteTerm for MeshCore</title>
<script type="module" crossorigin src="/assets/index-Bjui8xah.js"></script> <script type="module" crossorigin src="/assets/index-D9RuHvp1.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-RWG6EjFU.css"> <link rel="stylesheet" crossorigin href="/assets/index-RWG6EjFU.css">
</head> </head>
<body> <body>
+23
View File
@@ -515,6 +515,28 @@ export function App() {
setSidebarOpen(false); setSidebarOpen(false);
}, []); }, []);
// Mark all conversations as read
const handleMarkAllRead = useCallback(() => {
const now = Math.floor(Date.now() / 1000);
// Update localStorage for all channels
for (const channel of channels) {
const key = getStateKey('channel', channel.key);
setLastReadTime(key, now);
}
// Update localStorage for all contacts
for (const contact of contacts) {
if (contact.public_key) {
const key = getStateKey('contact', contact.public_key);
setLastReadTime(key, now);
}
}
// Clear all unread counts
setUnreadCounts({});
}, [channels, contacts]);
// Delete channel handler // Delete channel handler
const handleDeleteChannel = useCallback(async (key: string) => { const handleDeleteChannel = useCallback(async (key: string) => {
if (!confirm('Delete this channel? Message history will be preserved.')) return; if (!confirm('Delete this channel? Message history will be preserved.')) return;
@@ -640,6 +662,7 @@ export function App() {
showCracker={showCracker} showCracker={showCracker}
crackerRunning={crackerRunning} crackerRunning={crackerRunning}
onToggleCracker={() => setShowCracker((prev) => !prev)} onToggleCracker={() => setShowCracker((prev) => !prev)}
onMarkAllRead={handleMarkAllRead}
/> />
); );
+16 -3
View File
@@ -21,15 +21,16 @@ interface SidebarProps {
showCracker: boolean; showCracker: boolean;
crackerRunning: boolean; crackerRunning: boolean;
onToggleCracker: () => void; onToggleCracker: () => void;
onMarkAllRead: () => void;
} }
// Load sort preference from localStorage // Load sort preference from localStorage (default to 'recent')
function loadSortOrder(): SortOrder { function loadSortOrder(): SortOrder {
try { try {
const stored = localStorage.getItem('remoteterm-sortOrder'); const stored = localStorage.getItem('remoteterm-sortOrder');
return stored === 'recent' ? 'recent' : 'alpha'; return stored === 'alpha' ? 'alpha' : 'recent';
} catch { } catch {
return 'alpha'; return 'recent';
} }
} }
@@ -53,6 +54,7 @@ export function Sidebar({
showCracker, showCracker,
crackerRunning, crackerRunning,
onToggleCracker, onToggleCracker,
onMarkAllRead,
}: SidebarProps) { }: SidebarProps) {
const [sortOrder, setSortOrder] = useState<SortOrder>(loadSortOrder); const [sortOrder, setSortOrder] = useState<SortOrder>(loadSortOrder);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
@@ -247,6 +249,17 @@ export function Sidebar({
</div> </div>
)} )}
{/* Mark All Read */}
{!query && Object.keys(unreadCounts).length > 0 && (
<div
className="px-3 py-2.5 cursor-pointer flex items-center gap-2 border-l-2 border-transparent hover:bg-accent"
onClick={onMarkAllRead}
>
<span className="text-muted-foreground text-xs"></span>
<span className="flex-1 truncate text-muted-foreground">Mark all as read</span>
</div>
)}
{/* Channels */} {/* Channels */}
{filteredChannels.length > 0 && ( {filteredChannels.length > 0 && (
<> <>