fix: stop the viewer crashing on any message with attachments by keeping store selectors referentially stable

This commit is contained in:
Linus Rath
2026-08-29 22:41:09 +02:00
parent a02e7f8a49
commit f228257ec9
2 changed files with 21 additions and 6 deletions
+15 -5
View File
@@ -45,11 +45,21 @@ export function AttachmentChips({ email, jmapAccountId, calendarBannerShown, tne
const t = useLocaleStore((s) => s.t);
const hideInlineImageAttachments = useSettingsStore((s) => s.hideInlineImageAttachments);
const mailAttachmentAction = useSettingsStore((s) => s.mailAttachmentAction);
const exportOpts = useSettingsStore((s) => ({
spaceReplacement: s.exportSpaceReplacement,
lowercase: s.exportLowercase,
stripDiacritics: s.exportStripDiacritics,
}));
// Select primitives, not a fresh object: zustand v5 compares snapshots with
// Object.is, so a selector returning a new object literal on every call
// reports a change on every render - React then re-renders until it gives
// up with "Maximum update depth exceeded" (it crashed the whole viewer).
const exportSpaceReplacement = useSettingsStore((s) => s.exportSpaceReplacement);
const exportLowercase = useSettingsStore((s) => s.exportLowercase);
const exportStripDiacritics = useSettingsStore((s) => s.exportStripDiacritics);
const exportOpts = React.useMemo(
() => ({
spaceReplacement: exportSpaceReplacement,
lowercase: exportLowercase,
stripDiacritics: exportStripDiacritics,
}),
[exportSpaceReplacement, exportLowercase, exportStripDiacritics],
);
const [expanded, setExpanded] = React.useState(false);
const [busyKey, setBusyKey] = React.useState<string | null>(null);
+6 -1
View File
@@ -1,3 +1,4 @@
import React from 'react';
import { Linking } from 'react-native';
import { useSettingsStore, type SidebarApp } from '../stores/settings-store';
@@ -7,7 +8,11 @@ import { useSettingsStore, type SidebarApp } from '../stores/settings-store';
* edits them. Mirrors the webmail's navigation-rail filter on `showOnMobile`.
*/
export function useMobileSidebarApps(): SidebarApp[] {
return useSettingsStore((s) => s.sidebarApps.filter((app) => app.showOnMobile));
// `.filter()` in the selector would allocate a new array per call, which
// zustand v5 reads as a changed snapshot on every render (infinite loop).
// Subscribe to the stored array and derive from it.
const apps = useSettingsStore((s) => s.sidebarApps);
return React.useMemo(() => apps.filter((app) => app.showOnMobile), [apps]);
}
/**