Add message archive export and improve chat quality of life - #44
Add message archive export and improve chat quality of life#44mixtapejaxson wants to merge 2 commits into
Conversation
Dependency ReviewThe following issues were found:
OpenSSF ScorecardScorecard details
Scanned Files
|
|
This is still a WIP. There's weird quirks with the message parser that I don't know how to fix quite yet. However I have made it more readable |
Tighten message archive extraction so exports keep readable chat text and stop surfacing opaque ids, protobuf fragments, and encoded junk. Normalize archived records with stable defaults, improve sender fallbacks for direct chats, and avoid leaking names from other conversations into exports.
There was a problem hiding this comment.
Pull request overview
Adds a local Message Archive & Export capability to the chat tooling, wiring it into settings UI and a new module that syncs archived messages from Snapchat’s store, along with several settings-menu QoL tweaks and dependency bumps.
Changes:
- Introduces
messageArchivelibrary +MessageArchiveModuleto track conversations/messages, log deletions, and export archive/conversation data. - Adds a new settings panel for archive controls (enable/disable, limit, export, clear) and updates the Settings tabs to surface it.
- Refactors/formatting updates across settings components, telemetry-blocker, and updates several frontend/build dependencies.
Reviewed changes
Copilot reviewed 12 out of 20 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/script/modules/telemetry-blocker/workerInject.ts | Reformats worker-injected fetch hook and broadcast handling. |
| src/script/modules/telemetry-blocker/index.ts | Updates worker injection + fetch/XHR blocking wiring and broadcasts settings to worker. |
| src/script/modules/settings-menu/index.scss | Tweaks tab transition formatting and adds Group wrapping behavior. |
| src/script/modules/settings-menu/components/settings/Unread.tsx | Minor formatting cleanup. |
| src/script/modules/settings-menu/components/settings/MessageArchive.tsx | New settings UI for archiving, exporting, and clearing message history. |
| src/script/modules/settings-menu/components/settings/LocalSaveSnaps.tsx | Formatting/indentation cleanup in Local Save Snaps settings UI. |
| src/script/modules/settings-menu/components/settings/InfiniteSnapRewatch.tsx | Wraps long description line (no behavior change). |
| src/script/modules/settings-menu/components/settings/DisableTelemetry.tsx | Minor formatting cleanup. |
| src/script/modules/settings-menu/components/settings/DisableMetrics.tsx | Minor formatting cleanup. |
| src/script/modules/settings-menu/components/settings/ChatHandling.tsx | Removes extra whitespace. |
| src/script/modules/settings-menu/components/settings/BlockSpotlight.tsx | Minor formatting cleanup. |
| src/script/modules/settings-menu/components/settings/AutocacheBitmoji.tsx | Minor formatting cleanup. |
| src/script/modules/settings-menu/components/SettingsTabs.tsx | Adds new setting entries to tab filters and improves formatting. |
| src/script/modules/message-content/index.ts | Minor formatting fix for instanceof check. |
| src/script/modules/message-archive/index.ts | New module to sync archive state from the Snapchat store based on settings. |
| src/script/modules/local-save-snaps/index.tsx | Refactors/formatting cleanup and adds cache-duration behavior to retrieval. |
| src/script/lib/messageArchive.ts | New archive implementation (syncing, pruning, deletion markers, JSON/Markdown export). |
| src/script/lib/constants.ts | Adds new setting IDs and defaults for message archive features. |
| package.json | Dependency version updates (Mantine, icons, esbuild, etc.). |
| package-lock.json | Lockfile updates corresponding to dependency bumps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const [messageId, message] of messageEntries) { | ||
| currentIds.add(messageId); | ||
| const previous = archivedConversation.messages[messageId]; | ||
| const nextMessage = buildArchivedMessage(messageId, message, serializedId, title, previous); | ||
|
|
||
| if (JSON.stringify(previous) !== JSON.stringify(nextMessage)) { | ||
| archivedConversation.messages[messageId] = nextMessage; | ||
| changed = true; | ||
| } |
There was a problem hiding this comment.
syncConversations does JSON.stringify(previous) !== JSON.stringify(nextMessage) for every message on every sync. This is fairly expensive (allocations + full serialization) and will scale poorly as the archive grows / store updates become frequent. Consider comparing only the fields that can change (e.g., text, deletedAt, lastSeenAt, contentType, etc.) or using a cheaper equality check to decide when to set changed.
| [SettingIds.MESSAGE_ARCHIVE]: true, | ||
| [SettingIds.MESSAGE_DELETE_LOGGING]: true, |
There was a problem hiding this comment.
MESSAGE_ARCHIVE and MESSAGE_DELETE_LOGGING default to true, which means message history is persisted and deleted-message contents are logged to the console by default. This increases retained sensitive data and can expose message content via logs without explicit user opt-in. Consider defaulting these to false (or at least default MESSAGE_DELETE_LOGGING to false) and relying on the settings UI to enable them intentionally.
| [SettingIds.MESSAGE_ARCHIVE]: true, | |
| [SettingIds.MESSAGE_DELETE_LOGGING]: true, | |
| [SettingIds.MESSAGE_ARCHIVE]: false, | |
| [SettingIds.MESSAGE_DELETE_LOGGING]: false, |
| .mantine-Group-root { | ||
| flex-wrap: wrap; | ||
| } |
There was a problem hiding this comment.
.mantine-Group-root { flex-wrap: wrap; } is a global selector and will affect every Mantine Group in the app, not just the settings menu. This can cause unintended layout changes outside this module. Scope this rule under a settings-menu container (e.g. .settingsTabs .mantine-Group-root or another local wrapper) so it only applies where needed.
| const NAME = 'Infinite Snap Rewatch'; | ||
| const DESCRIPTION = 'Only opens the snap client side, allows us to rewatch it infinitly. You currently need to reload the page to rewatch it'; | ||
| const DESCRIPTION = | ||
| 'Only opens the snap client side, allows us to rewatch it infinitly. You currently need to reload the page to rewatch it'; |
There was a problem hiding this comment.
Spelling: the description says "infinitly"; this should be "infinitely".
| 'Only opens the snap client side, allows us to rewatch it infinitly. You currently need to reload the page to rewatch it'; | |
| 'Only opens the snap client side, allows us to rewatch it infinitely. You currently need to reload the page to rewatch it'; |
| // Minified logInfo - looking for a better way... | ||
| const PREFIX = '[SnapTweak - WebWorker Hook]'; | ||
| function logInfo(...n: any[]) { | ||
| console.log(`%c${PREFIX}`, 'color: #3b5bdb', ...n); | ||
| } |
There was a problem hiding this comment.
The comment says "Minified logInfo" but the helper is no longer minified. Update/remove the comment so it accurately reflects the current code.
| logInfo(infiniteRewatchSnap, unread) | ||
| logInfo(infiniteRewatchSnap, unread); | ||
|
|
||
| BROADCAST_CHANNEL.postMessage({ infiniteRewatchSnap: infiniteRewatchSnap, unread: unread }); |
There was a problem hiding this comment.
BROADCAST_CHANNEL.postMessage sends { infiniteRewatchSnap, unread }, but the injected worker hook reads event.data.infiniteRewatch. As-is, toggling Infinite Snap Rewatch won’t update the worker-side infiniteRewatchEnabled value. Align the payload key with what workerInject.ts expects (or update the worker to read infiniteRewatchSnap).
| BROADCAST_CHANNEL.postMessage({ infiniteRewatchSnap: infiniteRewatchSnap, unread: unread }); | |
| BROADCAST_CHANNEL.postMessage({ infiniteRewatch: infiniteRewatchSnap, unread: unread }); |
| infiniteRewatchEnabled = event.data.infiniteRewatch; | ||
| unreadEnabled = event.data.unread; |
There was a problem hiding this comment.
The broadcast handler assigns infiniteRewatchEnabled = event.data.infiniteRewatch, but the main thread currently posts infiniteRewatchSnap (see telemetry-blocker/index.ts). This prevents the worker from ever receiving updated Infinite Snap Rewatch state. Update the handler to read the actual posted key (and ideally guard against missing/invalid payloads).
| infiniteRewatchEnabled = event.data.infiniteRewatch; | |
| unreadEnabled = event.data.unread; | |
| const data = event.data; | |
| if (!data || typeof data !== 'object') { | |
| return; | |
| } | |
| if (typeof data.infiniteRewatchSnap === 'boolean') { | |
| infiniteRewatchEnabled = data.infiniteRewatchSnap; | |
| } | |
| if (typeof data.unread === 'boolean') { | |
| unreadEnabled = data.unread; | |
| } |
| storage.set(STORAGE_KEY, this.archive); | ||
| this.emitter.emit(UPDATED_EVENT); |
There was a problem hiding this comment.
persist() writes the full archive via storage.set(...) without handling quota/write errors. Since storage.set ultimately calls localStorage.setItem without a try/catch, this can throw (e.g., QuotaExceededError) and break archiving/export (and potentially other modules if the exception bubbles). Wrap the write in error handling and consider fallback behavior (e.g., prune more aggressively, disable archiving, and/or surface a user-visible error).
| storage.set(STORAGE_KEY, this.archive); | |
| this.emitter.emit(UPDATED_EVENT); | |
| try { | |
| storage.set(STORAGE_KEY, this.archive); | |
| this.emitter.emit(UPDATED_EVENT); | |
| return; | |
| } catch (error) { | |
| logWarn('Failed to persist message archive; pruning archive and retrying', error); | |
| } | |
| this.enforceLimit(); | |
| try { | |
| storage.set(STORAGE_KEY, this.archive); | |
| this.emitter.emit(UPDATED_EVENT); | |
| } catch (error) { | |
| logWarn('Failed to persist message archive after retry; disabling this save', error); | |
| } |
|
I'll review it today! |
This pull request introduces a new Message Archive & Export feature, adds related settings, and makes several improvements to the settings menu and code quality. The most significant changes are the addition of the message archive module, new settings and UI for managing message history, and updates to dependencies.
New Features: Message Archive & Export
MessageArchiveModule(src/script/modules/message-archive/index.ts) that enables local message history, deleted message logging, and export functionality. The module listens to relevant settings and keeps the archive in sync with chat state.MessageArchive.tsx) that allows users to enable/disable the archive, adjust archive size, log deleted messages, export chats, and clear the archive.Settings Infrastructure Updates
MESSAGE_ARCHIVE,MESSAGE_DELETE_LOGGING, andMESSAGE_ARCHIVE_LIMITinconstants.ts. [1] [2]Dependency Updates
package.json, including@mantine/core,@mantine/hooks,@tabler/icons-react,eventemitter3,fuse.js,preact, andesbuildto their latest versions for improved stability and features. [1] [2]UI and Code Quality Improvements
local-save-snapsandtelemetry-blocker, improving consistency and error handling. [1] [2] [3] [4] [5] [6] [7] [8] [9]These changes collectively enhance the application's chat management capabilities, provide users with more control over message history, and improve the overall developer and user experience.