diff --git a/frontend/src/components/settings/SettingsDatabaseSection.tsx b/frontend/src/components/settings/SettingsDatabaseSection.tsx index dcfe209..5a7cc48 100644 --- a/frontend/src/components/settings/SettingsDatabaseSection.tsx +++ b/frontend/src/components/settings/SettingsDatabaseSection.tsx @@ -121,93 +121,93 @@ export function SettingsDatabaseSection({ return (
+ {/* ── Database Overview ── */}
-
- Database size - {health?.database_size_mb ?? '?'} MB -
- - {health?.oldest_undecrypted_timestamp ? ( + +
- Oldest undecrypted packet - - {formatTime(health.oldest_undecrypted_timestamp)} - - ({Math.floor((Date.now() / 1000 - health.oldest_undecrypted_timestamp) / 86400)}{' '} - days old) + Database size + {health?.database_size_mb ?? '?'} MB +
+
+ Oldest undecrypted packet + {health?.oldest_undecrypted_timestamp ? ( + + {formatTime(health.oldest_undecrypted_timestamp)} + + ({Math.floor((Date.now() / 1000 - health.oldest_undecrypted_timestamp) / 86400)}{' '} + days) + - + ) : ( + None + )}
- ) : ( -
- Oldest undecrypted packet - None -
- )} +
-
- -

- Permanently deletes stored raw packets containing DMs and channel messages that have not - yet been decrypted. These packets are retained in case you later obtain the correct key — - once deleted, these messages can never be recovered or decrypted. -

-
-
- - setRetentionDays(e.target.value)} - className="w-24" - /> + {/* ── Storage Cleanup ── */} +
+ + +
+ +

+ Permanently deletes stored raw packets that have not yet been decrypted. These are + retained in case you later obtain the correct key — once deleted, these messages can + never be recovered. +

+
+
+ + setRetentionDays(e.target.value)} + className="w-24" + /> +
+
+
+ +
+ +

+ Deletes the raw packet bytes behind messages that are already decrypted and visible in + chat. This frees space but removes packet-analysis availability for those messages. It + does not affect displayed messages or future decryption. +

+ {/* ── DM Decryption ── */}
- -

- Deletes archival copies of raw packet bytes for messages that are already decrypted and - visible in your chat history.{' '} - - This will not affect any displayed messages or your ability to do historical decryption, - but it will remove packet-analysis availability for those historical messages. - {' '} - The raw bytes are only useful for manual packet analysis. -

- -
- - - -
- +
+ {error && ( +
+ {error} +
+ )} + + + + {/* ── Contact Management ── */} +
+ +
+ + {/* Block discovery of new node types */} +
+ +

+ Checked types will be ignored when heard via advertisement. Existing contacts of these + types are still updated. This does not affect contacts added manually or via DM. +

+
+ {( + [ + [1, 'Block clients'], + [2, 'Block repeaters'], + [3, 'Block room servers'], + [4, 'Block sensors'], + ] as const + ).map(([typeCode, label]) => { + const checked = discoveryBlockedTypes.includes(typeCode); + return ( + + ); + })} +
+ {discoveryBlockedTypes.length > 0 && ( +

+ New{' '} + {discoveryBlockedTypes + .map((t) => + t === 1 ? 'clients' : t === 2 ? 'repeaters' : t === 3 ? 'room servers' : 'sensors' + ) + .join(', ')}{' '} + heard via advertisement will not be added to your contact list. +

+ )} +
+ + + + {/* Blocked contacts list */}

- Blocking only hides messages from the UI. MQTT forwarding and bot responses are not - affected. Messages are still stored and will reappear if unblocked. + Blocked contacts are hidden from the sidebar. Blocking only hides messages from the UI — + MQTT forwarding and bot responses are not affected. Messages are still stored and will + reappear if unblocked.

{blockedKeys.length === 0 && blockedNames.length === 0 ? ( -

No blocked contacts

+

+ No blocked contacts. Block contacts from their info pane, viewed by clicking their + avatar in any channel, or their name within the top status bar with the conversation + open. +

) : (
{blockedKeys.length > 0 && ( @@ -286,6 +356,7 @@ export function SettingsDatabaseSection({ + {/* Bulk delete */}

@@ -302,64 +373,6 @@ export function SettingsDatabaseSection({ onDeleted={(keys) => onBulkDeleteContacts?.(keys)} />

- - - -
- -

- Checked types will be ignored when heard via advertisement. Existing contacts of these - types are still updated. This does not affect contacts added manually or via DM. -

-
- {( - [ - [1, 'Block clients'], - [2, 'Block repeaters'], - [3, 'Block room servers'], - [4, 'Block sensors'], - ] as const - ).map(([typeCode, label]) => { - const checked = discoveryBlockedTypes.includes(typeCode); - return ( - - ); - })} -
- {discoveryBlockedTypes.length > 0 && ( -

- New{' '} - {discoveryBlockedTypes - .map((t) => - t === 1 ? 'clients' : t === 2 ? 'repeaters' : t === 3 ? 'room servers' : 'sensors' - ) - .join(', ')}{' '} - heard via advertisement will not be added to your contact list. -

- )} -
- - {error && ( -
- {error} -
- )} - -
); } diff --git a/frontend/src/test/settingsModal.test.tsx b/frontend/src/test/settingsModal.test.tsx index 305dafc..6bff4cb 100644 --- a/frontend/src/test/settingsModal.test.tsx +++ b/frontend/src/test/settingsModal.test.tsx @@ -616,10 +616,10 @@ describe('SettingsModal', () => { openDatabaseSection(); expect( - screen.getByText(/remove packet-analysis availability for those historical messages/i) + screen.getByText(/removes packet-analysis availability for those messages/i) ).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: 'Purge Archival Raw Packets' })); + fireEvent.click(screen.getByRole('button', { name: 'Purge Archival Packets' })); await waitFor(() => { expect(runMaintenanceSpy).toHaveBeenCalledWith({ purgeLinkedRawPackets: true });