@bobfrankston/rmfmail 1.2.177 → 1.2.179
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/client/android-bootstrap.bundle.js +167 -6
- package/client/android-bootstrap.bundle.js.map +3 -3
- package/client/app.bundle.js +79 -7
- package/client/app.bundle.js.map +3 -3
- package/client/components/alarms.js +88 -11
- package/client/components/alarms.js.map +1 -1
- package/client/components/alarms.ts +83 -13
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +6 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +9 -0
- package/client/lib/mailxapi.js +27 -5
- package/package.json +7 -7
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- package/packages/mailx-service/index.d.ts +5 -0
- package/packages/mailx-service/index.d.ts.map +1 -1
- package/packages/mailx-service/index.js +59 -12
- package/packages/mailx-service/index.js.map +1 -1
- package/packages/mailx-service/index.ts +63 -13
- package/packages/mailx-service/jsonrpc.js +4 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +4 -0
- package/packages/mailx-service/package.json +1 -1
- package/packages/mailx-settings/index.d.ts +8 -1
- package/packages/mailx-settings/index.d.ts.map +1 -1
- package/packages/mailx-settings/index.js +71 -0
- package/packages/mailx-settings/index.js.map +1 -1
- package/packages/mailx-settings/index.ts +61 -1
- package/packages/mailx-settings/package.json +1 -1
- package/packages/mailx-store/package.json +1 -1
- package/packages/mailx-store-web/android-bootstrap.js +4 -0
- package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
- package/packages/mailx-store-web/android-bootstrap.ts +4 -0
- package/packages/mailx-store-web/package.json +1 -1
- package/packages/mailx-store-web/web-service.d.ts +4 -0
- package/packages/mailx-store-web/web-service.d.ts.map +1 -1
- package/packages/mailx-store-web/web-service.js +77 -8
- package/packages/mailx-store-web/web-service.js.map +1 -1
- package/packages/mailx-store-web/web-service.ts +71 -9
- package/packages/mailx-types/index.d.ts +4 -0
- package/packages/mailx-types/index.d.ts.map +1 -1
- package/packages/mailx-types/index.js +6 -0
- package/packages/mailx-types/index.js.map +1 -1
- package/packages/mailx-types/index.ts +12 -0
- package/packages/mailx-types/mailx-api.d.ts +14 -0
- package/packages/mailx-types/mailx-api.d.ts.map +1 -1
- package/packages/mailx-types/mailx-api.ts +5 -0
- package/packages/mailx-types/mime-inline-images.d.ts +38 -0
- package/packages/mailx-types/mime-inline-images.d.ts.map +1 -0
- package/packages/mailx-types/mime-inline-images.js +45 -0
- package/packages/mailx-types/mime-inline-images.js.map +1 -0
- package/packages/mailx-types/mime-inline-images.ts +55 -0
- package/packages/mailx-types/package.json +1 -1
- package/packages/mailx-types/reminder-state.d.ts +42 -0
- package/packages/mailx-types/reminder-state.d.ts.map +1 -0
- package/packages/mailx-types/reminder-state.js +85 -0
- package/packages/mailx-types/reminder-state.js.map +1 -0
- package/packages/mailx-types/reminder-state.ts +88 -0
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-69680 → node_modules.npmglobalize-stash-48352}/.package-lock.json +0 -0
|
@@ -2328,10 +2328,91 @@ var init_groups = __esm({
|
|
|
2328
2328
|
}
|
|
2329
2329
|
});
|
|
2330
2330
|
|
|
2331
|
+
// packages/mailx-types/reminder-state.js
|
|
2332
|
+
function normalizeReminderState(raw, now) {
|
|
2333
|
+
const out = { dismissed: {}, snoozed: {} };
|
|
2334
|
+
if (raw && typeof raw === "object") {
|
|
2335
|
+
for (const [k, v] of Object.entries(raw.dismissed || {})) {
|
|
2336
|
+
if (v === true)
|
|
2337
|
+
out.dismissed[k] = now;
|
|
2338
|
+
else if (Number.isFinite(v) && v > 0)
|
|
2339
|
+
out.dismissed[k] = v;
|
|
2340
|
+
}
|
|
2341
|
+
for (const [k, v] of Object.entries(raw.snoozed || {})) {
|
|
2342
|
+
if (Number.isFinite(v) && v > 0)
|
|
2343
|
+
out.snoozed[k] = v;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return out;
|
|
2347
|
+
}
|
|
2348
|
+
function mergeReminderStates(a, b, now) {
|
|
2349
|
+
const out = { dismissed: {}, snoozed: {} };
|
|
2350
|
+
const dismissedCutoff = now - DISMISSED_RETENTION_MS;
|
|
2351
|
+
const snoozedCutoff = now - SNOOZED_RETENTION_MS;
|
|
2352
|
+
for (const src of [a.dismissed, b.dismissed]) {
|
|
2353
|
+
for (const [k, v] of Object.entries(src)) {
|
|
2354
|
+
if (v < dismissedCutoff)
|
|
2355
|
+
continue;
|
|
2356
|
+
if (!(k in out.dismissed) || v > out.dismissed[k])
|
|
2357
|
+
out.dismissed[k] = v;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
for (const src of [a.snoozed, b.snoozed]) {
|
|
2361
|
+
for (const [k, v] of Object.entries(src)) {
|
|
2362
|
+
if (v < snoozedCutoff)
|
|
2363
|
+
continue;
|
|
2364
|
+
if (!(k in out.snoozed) || v > out.snoozed[k])
|
|
2365
|
+
out.snoozed[k] = v;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
return out;
|
|
2369
|
+
}
|
|
2370
|
+
function reminderStatesEqual(a, b) {
|
|
2371
|
+
const mapsEqual = (x, y) => {
|
|
2372
|
+
const xk = Object.keys(x);
|
|
2373
|
+
if (xk.length !== Object.keys(y).length)
|
|
2374
|
+
return false;
|
|
2375
|
+
return xk.every((k) => y[k] === x[k]);
|
|
2376
|
+
};
|
|
2377
|
+
return mapsEqual(a.dismissed, b.dismissed) && mapsEqual(a.snoozed, b.snoozed);
|
|
2378
|
+
}
|
|
2379
|
+
var REMINDER_STATE_FILE, DISMISSED_RETENTION_MS, SNOOZED_RETENTION_MS;
|
|
2380
|
+
var init_reminder_state = __esm({
|
|
2381
|
+
"packages/mailx-types/reminder-state.js"() {
|
|
2382
|
+
"use strict";
|
|
2383
|
+
REMINDER_STATE_FILE = "reminders.jsonc";
|
|
2384
|
+
DISMISSED_RETENTION_MS = 30 * 864e5;
|
|
2385
|
+
SNOOZED_RETENTION_MS = 7 * 864e5;
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
|
|
2389
|
+
// packages/mailx-types/mime-inline-images.js
|
|
2390
|
+
function extractInlineImages(html, idDomain) {
|
|
2391
|
+
const images = [];
|
|
2392
|
+
const byDataUri = /* @__PURE__ */ new Map();
|
|
2393
|
+
const stamp = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
2394
|
+
const out = html.replace(/(<img\b[^>]*?\ssrc=)(["'])(data:(image\/[\w.+-]+);base64,([A-Za-z0-9+/=\s]+))\2/gi, (_m, prefix, quote, dataUri, mime, b64) => {
|
|
2395
|
+
let cid = byDataUri.get(dataUri);
|
|
2396
|
+
if (!cid) {
|
|
2397
|
+
cid = `img${images.length + 1}.${stamp}@${idDomain}`;
|
|
2398
|
+
byDataUri.set(dataUri, cid);
|
|
2399
|
+
images.push({ contentId: cid, mime, base64: b64.replace(/\s+/g, "") });
|
|
2400
|
+
}
|
|
2401
|
+
return `${prefix}${quote}cid:${cid}${quote}`;
|
|
2402
|
+
});
|
|
2403
|
+
return { html: out, images };
|
|
2404
|
+
}
|
|
2405
|
+
var init_mime_inline_images = __esm({
|
|
2406
|
+
"packages/mailx-types/mime-inline-images.js"() {
|
|
2407
|
+
"use strict";
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
|
|
2331
2411
|
// packages/mailx-types/index.js
|
|
2332
2412
|
var mailx_types_exports = {};
|
|
2333
2413
|
__export(mailx_types_exports, {
|
|
2334
2414
|
CONTACT_RULES: () => CONTACT_RULES,
|
|
2415
|
+
REMINDER_STATE_FILE: () => REMINDER_STATE_FILE,
|
|
2335
2416
|
addContactsDenylistEntry: () => addContactsDenylistEntry,
|
|
2336
2417
|
addContactsPreferredEntry: () => addContactsPreferredEntry,
|
|
2337
2418
|
answeredOf: () => answeredOf,
|
|
@@ -2340,10 +2421,14 @@ __export(mailx_types_exports, {
|
|
|
2340
2421
|
encodeQuotedPrintable: () => encodeQuotedPrintable,
|
|
2341
2422
|
expandRecipients: () => expandRecipients,
|
|
2342
2423
|
extractAddress: () => extractAddress,
|
|
2424
|
+
extractInlineImages: () => extractInlineImages,
|
|
2343
2425
|
flaggedOf: () => flaggedOf,
|
|
2344
2426
|
htmlToPlainText: () => htmlToPlainText,
|
|
2345
2427
|
isAddressToken: () => isAddressToken,
|
|
2428
|
+
mergeReminderStates: () => mergeReminderStates,
|
|
2429
|
+
normalizeReminderState: () => normalizeReminderState,
|
|
2346
2430
|
parseSearchQuery: () => parseSearchQuery,
|
|
2431
|
+
reminderStatesEqual: () => reminderStatesEqual,
|
|
2347
2432
|
sanitizeHtml: () => sanitizeHtml,
|
|
2348
2433
|
seenOf: () => seenOf,
|
|
2349
2434
|
setAnswered: () => setAnswered,
|
|
@@ -2544,6 +2629,8 @@ var init_mailx_types = __esm({
|
|
|
2544
2629
|
init_contact_rules();
|
|
2545
2630
|
init_contacts_config();
|
|
2546
2631
|
init_groups();
|
|
2632
|
+
init_reminder_state();
|
|
2633
|
+
init_mime_inline_images();
|
|
2547
2634
|
_SEEN = "\\Seen";
|
|
2548
2635
|
_FLAGGED = "\\Flagged";
|
|
2549
2636
|
_ANSWERED = "\\Answered";
|
|
@@ -6107,12 +6194,14 @@ var WebMailxService = class _WebMailxService {
|
|
|
6107
6194
|
const cc = msg.cc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
|
|
6108
6195
|
const bcc = msg.bcc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
|
|
6109
6196
|
const hasHtml = !!msg.bodyHtml;
|
|
6110
|
-
const
|
|
6197
|
+
const domain = account.email.split("@")[1] || "mailx.local";
|
|
6198
|
+
const messageId = `<${Date.now()}.${Math.random().toString(36).slice(2)}@${domain}>`;
|
|
6199
|
+
const extracted = hasHtml ? extractInlineImages(msg.bodyHtml, domain) : { html: "", images: [] };
|
|
6200
|
+
const htmlBody = extracted.html;
|
|
6201
|
+
const inlineImages = extracted.images;
|
|
6111
6202
|
const textBody = msg.bodyText || (hasHtml ? htmlToPlainText(htmlBody) : "");
|
|
6112
6203
|
const htmlEncoded = hasHtml ? encodeQuotedPrintable(htmlBody) : "";
|
|
6113
6204
|
const textEncoded = encodeQuotedPrintable(textBody);
|
|
6114
|
-
const domain = account.email.split("@")[1] || "mailx.local";
|
|
6115
|
-
const messageId = `<${Date.now()}.${Math.random().toString(36).slice(2)}@${domain}>`;
|
|
6116
6205
|
const envelope = [
|
|
6117
6206
|
`From: ${fromHeader}`,
|
|
6118
6207
|
`To: ${to}`,
|
|
@@ -6125,9 +6214,11 @@ var WebMailxService = class _WebMailxService {
|
|
|
6125
6214
|
msg.references?.length ? `References: ${msg.references.join(" ")}` : null,
|
|
6126
6215
|
`MIME-Version: 1.0`
|
|
6127
6216
|
].filter((h) => h !== null);
|
|
6217
|
+
const newBoundary = () => `mailx_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
6218
|
+
const wrap76 = (s) => s.match(/.{1,76}/g)?.join("\r\n") || s;
|
|
6128
6219
|
const makeInner = () => {
|
|
6129
6220
|
if (hasHtml) {
|
|
6130
|
-
const altBoundary =
|
|
6221
|
+
const altBoundary = newBoundary();
|
|
6131
6222
|
const body = `--${altBoundary}\r
|
|
6132
6223
|
Content-Type: text/plain; charset=UTF-8\r
|
|
6133
6224
|
Content-Transfer-Encoding: quoted-printable\r
|
|
@@ -6140,7 +6231,29 @@ Content-Transfer-Encoding: quoted-printable\r
|
|
|
6140
6231
|
${htmlEncoded}\r
|
|
6141
6232
|
--${altBoundary}--\r
|
|
6142
6233
|
`;
|
|
6143
|
-
|
|
6234
|
+
const alt = { headers: [`Content-Type: multipart/alternative; boundary="${altBoundary}"`], body };
|
|
6235
|
+
if (inlineImages.length === 0)
|
|
6236
|
+
return alt;
|
|
6237
|
+
const relBoundary = newBoundary();
|
|
6238
|
+
const relParts = [
|
|
6239
|
+
`--${relBoundary}\r
|
|
6240
|
+
` + alt.headers.join("\r\n") + `\r
|
|
6241
|
+
\r
|
|
6242
|
+
` + alt.body,
|
|
6243
|
+
...inlineImages.map((im) => `--${relBoundary}\r
|
|
6244
|
+
Content-Type: ${im.mime}\r
|
|
6245
|
+
Content-ID: <${im.contentId}>\r
|
|
6246
|
+
Content-Disposition: inline\r
|
|
6247
|
+
Content-Transfer-Encoding: base64\r
|
|
6248
|
+
\r
|
|
6249
|
+
${wrap76(im.base64)}\r
|
|
6250
|
+
`)
|
|
6251
|
+
];
|
|
6252
|
+
return {
|
|
6253
|
+
headers: [`Content-Type: multipart/related; boundary="${relBoundary}"; type="multipart/alternative"`],
|
|
6254
|
+
body: relParts.join("") + `--${relBoundary}--\r
|
|
6255
|
+
`
|
|
6256
|
+
};
|
|
6144
6257
|
}
|
|
6145
6258
|
return {
|
|
6146
6259
|
headers: [`Content-Type: text/plain; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`],
|
|
@@ -6150,7 +6263,6 @@ ${htmlEncoded}\r
|
|
|
6150
6263
|
const atts = Array.isArray(msg.attachments) ? msg.attachments : [];
|
|
6151
6264
|
let rawMessage;
|
|
6152
6265
|
if (atts.length > 0) {
|
|
6153
|
-
const wrap76 = (s) => s.match(/.{1,76}/g)?.join("\r\n") || s;
|
|
6154
6266
|
const mixedBoundary = `mailxmix_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
6155
6267
|
const inner = makeInner();
|
|
6156
6268
|
const parts = [];
|
|
@@ -6640,6 +6752,51 @@ ${bodyEncoded}`;
|
|
|
6640
6752
|
async deleteTask(_uuid) {
|
|
6641
6753
|
return this.notImpl("deleteTask");
|
|
6642
6754
|
}
|
|
6755
|
+
// Reminder dismissed/snoozed state — same reminders.jsonc on the shared
|
|
6756
|
+
// Drive folder the desktop uses, same mailx-types merge. Alarms don't
|
|
6757
|
+
// fire on Android today (getCalendarEvents/getTasks return []), but the
|
|
6758
|
+
// API is live so this device still contributes its state and adopts the
|
|
6759
|
+
// desktops' — the moment calendar lands here, sync just works.
|
|
6760
|
+
reminderStateCache = null;
|
|
6761
|
+
async getReminderState() {
|
|
6762
|
+
if (this.reminderStateCache && Date.now() - this.reminderStateCache.at < 6e4) {
|
|
6763
|
+
return this.reminderStateCache.state;
|
|
6764
|
+
}
|
|
6765
|
+
const now = Date.now();
|
|
6766
|
+
let state = { dismissed: {}, snoozed: {} };
|
|
6767
|
+
try {
|
|
6768
|
+
const json = await cloudRead(REMINDER_STATE_FILE);
|
|
6769
|
+
if (json != null)
|
|
6770
|
+
state = normalizeReminderState(JSON.parse(json), now);
|
|
6771
|
+
} catch {
|
|
6772
|
+
}
|
|
6773
|
+
this.reminderStateCache = { state, at: Date.now() };
|
|
6774
|
+
return state;
|
|
6775
|
+
}
|
|
6776
|
+
async mergeReminderState(patch) {
|
|
6777
|
+
const now = Date.now();
|
|
6778
|
+
const local = normalizeReminderState(patch, now);
|
|
6779
|
+
let cloud = { dismissed: {}, snoozed: {} };
|
|
6780
|
+
let hadCloud = false;
|
|
6781
|
+
try {
|
|
6782
|
+
const json = await cloudRead(REMINDER_STATE_FILE);
|
|
6783
|
+
if (json != null) {
|
|
6784
|
+
cloud = normalizeReminderState(JSON.parse(json), now);
|
|
6785
|
+
hadCloud = true;
|
|
6786
|
+
}
|
|
6787
|
+
} catch {
|
|
6788
|
+
}
|
|
6789
|
+
const merged = mergeReminderStates(cloud, local, now);
|
|
6790
|
+
if (!(hadCloud && reminderStatesEqual(merged, cloud))) {
|
|
6791
|
+
try {
|
|
6792
|
+
await cloudWrite(REMINDER_STATE_FILE, JSON.stringify(merged, null, 2) + "\n");
|
|
6793
|
+
} catch (e) {
|
|
6794
|
+
console.error(`[reminders] cloud write failed: ${e?.message || e}`);
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
this.reminderStateCache = { state: merged, at: Date.now() };
|
|
6798
|
+
return merged;
|
|
6799
|
+
}
|
|
6643
6800
|
// User dictionary (cloud-mirrored) — Android can implement these via
|
|
6644
6801
|
// the same userdict.csv cloud round-trip the desktop uses; until that
|
|
6645
6802
|
// wire-up lands, return safe defaults so spellcheck.ts catch() paths
|
|
@@ -11346,6 +11503,10 @@ function installBridge() {
|
|
|
11346
11503
|
return { ok: true };
|
|
11347
11504
|
},
|
|
11348
11505
|
loadContactsConfig: () => service.loadContactsConfig(),
|
|
11506
|
+
// Cross-device reminder dismissed/snoozed state (reminders.jsonc on
|
|
11507
|
+
// Drive) — alarm poller pushes/pulls through these.
|
|
11508
|
+
getReminderState: () => service.getReminderState(),
|
|
11509
|
+
mergeReminderState: (patch) => service.mergeReminderState(patch),
|
|
11349
11510
|
syncAll: async () => {
|
|
11350
11511
|
await service.syncAll();
|
|
11351
11512
|
return { ok: true };
|