@bobfrankston/mailx-settings 0.1.44 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +8 -1
- package/index.d.ts.map +1 -1
- package/index.js +71 -0
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The old settings.jsonc is still supported for backward compatibility.
|
|
17
17
|
*/
|
|
18
|
-
import type { MailxSettings, AccountConfig, AutocompleteSettings, AiKeys } from "@bobfrankston/mailx-types";
|
|
18
|
+
import type { MailxSettings, AccountConfig, AutocompleteSettings, AiKeys, ReminderState } from "@bobfrankston/mailx-types";
|
|
19
19
|
declare const LOCAL_DIR: string;
|
|
20
20
|
/** Subscribers notified whenever lastCloudError changes (push to UI immediately). */
|
|
21
21
|
type CloudErrorListener = (error: string | null, context?: {
|
|
@@ -216,6 +216,13 @@ export declare function loadUserDict(): Promise<string[]>;
|
|
|
216
216
|
/** Save user dictionary — merges with the cloud copy so concurrent edits on
|
|
217
217
|
* different machines union rather than overwrite. */
|
|
218
218
|
export declare function saveUserDict(words: string[]): Promise<void>;
|
|
219
|
+
/** Load merged reminder state — cloud first, local cache fallback. */
|
|
220
|
+
export declare function loadReminderState(): Promise<ReminderState>;
|
|
221
|
+
/** Merge `patch` (a device's full local state) into the shared copy and
|
|
222
|
+
* persist. Returns the merged result so the caller can adopt it as its new
|
|
223
|
+
* local state — one round-trip does both push and pull. Skips the cloud
|
|
224
|
+
* write when the merge changed nothing (the common poll case). */
|
|
225
|
+
export declare function saveReminderState(patch: ReminderState): Promise<ReminderState>;
|
|
219
226
|
/** Load settings — unified view combining all files (backward compatible) */
|
|
220
227
|
export declare function loadSettings(): MailxSettings;
|
|
221
228
|
export declare function saveSettings(settings: MailxSettings): Promise<void>;
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
21
|
+
import { REMINDER_STATE_FILE, normalizeReminderState, mergeReminderStates, reminderStatesEqual } from "@bobfrankston/mailx-types";
|
|
21
22
|
import { getCloudProvider, gDriveFindOrCreateFolder, gDriveValidateCachedFolder } from "./cloud.js";
|
|
22
23
|
const __dirname = import.meta.dirname;
|
|
23
24
|
// ── Paths ──
|
|
@@ -1298,6 +1299,76 @@ export async function saveUserDict(words) {
|
|
|
1298
1299
|
.catch(e => console.error(` [cloud] Failed to save ${USER_DICT_FILE}: ${e.message}`));
|
|
1299
1300
|
}
|
|
1300
1301
|
}
|
|
1302
|
+
// ── Reminder dismissed/snoozed state (cross-device) ──
|
|
1303
|
+
// Same rails as userdict.csv: cloud copy is authoritative, local file is the
|
|
1304
|
+
// offline cache, every save is read-merge-write so concurrent devices union
|
|
1305
|
+
// rather than clobber. Merge/prune logic lives in mailx-types so the
|
|
1306
|
+
// Android/web service shares it. See reminder-state.ts for the data model.
|
|
1307
|
+
/** Load merged reminder state — cloud first, local cache fallback. */
|
|
1308
|
+
export async function loadReminderState() {
|
|
1309
|
+
const now = Date.now();
|
|
1310
|
+
try {
|
|
1311
|
+
const cloudJson = await cloudRead(REMINDER_STATE_FILE);
|
|
1312
|
+
if (cloudJson != null)
|
|
1313
|
+
return normalizeReminderState(parseJsonc(cloudJson), now);
|
|
1314
|
+
}
|
|
1315
|
+
catch { /* cloud unreachable — fall back to local */ }
|
|
1316
|
+
const sharedPath = path.join(getSharedDir(), REMINDER_STATE_FILE);
|
|
1317
|
+
const localPath = path.join(LOCAL_DIR, REMINDER_STATE_FILE);
|
|
1318
|
+
try {
|
|
1319
|
+
if (fs.existsSync(sharedPath))
|
|
1320
|
+
return normalizeReminderState(parseJsonc(fs.readFileSync(sharedPath, "utf-8")), now);
|
|
1321
|
+
if (fs.existsSync(localPath))
|
|
1322
|
+
return normalizeReminderState(parseJsonc(fs.readFileSync(localPath, "utf-8")), now);
|
|
1323
|
+
}
|
|
1324
|
+
catch { /* unreadable */ }
|
|
1325
|
+
return { dismissed: {}, snoozed: {} };
|
|
1326
|
+
}
|
|
1327
|
+
/** Merge `patch` (a device's full local state) into the shared copy and
|
|
1328
|
+
* persist. Returns the merged result so the caller can adopt it as its new
|
|
1329
|
+
* local state — one round-trip does both push and pull. Skips the cloud
|
|
1330
|
+
* write when the merge changed nothing (the common poll case). */
|
|
1331
|
+
export async function saveReminderState(patch) {
|
|
1332
|
+
const now = Date.now();
|
|
1333
|
+
const local = normalizeReminderState(patch, now);
|
|
1334
|
+
let cloud = { dismissed: {}, snoozed: {} };
|
|
1335
|
+
let cloudJson = null;
|
|
1336
|
+
try {
|
|
1337
|
+
cloudJson = await cloudRead(REMINDER_STATE_FILE);
|
|
1338
|
+
}
|
|
1339
|
+
catch { /* offline — merge with local file below */ }
|
|
1340
|
+
if (cloudJson != null) {
|
|
1341
|
+
cloud = normalizeReminderState(parseJsonc(cloudJson), now);
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
// Cloud unreachable/absent — merge against the local cache so a
|
|
1345
|
+
// flaky network can't shrink the state file.
|
|
1346
|
+
try {
|
|
1347
|
+
const sharedPath = path.join(getSharedDir(), REMINDER_STATE_FILE);
|
|
1348
|
+
if (fs.existsSync(sharedPath))
|
|
1349
|
+
cloud = normalizeReminderState(parseJsonc(fs.readFileSync(sharedPath, "utf-8")), now);
|
|
1350
|
+
}
|
|
1351
|
+
catch { /* */ }
|
|
1352
|
+
}
|
|
1353
|
+
const merged = mergeReminderStates(cloud, local, now);
|
|
1354
|
+
const text = JSON.stringify(merged, null, 2) + "\n";
|
|
1355
|
+
const sharedDir = getSharedDir();
|
|
1356
|
+
atomicWriteText(path.join(sharedDir, REMINDER_STATE_FILE), text);
|
|
1357
|
+
if (sharedDir !== LOCAL_DIR) {
|
|
1358
|
+
try {
|
|
1359
|
+
atomicWriteText(path.join(LOCAL_DIR, REMINDER_STATE_FILE), text);
|
|
1360
|
+
}
|
|
1361
|
+
catch { /* ignore */ }
|
|
1362
|
+
}
|
|
1363
|
+
// Only push to Drive when this device actually contributed something —
|
|
1364
|
+
// otherwise every alarm poll on every machine would burn a write.
|
|
1365
|
+
if (pendingCloudConfig && !(cloudJson != null && reminderStatesEqual(merged, cloud))) {
|
|
1366
|
+
cloudWrite(REMINDER_STATE_FILE, text)
|
|
1367
|
+
.then(() => console.log(` [cloud] Saved ${REMINDER_STATE_FILE} via ${pendingCloudConfig.provider} API`))
|
|
1368
|
+
.catch(e => console.error(` [cloud] Failed to save ${REMINDER_STATE_FILE}: ${e.message}`));
|
|
1369
|
+
}
|
|
1370
|
+
return merged;
|
|
1371
|
+
}
|
|
1301
1372
|
// ── Legacy compatibility ──
|
|
1302
1373
|
function loadLegacySettings() {
|
|
1303
1374
|
const config = readLocalConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-settings",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"license": "ISC",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
20
|
+
"@bobfrankston/mailx-types": "^0.1.36",
|
|
21
21
|
"jsonc-parser": "^3.3.1"
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
".transformedSnapshot": {
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
36
|
+
"@bobfrankston/mailx-types": "^0.1.36",
|
|
37
37
|
"jsonc-parser": "^3.3.1"
|
|
38
38
|
}
|
|
39
39
|
}
|