@bobfrankston/rmfmail 1.0.700 → 1.0.702

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.
@@ -634,6 +634,48 @@ var init_message_state = __esm({
634
634
  }
635
635
  });
636
636
 
637
+ // packages/mailx-types/contact-rules.js
638
+ var init_contact_rules = __esm({
639
+ "packages/mailx-types/contact-rules.js"() {
640
+ "use strict";
641
+ }
642
+ });
643
+
644
+ // packages/mailx-types/groups.js
645
+ var init_groups = __esm({
646
+ "packages/mailx-types/groups.js"() {
647
+ "use strict";
648
+ }
649
+ });
650
+
651
+ // packages/mailx-types/index.js
652
+ function _has(msg, flag) {
653
+ return !!msg.flags && msg.flags.includes(flag);
654
+ }
655
+ function _set(msg, flag, state) {
656
+ const present = (msg.flags || []).includes(flag);
657
+ if (state === present)
658
+ return;
659
+ const next = state ? [...msg.flags || [], flag] : (msg.flags || []).filter((f) => f !== flag);
660
+ msg.flags = next;
661
+ }
662
+ var _SEEN, _FLAGGED, _DRAFT, seenOf, flaggedOf, draftOf, setSeen, setFlagged;
663
+ var init_mailx_types = __esm({
664
+ "packages/mailx-types/index.js"() {
665
+ "use strict";
666
+ init_contact_rules();
667
+ init_groups();
668
+ _SEEN = "\\Seen";
669
+ _FLAGGED = "\\Flagged";
670
+ _DRAFT = "\\Draft";
671
+ seenOf = (m) => _has(m, _SEEN);
672
+ flaggedOf = (m) => _has(m, _FLAGGED);
673
+ draftOf = (m) => _has(m, _DRAFT);
674
+ setSeen = (m, state) => _set(m, _SEEN, state);
675
+ setFlagged = (m, state) => _set(m, _FLAGGED, state);
676
+ }
677
+ });
678
+
637
679
  // client/components/message-viewer.js
638
680
  function parsedCacheGet(accountId, uid) {
639
681
  const key = `${accountId}:${uid}`;
@@ -1047,7 +1089,7 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
1047
1089
  parsedCachePut(accountId, uid, msg);
1048
1090
  currentMessage = msg;
1049
1091
  currentAccountId = accountId;
1050
- if (!msg.flags.includes("\\Seen")) {
1092
+ if (!seenOf(msg)) {
1051
1093
  let enabled = true;
1052
1094
  let delaySec = 2;
1053
1095
  try {
@@ -1059,21 +1101,24 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
1059
1101
  }
1060
1102
  if (enabled) {
1061
1103
  const captureGen = gen;
1062
- const newFlags = [...msg.flags, "\\Seen"];
1063
- if (delaySec === 0) {
1064
- updateFlags(accountId, uid, newFlags);
1065
- } else {
1066
- setTimeout(() => {
1067
- if (captureGen !== showMessageGeneration)
1068
- return;
1069
- updateFlags(accountId, uid, newFlags);
1070
- msg.flags = newFlags;
1071
- try {
1072
- updateMessageFlags(accountId, uid, newFlags);
1073
- } catch {
1074
- }
1075
- }, delaySec * 1e3);
1076
- }
1104
+ const apply = () => {
1105
+ if (captureGen !== showMessageGeneration)
1106
+ return;
1107
+ setSeen(msg, true);
1108
+ updateFlags(accountId, uid, msg.flags);
1109
+ try {
1110
+ updateMessageFlags(accountId, uid, msg.flags);
1111
+ } catch {
1112
+ }
1113
+ try {
1114
+ setRowSeen(accountId, uid, true);
1115
+ } catch {
1116
+ }
1117
+ };
1118
+ if (delaySec === 0)
1119
+ apply();
1120
+ else
1121
+ setTimeout(apply, delaySec * 1e3);
1077
1122
  }
1078
1123
  }
1079
1124
  headerEl.hidden = false;
@@ -2021,6 +2066,8 @@ var init_message_viewer = __esm({
2021
2066
  init_api_client();
2022
2067
  init_context_menu();
2023
2068
  init_message_state();
2069
+ init_message_list();
2070
+ init_mailx_types();
2024
2071
  currentMessage = null;
2025
2072
  currentAccountId = "";
2026
2073
  lastAutoOpenedDraftUid = -1;
@@ -2192,6 +2239,7 @@ __export(message_list_exports, {
2192
2239
  removeMessagesAndReconcile: () => removeMessagesAndReconcile,
2193
2240
  scrollFocusedIntoView: () => scrollFocusedIntoView,
2194
2241
  setRowFlagged: () => setRowFlagged,
2242
+ setRowSeen: () => setRowSeen,
2195
2243
  showThreadPopup: () => showThreadPopup
2196
2244
  });
2197
2245
  function cacheKey(mode, a, f, flagged, q) {
@@ -2313,6 +2361,11 @@ function setRowFlagged(accountId, uid, yes) {
2313
2361
  if (row)
2314
2362
  row.setFlaggedClass(yes);
2315
2363
  }
2364
+ function setRowSeen(accountId, uid, yes) {
2365
+ const row = rowByKey.get(rowKey(accountId, uid));
2366
+ if (row)
2367
+ row.setUnreadClass(!yes);
2368
+ }
2316
2369
  function scrollFocusedIntoView() {
2317
2370
  if (focusedRow)
2318
2371
  focusedRow.el.scrollIntoView({ block: "center" });
@@ -2803,7 +2856,7 @@ async function showThreadPopup(pillEl, headMsg) {
2803
2856
  for (const msg of thread) {
2804
2857
  const item = document.createElement("div");
2805
2858
  item.className = "ml-thread-popup-item";
2806
- if (!msg.flags.includes("\\Seen"))
2859
+ if (!seenOf(msg))
2807
2860
  item.classList.add("unread");
2808
2861
  const from = document.createElement("span");
2809
2862
  from.className = "ml-thread-popup-from";
@@ -2908,6 +2961,7 @@ var init_message_list = __esm({
2908
2961
  init_message_viewer();
2909
2962
  init_context_menu();
2910
2963
  init_folder_picker();
2964
+ init_mailx_types();
2911
2965
  currentSpecialUse = "";
2912
2966
  lastClickedRow = null;
2913
2967
  loading = false;
@@ -2970,9 +3024,9 @@ var init_message_list = __esm({
2970
3024
  this.el = row;
2971
3025
  row.className = "ml-row";
2972
3026
  row.draggable = true;
2973
- if (!msg.flags.includes("\\Seen"))
3027
+ if (!seenOf(msg))
2974
3028
  row.classList.add("unread");
2975
- if (msg.flags.includes("\\Flagged"))
3029
+ if (flaggedOf(msg))
2976
3030
  row.classList.add("flagged");
2977
3031
  if (!msg.bodyPath)
2978
3032
  row.classList.add("not-downloaded");
@@ -3001,7 +3055,7 @@ var init_message_list = __esm({
3001
3055
  avatar.addEventListener("contextmenu", (e) => this.onAvatarContextMenu(e));
3002
3056
  const flag = document.createElement("span");
3003
3057
  flag.className = "ml-flag";
3004
- flag.textContent = msg.flags.includes("\\Flagged") ? "\u2605" : "\u2606";
3058
+ flag.textContent = flaggedOf(msg) ? "\u2605" : "\u2606";
3005
3059
  flag.title = "Toggle flag";
3006
3060
  flag.addEventListener("click", (e) => this.onFlagClick(e));
3007
3061
  this.flagEl = flag;
@@ -3097,6 +3151,28 @@ var init_message_list = __esm({
3097
3151
  markBodyCached() {
3098
3152
  this.el.classList.remove("not-downloaded");
3099
3153
  }
3154
+ // Visual-state accessors for flag toggles. Read the *visual* state
3155
+ // (CSS class), not `this.msg.flags`, because the flag array can lag
3156
+ // the rendered class — auto-mark-as-read removes the `unread` class
3157
+ // immediately on click but `\Seen` doesn't land in `msg.flags` until
3158
+ // the auto-mark timer fires (default 2 s). Right-click menus and
3159
+ // keyboard shortcuts ask "what does the user see?" — that's what
3160
+ // these getters answer. Setters keep both visual + flag-array in
3161
+ // sync so the next read by either side agrees.
3162
+ get isSeen() {
3163
+ return !this.el.classList.contains("unread");
3164
+ }
3165
+ set isSeen(yes) {
3166
+ this.setUnreadClass(!yes);
3167
+ setSeen(this.msg, yes);
3168
+ }
3169
+ get isFlagged() {
3170
+ return this.el.classList.contains("flagged");
3171
+ }
3172
+ set isFlagged(yes) {
3173
+ this.setFlaggedClass(yes);
3174
+ setFlagged(this.msg, yes);
3175
+ }
3100
3176
  onAvatarClick(e) {
3101
3177
  e.stopPropagation();
3102
3178
  const body = document.getElementById("ml-body");
@@ -3155,13 +3231,13 @@ var init_message_list = __esm({
3155
3231
  }
3156
3232
  async onFlagClick(e) {
3157
3233
  e.stopPropagation();
3158
- const isFlagged = this.el.classList.contains("flagged");
3159
- const currentFlags = this.msg.flags || [];
3160
- const newFlags = isFlagged ? currentFlags.filter((f) => f !== "\\Flagged") : [...currentFlags, "\\Flagged"];
3234
+ const newFlaggedState = !this.el.classList.contains("flagged");
3235
+ const probe = { flags: [...this.msg.flags || []] };
3236
+ setFlagged(probe, newFlaggedState);
3161
3237
  try {
3162
- await updateFlags(this.accountId, this.msg.uid, newFlags);
3163
- this.msg.flags = newFlags;
3164
- this.setFlaggedClass(!isFlagged);
3238
+ await updateFlags(this.accountId, this.msg.uid, probe.flags);
3239
+ this.msg.flags = probe.flags;
3240
+ this.setFlaggedClass(newFlaggedState);
3165
3241
  } catch {
3166
3242
  }
3167
3243
  }
@@ -3175,10 +3251,8 @@ var init_message_list = __esm({
3175
3251
  const willBeSelected = !this.isSelected;
3176
3252
  this.setSelected(willBeSelected);
3177
3253
  lastClickedRow = this.el;
3178
- if (willBeSelected) {
3254
+ if (willBeSelected)
3179
3255
  focusRow(this);
3180
- this.setUnreadClass(false);
3181
- }
3182
3256
  updateBulkBar();
3183
3257
  return;
3184
3258
  }
@@ -3188,13 +3262,11 @@ var init_message_list = __esm({
3188
3262
  clearSelection();
3189
3263
  selectRange(anchor, this.el);
3190
3264
  lastClickedRow = this.el;
3191
- this.setUnreadClass(false);
3192
3265
  focusRow(this);
3193
3266
  } else {
3194
3267
  clearSelection();
3195
3268
  focusRow(this);
3196
3269
  lastClickedRow = this.el;
3197
- this.setUnreadClass(false);
3198
3270
  }
3199
3271
  } else if (e.ctrlKey || e.metaKey) {
3200
3272
  this.setSelected(!this.isSelected);
@@ -3203,7 +3275,6 @@ var init_message_list = __esm({
3203
3275
  clearSelection();
3204
3276
  focusRow(this);
3205
3277
  lastClickedRow = this.el;
3206
- this.setUnreadClass(false);
3207
3278
  }
3208
3279
  updateBulkBar();
3209
3280
  }
@@ -3289,7 +3360,7 @@ var init_message_list = __esm({
3289
3360
  }
3290
3361
  }
3291
3362
  const isSeen = !this.el.classList.contains("unread");
3292
- const isFlagged = this.msg.flags.includes("\\Flagged");
3363
+ const isFlagged = this.el.classList.contains("flagged");
3293
3364
  const accountId = this.accountId;
3294
3365
  const msg = this.msg;
3295
3366
  const self = this;
@@ -3297,17 +3368,13 @@ var init_message_list = __esm({
3297
3368
  {
3298
3369
  label: isSeen ? "Mark unread" : "Mark read",
3299
3370
  action: async () => {
3300
- const set = new Set(msg.flags);
3301
- if (isSeen)
3302
- set.delete("\\Seen");
3303
- else
3304
- set.add("\\Seen");
3305
- const newFlags = Array.from(set);
3371
+ const probe = { flags: [...msg.flags || []] };
3372
+ setSeen(probe, !isSeen);
3306
3373
  try {
3307
- await updateFlags(accountId, msg.uid, newFlags);
3308
- msg.flags = newFlags;
3309
- updateMessageFlags(accountId, msg.uid, newFlags);
3310
- self.setUnreadClass(!newFlags.includes("\\Seen"));
3374
+ await updateFlags(accountId, msg.uid, probe.flags);
3375
+ msg.flags = probe.flags;
3376
+ updateMessageFlags(accountId, msg.uid, probe.flags);
3377
+ self.setUnreadClass(isSeen);
3311
3378
  } catch {
3312
3379
  }
3313
3380
  }
@@ -3315,11 +3382,12 @@ var init_message_list = __esm({
3315
3382
  {
3316
3383
  label: isFlagged ? "Unflag" : "Flag",
3317
3384
  action: async () => {
3318
- const newFlags = isFlagged ? msg.flags.filter((f) => f !== "\\Flagged") : [...msg.flags, "\\Flagged"];
3385
+ const probe = { flags: [...msg.flags || []] };
3386
+ setFlagged(probe, !isFlagged);
3319
3387
  try {
3320
- await updateFlags(accountId, msg.uid, newFlags);
3321
- msg.flags = newFlags;
3322
- self.setFlaggedClass(newFlags.includes("\\Flagged"));
3388
+ await updateFlags(accountId, msg.uid, probe.flags);
3389
+ msg.flags = probe.flags;
3390
+ self.setFlaggedClass(!isFlagged);
3323
3391
  } catch {
3324
3392
  }
3325
3393
  }
@@ -5805,6 +5873,7 @@ async function updateFolderCounts() {
5805
5873
 
5806
5874
  // client/app.ts
5807
5875
  init_message_list();
5876
+ init_mailx_types();
5808
5877
  init_message_viewer();
5809
5878
  init_api_client();
5810
5879
  init_message_state();
@@ -6711,14 +6780,14 @@ document.getElementById("btn-tb-spam")?.addEventListener("click", spamSelectedMe
6711
6780
  document.getElementById("btn-flag")?.addEventListener("click", async () => {
6712
6781
  const sel = getCurrentFocused();
6713
6782
  if (!sel) return;
6714
- const isFlagged = sel.flags.includes("\\Flagged");
6715
- const newFlags = isFlagged ? sel.flags.filter((f) => f !== "\\Flagged") : [...sel.flags, "\\Flagged"];
6783
+ const wasFlagged = flaggedOf(sel);
6784
+ setFlagged(sel, !wasFlagged);
6716
6785
  try {
6717
- await updateFlags(sel.accountId, sel.uid, newFlags);
6718
- sel.flags = newFlags;
6719
- updateMessageFlags(sel.accountId, sel.uid, newFlags);
6720
- setRowFlagged(sel.accountId, sel.uid, newFlags.includes("\\Flagged"));
6786
+ await updateFlags(sel.accountId, sel.uid, sel.flags);
6787
+ updateMessageFlags(sel.accountId, sel.uid, sel.flags);
6788
+ setRowFlagged(sel.accountId, sel.uid, !wasFlagged);
6721
6789
  } catch (e) {
6790
+ setFlagged(sel, wasFlagged);
6722
6791
  console.error(`Flag toggle failed: ${e.message}`);
6723
6792
  }
6724
6793
  });
@@ -6807,14 +6876,14 @@ document.getElementById("btn-compose")?.addEventListener("click", () => openComp
6807
6876
  document.getElementById("btn-mark-unread")?.addEventListener("click", () => {
6808
6877
  const sel = getCurrentFocused();
6809
6878
  if (!sel) return;
6810
- const isSeen = sel.flags.includes("\\Seen");
6811
- const newFlags = isSeen ? sel.flags.filter((f) => f !== "\\Seen") : [...sel.flags, "\\Seen"];
6812
- updateFlags(sel.accountId, sel.uid, newFlags).then(() => {
6813
- sel.flags = newFlags;
6814
- updateMessageFlags(sel.accountId, sel.uid, newFlags);
6879
+ const wasSeen = seenOf(sel);
6880
+ setSeen(sel, !wasSeen);
6881
+ updateFlags(sel.accountId, sel.uid, sel.flags).then(() => {
6882
+ updateMessageFlags(sel.accountId, sel.uid, sel.flags);
6815
6883
  const row = document.querySelector(`.ml-row[data-uid="${sel.uid}"][data-account-id="${sel.accountId}"]`);
6816
- if (row) row.classList.toggle("unread", !newFlags.includes("\\Seen"));
6884
+ if (row) row.classList.toggle("unread", wasSeen);
6817
6885
  }).catch(() => {
6886
+ setSeen(sel, wasSeen);
6818
6887
  });
6819
6888
  });
6820
6889
  document.getElementById("btn-reply")?.addEventListener("click", () => openCompose("reply"));
@@ -7828,14 +7897,14 @@ document.addEventListener("keydown", (e) => {
7828
7897
  const sel = getCurrentFocused();
7829
7898
  if (!sel) return;
7830
7899
  e.preventDefault();
7831
- const isSeen = sel.flags.includes("\\Seen");
7832
- const newFlags = isSeen ? sel.flags.filter((f) => f !== "\\Seen") : [...sel.flags, "\\Seen"];
7833
- updateFlags(sel.accountId, sel.uid, newFlags).then(() => {
7834
- sel.flags = newFlags;
7835
- updateMessageFlags(sel.accountId, sel.uid, newFlags);
7900
+ const wasSeen = seenOf(sel);
7901
+ setSeen(sel, !wasSeen);
7902
+ updateFlags(sel.accountId, sel.uid, sel.flags).then(() => {
7903
+ updateMessageFlags(sel.accountId, sel.uid, sel.flags);
7836
7904
  const row = document.querySelector(`.ml-row[data-uid="${sel.uid}"][data-account-id="${sel.accountId}"]`);
7837
- if (row) row.classList.toggle("unread", !newFlags.includes("\\Seen"));
7905
+ if (row) row.classList.toggle("unread", wasSeen);
7838
7906
  }).catch(() => {
7907
+ setSeen(sel, wasSeen);
7839
7908
  });
7840
7909
  }
7841
7910
  if (e.key.toLowerCase() === "z" && !e.ctrlKey && !e.metaKey && !e.altKey) {
@@ -9173,7 +9242,7 @@ document.addEventListener("mailx-popout-message", (async (e) => {
9173
9242
  alert(`Couldn't load message: ${err?.message || err}`);
9174
9243
  return;
9175
9244
  }
9176
- const isDraft = Array.isArray(msg?.flags) && msg.flags.includes("\\Draft");
9245
+ const isDraft = !!msg && draftOf(msg);
9177
9246
  if (isDraft) {
9178
9247
  const accts = await getAccounts();
9179
9248
  const init = {