mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-08-10 18:53:03 +02:00
Fix non-cache refresh on unfocused active threads; doc and test improvements
This commit is contained in:
+12
-4
@@ -27,6 +27,7 @@ frontend/src/
|
||||
├── prefetch.ts # Consumes prefetched API promises started in index.html
|
||||
├── index.css # Global styles/utilities
|
||||
├── styles.css # Additional global app styles
|
||||
├── themes.css # Color theme definitions
|
||||
├── lib/
|
||||
│ └── utils.ts # cn() — clsx + tailwind-merge helper
|
||||
├── hooks/
|
||||
@@ -53,7 +54,8 @@ frontend/src/
|
||||
│ ├── lastViewedConversation.ts # localStorage for last-viewed conversation
|
||||
│ ├── contactMerge.ts # Merge WS contact updates into list
|
||||
│ ├── localLabel.ts # Local label (text + color) in localStorage
|
||||
│ └── radioPresets.ts # LoRa radio preset configurations
|
||||
│ ├── radioPresets.ts # LoRa radio preset configurations
|
||||
│ └── theme.ts # Theme switching helpers
|
||||
├── components/
|
||||
│ ├── StatusBar.tsx
|
||||
│ ├── Sidebar.tsx
|
||||
@@ -75,6 +77,7 @@ frontend/src/
|
||||
│ ├── ContactStatusInfo.tsx # Contact status info component
|
||||
│ ├── RepeaterDashboard.tsx # Layout shell — delegates to repeater/ panes
|
||||
│ ├── RepeaterLogin.tsx # Repeater login form (password + guest)
|
||||
│ ├── ChannelInfoPane.tsx # Channel detail sheet (stats, top senders)
|
||||
│ ├── NeighborsMiniMap.tsx # Leaflet mini-map for repeater neighbor locations
|
||||
│ ├── settings/
|
||||
│ │ ├── settingsConstants.ts # Settings section type, ordering, labels
|
||||
@@ -85,7 +88,8 @@ frontend/src/
|
||||
│ │ ├── SettingsDatabaseSection.tsx # DB size, cleanup, auto-decrypt, local label
|
||||
│ │ ├── SettingsBotSection.tsx # Bot list, code editor, add/delete/reset
|
||||
│ │ ├── SettingsStatisticsSection.tsx # Read-only mesh network stats
|
||||
│ │ └── SettingsAboutSection.tsx # Version, author, license, links
|
||||
│ │ ├── SettingsAboutSection.tsx # Version, author, license, links
|
||||
│ │ └── ThemeSelector.tsx # Color theme picker
|
||||
│ ├── repeater/
|
||||
│ │ ├── repeaterPaneShared.tsx # Shared: RepeaterPane, KvRow, format helpers
|
||||
│ │ ├── RepeaterTelemetryPane.tsx # Battery, airtime, packet counts
|
||||
@@ -125,6 +129,10 @@ frontend/src/
|
||||
├── sidebar.test.tsx
|
||||
├── unreadCounts.test.ts
|
||||
├── urlHash.test.ts
|
||||
├── appSearchJump.test.tsx
|
||||
├── channelInfoKeyVisibility.test.tsx
|
||||
├── chatHeaderKeyVisibility.test.tsx
|
||||
├── searchView.test.tsx
|
||||
├── useConversationMessages.test.ts
|
||||
├── useConversationMessages.race.test.ts
|
||||
├── useRepeaterDashboard.test.ts
|
||||
@@ -177,7 +185,7 @@ frontend/src/
|
||||
|
||||
- Auto reconnect (3s) with cleanup guard on unmount.
|
||||
- Heartbeat ping every 30s.
|
||||
- Event handlers: `health`, `message`, `contact`, `raw_packet`, `message_acked`, `error`, `success`, `pong` (ignored).
|
||||
- Event handlers: `health`, `message`, `contact`, `raw_packet`, `message_acked`, `contact_deleted`, `channel_deleted`, `error`, `success`, `pong` (ignored).
|
||||
- For `raw_packet` events, use `observation_id` as event identity; `id` is a storage reference and may repeat.
|
||||
|
||||
## URL Hash Navigation (`utils/urlHash.ts`)
|
||||
@@ -310,7 +318,7 @@ Do not rely on old class-only layout assumptions.
|
||||
|
||||
## Testing
|
||||
|
||||
Run all quality checks (backend + frontend, parallelized) from the repo root:
|
||||
Run all quality checks (backend + frontend) from the repo root:
|
||||
|
||||
```bash
|
||||
./scripts/all_quality.sh
|
||||
|
||||
@@ -307,10 +307,20 @@ export function App() {
|
||||
onContactDeleted: (publicKey: string) => {
|
||||
setContacts((prev) => prev.filter((c) => c.public_key !== publicKey));
|
||||
messageCache.remove(publicKey);
|
||||
const active = activeConversationRef.current;
|
||||
if (active?.type === 'contact' && active.id === publicKey) {
|
||||
pendingDeleteFallbackRef.current = true;
|
||||
setActiveConversation(null);
|
||||
}
|
||||
},
|
||||
onChannelDeleted: (key: string) => {
|
||||
setChannels((prev) => prev.filter((c) => c.key !== key));
|
||||
messageCache.remove(key);
|
||||
const active = activeConversationRef.current;
|
||||
if (active?.type === 'channel' && active.id === key) {
|
||||
pendingDeleteFallbackRef.current = true;
|
||||
setActiveConversation(null);
|
||||
}
|
||||
},
|
||||
onRawPacket: (packet: RawPacket) => {
|
||||
setRawPackets((prev) => appendRawPacketUnique(prev, packet, MAX_RAW_PACKETS));
|
||||
@@ -331,6 +341,7 @@ export function App() {
|
||||
setConfig,
|
||||
activeConversationRef,
|
||||
hasNewerMessagesRef,
|
||||
setActiveConversation,
|
||||
setContacts,
|
||||
setChannels,
|
||||
triggerReconcile,
|
||||
|
||||
@@ -76,6 +76,9 @@ export function addMessage(id: string, msg: Message, contentKey: string): boolea
|
||||
.sort((a, b) => b.received_at - a.received_at)
|
||||
.slice(0, MAX_MESSAGES_PER_ENTRY);
|
||||
}
|
||||
// Promote to MRU so actively-messaged conversations aren't evicted
|
||||
cache.delete(id);
|
||||
cache.set(id, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,23 @@ describe('messageCache', () => {
|
||||
expect(entry!.seenContent.has('CHAN-channel123-First contact-1700000000')).toBe(true);
|
||||
});
|
||||
|
||||
it('promotes entry to MRU on addMessage', () => {
|
||||
// Fill cache to capacity
|
||||
for (let i = 0; i < MAX_CACHED_CONVERSATIONS; i++) {
|
||||
messageCache.set(`conv${i}`, createEntry([createMessage({ id: i })]));
|
||||
}
|
||||
|
||||
// addMessage to conv0 (currently LRU) should promote it
|
||||
const msg = createMessage({ id: 999, text: 'Incoming WS message' });
|
||||
messageCache.addMessage('conv0', msg, 'CHAN-channel123-Incoming WS message-1700000000');
|
||||
|
||||
// Add one more — conv1 should now be LRU and get evicted, not conv0
|
||||
messageCache.set('conv_new', createEntry());
|
||||
|
||||
expect(messageCache.get('conv0')).toBeDefined(); // Was promoted by addMessage
|
||||
expect(messageCache.get('conv1')).toBeUndefined(); // Was LRU, evicted
|
||||
});
|
||||
|
||||
it('returns false for duplicate delivery to auto-created entry', () => {
|
||||
const msg = createMessage({ id: 10, text: 'Echo' });
|
||||
const contentKey = 'CHAN-channel123-Echo-1700000000';
|
||||
|
||||
Reference in New Issue
Block a user