Linting and code cleanup for an imitation of order

This commit is contained in:
Jack Kingsman
2026-01-14 20:08:41 -08:00
parent c6d38ce400
commit 076d466fbd
73 changed files with 2799 additions and 1192 deletions
+13 -2
View File
@@ -1,3 +1,14 @@
export { useRepeaterMode, type UseRepeaterModeResult, formatDuration, formatTelemetry, formatNeighbors, formatAcl } from './useRepeaterMode';
export {
useRepeaterMode,
type UseRepeaterModeResult,
formatDuration,
formatTelemetry,
formatNeighbors,
formatAcl,
} from './useRepeaterMode';
export { useUnreadCounts, type UseUnreadCountsResult } from './useUnreadCounts';
export { useConversationMessages, type UseConversationMessagesResult, getMessageContentKey } from './useConversationMessages';
export {
useConversationMessages,
type UseConversationMessagesResult,
getMessageContentKey,
} from './useConversationMessages';
+45 -36
View File
@@ -34,47 +34,56 @@ export function useConversationMessages(
const seenMessageContent = useRef<Set<string>>(new Set());
// Fetch messages for active conversation
const fetchMessages = useCallback(async (showLoading = false) => {
if (!activeConversation || activeConversation.type === 'raw') {
setMessages([]);
setHasOlderMessages(false);
return;
}
const fetchMessages = useCallback(
async (showLoading = false) => {
if (!activeConversation || activeConversation.type === 'raw') {
setMessages([]);
setHasOlderMessages(false);
return;
}
if (showLoading) {
setMessagesLoading(true);
// Clear messages first so MessageList resets scroll state for new conversation
setMessages([]);
}
try {
const data = await api.getMessages({
type: activeConversation.type === 'channel' ? 'CHAN' : 'PRIV',
conversation_key: activeConversation.id,
limit: MESSAGE_PAGE_SIZE,
});
setMessages(data);
// Track seen content for new messages
seenMessageContent.current.clear();
for (const msg of data) {
seenMessageContent.current.add(getMessageContentKey(msg));
}
// If we got a full page, there might be more
setHasOlderMessages(data.length >= MESSAGE_PAGE_SIZE);
} catch (err) {
console.error('Failed to fetch messages:', err);
toast.error('Failed to load messages', {
description: err instanceof Error ? err.message : 'Check your connection',
});
} finally {
if (showLoading) {
setMessagesLoading(false);
setMessagesLoading(true);
// Clear messages first so MessageList resets scroll state for new conversation
setMessages([]);
}
}
}, [activeConversation]);
try {
const data = await api.getMessages({
type: activeConversation.type === 'channel' ? 'CHAN' : 'PRIV',
conversation_key: activeConversation.id,
limit: MESSAGE_PAGE_SIZE,
});
setMessages(data);
// Track seen content for new messages
seenMessageContent.current.clear();
for (const msg of data) {
seenMessageContent.current.add(getMessageContentKey(msg));
}
// If we got a full page, there might be more
setHasOlderMessages(data.length >= MESSAGE_PAGE_SIZE);
} catch (err) {
console.error('Failed to fetch messages:', err);
toast.error('Failed to load messages', {
description: err instanceof Error ? err.message : 'Check your connection',
});
} finally {
if (showLoading) {
setMessagesLoading(false);
}
}
},
[activeConversation]
);
// Fetch older messages (pagination)
const fetchOlderMessages = useCallback(async () => {
if (!activeConversation || activeConversation.type === 'raw' || loadingOlder || !hasOlderMessages) return;
if (
!activeConversation ||
activeConversation.type === 'raw' ||
loadingOlder ||
!hasOlderMessages
)
return;
setLoadingOlder(true);
try {
@@ -87,7 +96,7 @@ export function useConversationMessages(
if (data.length > 0) {
// Prepend older messages (they come sorted DESC, so older are at the end)
setMessages(prev => [...prev, ...data]);
setMessages((prev) => [...prev, ...data]);
// Track seen content
for (const msg of data) {
seenMessageContent.current.add(getMessageContentKey(msg));
+10 -8
View File
@@ -1,6 +1,13 @@
import { useState, useCallback, useMemo, useEffect } from 'react';
import { api } from '../api';
import type { Contact, Conversation, Message, TelemetryResponse, NeighborInfo, AclEntry } from '../types';
import type {
Contact,
Conversation,
Message,
TelemetryResponse,
NeighborInfo,
AclEntry,
} from '../types';
import { CONTACT_TYPE_REPEATER } from '../types';
// Format seconds into human-readable duration (e.g., 1d17h2m, 1h5m, 3m)
@@ -121,7 +128,7 @@ export function useRepeaterMode(
// Check if active conversation is a repeater
const activeContactIsRepeater = useMemo(() => {
if (!activeConversation || activeConversation.type !== 'contact') return false;
const contact = contacts.find(c => c.public_key === activeConversation.id);
const contact = contacts.find((c) => c.public_key === activeConversation.id);
return contact?.type === CONTACT_TYPE_REPEATER;
}, [activeConversation, contacts]);
@@ -181,12 +188,7 @@ export function useRepeaterMode(
if (!activeContactIsRepeater || !repeaterLoggedIn) return;
// Show the command as an outgoing message
const commandMessage = createLocalMessage(
activeConversation.id,
`> ${command}`,
true,
0
);
const commandMessage = createLocalMessage(activeConversation.id, `> ${command}`, true, 0);
setMessages((prev) => [...prev, commandMessage]);
try {
+21 -15
View File
@@ -51,19 +51,21 @@ export function useUnreadCounts(
// Fetch messages and count unreads for new channels/contacts
// Uses server-side last_read_at for consistent read state across devices
useEffect(() => {
const newChannels = channels.filter(c => !fetchedChannels.current.has(c.key));
const newContacts = contacts.filter(c => c.public_key && !fetchedContacts.current.has(c.public_key));
const newChannels = channels.filter((c) => !fetchedChannels.current.has(c.key));
const newContacts = contacts.filter(
(c) => c.public_key && !fetchedContacts.current.has(c.public_key)
);
if (newChannels.length === 0 && newContacts.length === 0) return;
// Mark as fetched before starting (to avoid duplicate fetches if effect re-runs)
newChannels.forEach(c => fetchedChannels.current.add(c.key));
newContacts.forEach(c => fetchedContacts.current.add(c.public_key));
newChannels.forEach((c) => fetchedChannels.current.add(c.key));
newContacts.forEach((c) => fetchedContacts.current.add(c.public_key));
const fetchAndCountUnreads = async () => {
const conversations: Array<{ type: 'PRIV' | 'CHAN'; conversation_key: string }> = [
...newChannels.map(c => ({ type: 'CHAN' as const, conversation_key: c.key })),
...newContacts.map(c => ({ type: 'PRIV' as const, conversation_key: c.public_key })),
...newChannels.map((c) => ({ type: 'CHAN' as const, conversation_key: c.key })),
...newContacts.map((c) => ({ type: 'PRIV' as const, conversation_key: c.public_key })),
];
if (conversations.length === 0) return;
@@ -82,16 +84,16 @@ export function useUnreadCounts(
// Use server-side last_read_at, fallback to 0 if never read
const lastRead = channel.last_read_at || 0;
const unreadMsgs = msgs.filter(m => !m.outgoing && m.received_at > lastRead);
const unreadMsgs = msgs.filter((m) => !m.outgoing && m.received_at > lastRead);
if (unreadMsgs.length > 0) {
newUnreadCounts[key] = unreadMsgs.length;
// Check if any unread message mentions the user
if (unreadMsgs.some(m => messageContainsMention(m.text, myNameRef.current))) {
if (unreadMsgs.some((m) => messageContainsMention(m.text, myNameRef.current))) {
newMentions[key] = true;
}
}
const latestTime = Math.max(...msgs.map(m => m.received_at));
const latestTime = Math.max(...msgs.map((m) => m.received_at));
newLastMessageTimes[key] = latestTime;
setLastMessageTime(key, latestTime);
}
@@ -105,26 +107,26 @@ export function useUnreadCounts(
// Use server-side last_read_at, fallback to 0 if never read
const lastRead = contact.last_read_at || 0;
const unreadMsgs = msgs.filter(m => !m.outgoing && m.received_at > lastRead);
const unreadMsgs = msgs.filter((m) => !m.outgoing && m.received_at > lastRead);
if (unreadMsgs.length > 0) {
newUnreadCounts[key] = unreadMsgs.length;
// Check if any unread message mentions the user
if (unreadMsgs.some(m => messageContainsMention(m.text, myNameRef.current))) {
if (unreadMsgs.some((m) => messageContainsMention(m.text, myNameRef.current))) {
newMentions[key] = true;
}
}
const latestTime = Math.max(...msgs.map(m => m.received_at));
const latestTime = Math.max(...msgs.map((m) => m.received_at));
newLastMessageTimes[key] = latestTime;
setLastMessageTime(key, latestTime);
}
}
if (Object.keys(newUnreadCounts).length > 0) {
setUnreadCounts(prev => ({ ...prev, ...newUnreadCounts }));
setUnreadCounts((prev) => ({ ...prev, ...newUnreadCounts }));
}
if (Object.keys(newMentions).length > 0) {
setMentions(prev => ({ ...prev, ...newMentions }));
setMentions((prev) => ({ ...prev, ...newMentions }));
}
setLastMessageTimes(getLastMessageTimes());
} catch (err) {
@@ -138,7 +140,11 @@ export function useUnreadCounts(
// Mark conversation as read when user views it
// Calls server API to persist read state across devices
useEffect(() => {
if (activeConversation && activeConversation.type !== 'raw' && activeConversation.type !== 'map') {
if (
activeConversation &&
activeConversation.type !== 'raw' &&
activeConversation.type !== 'map'
) {
const key = getStateKey(
activeConversation.type as 'channel' | 'contact',
activeConversation.id