Options and lifecycle
There is no options page and no URL for removal. The onInstalled handler does one thing: it
makes the icon open the side panel. Every setting lives in the panel itself, in one of two
places:
- The header holds the language (see localization) and NSFW
mode. Both matter before the settings dialog has anything to show. An empty cache still needs
a language, and an empty membership list still needs to know whether to filter. Both stay
reachable from every state (
components/App.tsx). - The settings dialog (
components/SettingsDialog.tsx, opened from the gear button next to the header) holds every setting that only matters once the cache already holds something. This is the switch for the byte cache, its size ceiling, the purge that satisfies the retention promise (see risks), and two feed behaviours. Moving these off the header cost no reachability.CacheFooter, their old home, never rendered before the membership list did either.
NSFW mode is off by default (usePersistentState('nsfw-visible', false),
components/App.tsx). Off, a membership whose campaign carries is_nsfw (see
the panel) does not appear in the membership list. A post that carries
is_nsfw renders blurred behind a reveal button (components/PostCard.tsx). A tile of that
post gets the same treatment in the photo view (components/PhotoGallery.tsx). A reveal is local to
the card or the tile: it does not persist, and it does not turn the setting on. Turning the
setting on removes the blur and the filter for the rest of the session. A membership the filter
had removed from the selection is deselected the moment the filter starts hiding it again.
The settings dialog holds five controls, each a usePersistentState value read or written where
it applies, not a new central store:
- Fetch posts automatically (
auto-fetch-posts, default on). Off, opening a creator with no cached posts leaves the empty state up instead of firinggopher/sync-postson its own. The “Fetch posts” button in that empty state still works; it never depended on this setting. - Expand long posts automatically (
auto-expand-posts, default off). Sets the initialexpandedstate ofPostCard, in place of the clamp behind “Show more”. - The byte cache switch and its size — the same
mediaCacheEnabledandrequestMediaCachePermissionpair the dialog and the old footer both used. Asking for the optional host permission still needs the user gesture the dialog’s button gives it. - Media cache ceiling (
cache-ceiling-mb, default 300, clamped to 50–2000 in the dialog).lib/media-cache.tsreads this key directly frombrowser.storage.localon every write (cacheCeilingBytes()), because that module sits outside the React tree. The key must keep matching whatusePersistentState('cache-ceiling-mb', …)writes. - Clear cached data, the same purge the footer’s button used to run.
CacheFooter under the membership list now shows only the live cache-size summary; it renders
nothing that acts on the cache.
The panel asks before it acts. When it starts, it sends gopher/runner-state, which costs no
request to Patreon and passes no limit. If the answer says that the runner is not ready, the
panel shows the empty state with its Open Patreon button and sends no request. If the
answer says ready, the panel starts the automatic sync. Open Patreon then opens or selects
the tab. It asks gopher/runner-state again until the runner answers or the count ends
(approximately ten seconds), and starts a sync. Thus the button that the user sees is the
button that repairs the condition.