Added export/import features
This commit is contained in:
@@ -15,13 +15,14 @@ import {
|
||||
getEntries,
|
||||
getHistory,
|
||||
getLastClosedDay,
|
||||
importBackup,
|
||||
patchDay,
|
||||
patchEntry,
|
||||
putDay,
|
||||
setEntryStart,
|
||||
} from './db.js';
|
||||
import { buildModel, suggestClients, suggestTasks, TRANSPORT_TASK } from './predict.js';
|
||||
import { settings } from './settings.svelte.js';
|
||||
import { loadSettings, settings } from './settings.svelte.js';
|
||||
import { atLocalTime, HOUR, localDateKey, roundTs, weekday } from './time.js';
|
||||
|
||||
export const dayState = $state({
|
||||
@@ -37,6 +38,8 @@ export const dayState = $state({
|
||||
/** True once the configured end time has passed and the worker has not answered. */
|
||||
needsClosePrompt: false,
|
||||
loaded: false,
|
||||
/** Blocks clock reconciliation while an imported snapshot is installed. */
|
||||
importing: false,
|
||||
});
|
||||
|
||||
export const running = () => dayState.entries.find((e) => e.endTs == null) ?? null;
|
||||
@@ -45,12 +48,24 @@ export const running = () => dayState.entries.find((e) => e.endTs == null) ?? nu
|
||||
|
||||
/** Retention runs once per session, not on every 30 s tick. */
|
||||
let cleanedUpFor = null;
|
||||
const pendingReconciliations = new Set();
|
||||
|
||||
/**
|
||||
* Bring stored state in line with the clock. Safe to call often — it is invoked on
|
||||
* load, on tab focus, and from a 30 s tick.
|
||||
*/
|
||||
export async function reconcile(now = Date.now()) {
|
||||
export function reconcile(now = Date.now()) {
|
||||
if (dayState.importing) return Promise.resolve();
|
||||
const pending = reconcileDay(now);
|
||||
pendingReconciliations.add(pending);
|
||||
pending.then(
|
||||
() => pendingReconciliations.delete(pending),
|
||||
() => pendingReconciliations.delete(pending),
|
||||
);
|
||||
return pending;
|
||||
}
|
||||
|
||||
async function reconcileDay(now) {
|
||||
dayState.today = localDateKey(now);
|
||||
|
||||
// Applying retention here is what makes the setting mean anything; leaving it to
|
||||
@@ -203,6 +218,34 @@ export async function removeEntry(id) {
|
||||
await refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an imported dataset, then rebuild everything that was derived from the old
|
||||
* one: the settings rune (replace mode adopts the file's settings), the prediction
|
||||
* model, and the resume.
|
||||
*
|
||||
* @param {{days: Array, entries: Array, settings: object|null}} payload
|
||||
* @param {'merge'|'replace'} mode
|
||||
*/
|
||||
export async function applyImport(payload, mode) {
|
||||
if (dayState.importing) throw new Error('Un import est déjà en cours.');
|
||||
dayState.importing = true;
|
||||
try {
|
||||
// Let an already-running clock operation finish before replacing its data.
|
||||
// The guard above prevents subsequent ticks/focus events during the import.
|
||||
await Promise.allSettled([...pendingReconciliations]);
|
||||
const result = await importBackup(payload, mode);
|
||||
if (mode === 'replace') await loadSettings();
|
||||
// Do not immediately purge old imported days or create a new working day.
|
||||
// Normal retention resumes next session/day; manual purge remains available.
|
||||
dayState.today = localDateKey();
|
||||
cleanedUpFor = dayState.today;
|
||||
await refresh();
|
||||
return result;
|
||||
} finally {
|
||||
dayState.importing = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function endDay(now = Date.now()) {
|
||||
if (!dayState.day) return;
|
||||
const date = dayState.day.date;
|
||||
|
||||
Reference in New Issue
Block a user