feat: persist worker-pause state across restarts

Worker pause states can optionally survive app restarts. A new toggle in
Settings saves the current pause map to settings/worker_pauses.json; on
startup lib.rs restores it before workers are spawned. Backend: new
snapshot/replace helpers in indexer.rs, persist functions in commands.rs
(get/set_worker_pauses_persist). Frontend: workerPausesPersist store
field, load/setWorkerPausesPersist actions, toggle in SettingsModal.
This commit is contained in:
2026-06-29 13:21:53 +01:00
parent 949382f28c
commit d81624573d
7 changed files with 151 additions and 1 deletions
+2
View File
@@ -27,6 +27,7 @@ export default function App() {
const loadAlbums = useGalleryStore((state) => state.loadAlbums);
const loadMutedFolderIds = useGalleryStore((state) => state.loadMutedFolderIds);
const loadNotificationsPaused = useGalleryStore((state) => state.loadNotificationsPaused);
const loadWorkerPausesPersist = useGalleryStore((state) => state.loadWorkerPausesPersist);
const subscribeToProgress = useGalleryStore((state) => state.subscribeToProgress);
const loadAppVersion = useGalleryStore((state) => state.loadAppVersion);
const checkForUpdates = useGalleryStore((state) => state.checkForUpdates);
@@ -39,6 +40,7 @@ export default function App() {
void initializeNotifications();
void loadMutedFolderIds();
void loadNotificationsPaused();
void loadWorkerPausesPersist();
void loadFfmpegStatus();
void loadOnboardingCompleted();
// Load the app version first so the What's New toast/modal (which read
+15
View File
@@ -228,6 +228,8 @@ export function SettingsModal() {
const openAppDataFolder = useGalleryStore((state) => state.openAppDataFolder);
const notificationsPaused = useGalleryStore((state) => state.notificationsPaused);
const setNotificationsPaused = useGalleryStore((state) => state.setNotificationsPaused);
const workerPausesPersist = useGalleryStore((state) => state.workerPausesPersist);
const setWorkerPausesPersist = useGalleryStore((state) => state.setWorkerPausesPersist);
const getDatabaseInfo = useGalleryStore((state) => state.getDatabaseInfo);
const vacuumDatabase = useGalleryStore((state) => state.vacuumDatabase);
const rebuildSemanticIndex = useGalleryStore((state) => state.rebuildSemanticIndex);
@@ -854,6 +856,19 @@ export function SettingsModal() {
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${notificationsPaused ? "translate-x-4" : "translate-x-0"}`} />
</button>
</SettingsItem>
<SettingsItem
label="Keep background pauses after restart"
description="When enabled, folders you pause from the sidebar or background bar stay paused the next time Phokus opens."
>
<button
role="switch"
aria-checked={workerPausesPersist}
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none ${workerPausesPersist ? "bg-sky-500" : "bg-white/15"}`}
onClick={() => setWorkerPausesPersist(!workerPausesPersist)}
>
<span className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${workerPausesPersist ? "translate-x-4" : "translate-x-0"}`} />
</button>
</SettingsItem>
</SettingsGroup>
<SettingsGroup title="Maintenance">
+2
View File
@@ -381,9 +381,11 @@ export async function handleMockCommand(cmd: string, payload?: unknown): Promise
case "set_tagging_queue_folder_ids":
case "set_muted_folder_ids":
case "set_notifications_paused":
case "set_worker_pauses_persist":
case "set_worker_paused":
return null;
case "get_notifications_paused":
case "get_worker_pauses_persist":
case "get_onboarding_completed":
return db.scenario !== "empty";
case "set_onboarding_completed":
+18
View File
@@ -400,6 +400,7 @@ interface GalleryState {
taggingQueueFolderIds: number[];
mutedFolderIds: number[];
notificationsPaused: boolean;
workerPausesPersist: boolean;
theme: AppTheme;
lightboxAutoplay: boolean;
lightboxAutoMute: boolean;
@@ -524,6 +525,8 @@ interface GalleryState {
toggleMutedFolder: (folderId: number) => void;
loadNotificationsPaused: () => Promise<void>;
setNotificationsPaused: (paused: boolean) => void;
loadWorkerPausesPersist: () => Promise<void>;
setWorkerPausesPersist: (persist: boolean) => void;
setTheme: (theme: AppTheme) => void;
setLightboxAutoplay: (enabled: boolean) => void;
setLightboxAutoMute: (enabled: boolean) => void;
@@ -889,6 +892,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
taggingQueueFolderIds: [],
mutedFolderIds: [],
notificationsPaused: false,
workerPausesPersist: false,
theme: initialTheme(),
lightboxAutoplay: initialBoolSetting(LIGHTBOX_AUTOPLAY_KEY, true),
lightboxAutoMute: initialBoolSetting(LIGHTBOX_AUTO_MUTE_KEY, false),
@@ -1921,6 +1925,20 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
return entries;
},
loadWorkerPausesPersist: async () => {
try {
const persist = await invoke<boolean>("get_worker_pauses_persist");
set({ workerPausesPersist: persist });
} catch {
// fall back to in-memory default
}
},
setWorkerPausesPersist: (persist) => {
set({ workerPausesPersist: persist });
void invoke("set_worker_pauses_persist", { persist }).catch(() => {});
},
setTheme: (theme) => {
window.localStorage.setItem(THEME_KEY, theme);
document.documentElement.dataset.theme = theme;