@bobfrankston/rmfmail 1.1.249 → 1.1.251

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.
@@ -52,25 +52,40 @@ export function getMessages() {
52
52
  * backward when the deletion is at the bottom.
53
53
  */
54
54
  export function removeMessages(uids, currentlyFocused) {
55
- const removeSet = new Set(uids.map(u => `${u.accountId}:${u.uid}`));
56
- const focusedKey = currentlyFocused ? `${currentlyFocused.accountId}:${currentlyFocused.uid}` : null;
57
- const focusedWasRemoved = focusedKey !== null && removeSet.has(focusedKey);
55
+ // Key on (account, FOLDER, uid) when a folderId is supplied. IMAP UIDs are
56
+ // per-folder, so in a mixed view (e.g. a search spanning INBOX + Trash) a
57
+ // Trash row can share a uid with a SURVIVING INBOX message — keying on
58
+ // (account, uid) alone then kept the deleted Trash row visible (Bob
59
+ // 2026-06-12: "the trash ones did not disappear"). Legacy callers that
60
+ // don't pass folderId fall back to the loose (account, uid) match — those
61
+ // are single-folder contexts with no collision risk.
62
+ const fullSet = new Set();
63
+ const looseSet = new Set();
64
+ for (const u of uids) {
65
+ if (u.folderId != null)
66
+ fullSet.add(`${u.accountId}:${u.folderId}:${u.uid}`);
67
+ else
68
+ looseSet.add(`${u.accountId}:${u.uid}`);
69
+ }
70
+ const isRemoved = (m) => (m.folderId != null && fullSet.has(`${m.accountId}:${m.folderId}:${m.uid}`)) ||
71
+ looseSet.has(`${m.accountId}:${m.uid}`);
72
+ const focusedWasRemoved = currentlyFocused !== null && isRemoved(currentlyFocused);
58
73
  let nextSurvivor = null;
59
74
  if (focusedWasRemoved) {
60
75
  let lastRemovedIdx = -1;
61
76
  for (let i = 0; i < messages.length; i++) {
62
- if (removeSet.has(`${messages[i].accountId}:${messages[i].uid}`))
77
+ if (isRemoved(messages[i]))
63
78
  lastRemovedIdx = i;
64
79
  }
65
80
  for (let i = lastRemovedIdx + 1; i < messages.length; i++) {
66
- if (!removeSet.has(`${messages[i].accountId}:${messages[i].uid}`)) {
81
+ if (!isRemoved(messages[i])) {
67
82
  nextSurvivor = messages[i];
68
83
  break;
69
84
  }
70
85
  }
71
86
  if (!nextSurvivor) {
72
87
  for (let i = lastRemovedIdx - 1; i >= 0; i--) {
73
- if (!removeSet.has(`${messages[i].accountId}:${messages[i].uid}`)) {
88
+ if (!isRemoved(messages[i])) {
74
89
  nextSurvivor = messages[i];
75
90
  break;
76
91
  }
@@ -83,11 +98,11 @@ export function removeMessages(uids, currentlyFocused) {
83
98
  // messageId matches a removed one.
84
99
  const removedIds = new Set();
85
100
  for (const m of messages) {
86
- if (removeSet.has(`${m.accountId}:${m.uid}`) && m.messageId) {
101
+ if (isRemoved(m) && m.messageId) {
87
102
  removedIds.add(m.messageId);
88
103
  }
89
104
  }
90
- messages = messages.filter(m => !removeSet.has(`${m.accountId}:${m.uid}`));
105
+ messages = messages.filter(m => !isRemoved(m));
91
106
  if (removedIds.size > 0) {
92
107
  for (const m of messages) {
93
108
  if (m.messageId && removedIds.has(m.messageId) && typeof m.dupeCount === "number") {
@@ -1 +1 @@
1
- {"version":3,"file":"message-state.js","sourceRoot":"","sources":["message-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-state.js","sourceRoot":"","sources":["message-state.ts"],"names":[],"mappings":"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"}
@@ -88,28 +88,42 @@ export interface RemovalOutcome {
88
88
  * backward when the deletion is at the bottom.
89
89
  */
90
90
  export function removeMessages(
91
- uids: { accountId: string; uid: number }[],
92
- currentlyFocused: { accountId: string; uid: number } | null,
91
+ uids: { accountId: string; uid: number; folderId?: number }[],
92
+ currentlyFocused: { accountId: string; uid: number; folderId?: number } | null,
93
93
  ): RemovalOutcome {
94
- const removeSet = new Set(uids.map(u => `${u.accountId}:${u.uid}`));
95
- const focusedKey = currentlyFocused ? `${currentlyFocused.accountId}:${currentlyFocused.uid}` : null;
96
- const focusedWasRemoved = focusedKey !== null && removeSet.has(focusedKey);
94
+ // Key on (account, FOLDER, uid) when a folderId is supplied. IMAP UIDs are
95
+ // per-folder, so in a mixed view (e.g. a search spanning INBOX + Trash) a
96
+ // Trash row can share a uid with a SURVIVING INBOX message — keying on
97
+ // (account, uid) alone then kept the deleted Trash row visible (Bob
98
+ // 2026-06-12: "the trash ones did not disappear"). Legacy callers that
99
+ // don't pass folderId fall back to the loose (account, uid) match — those
100
+ // are single-folder contexts with no collision risk.
101
+ const fullSet = new Set<string>();
102
+ const looseSet = new Set<string>();
103
+ for (const u of uids) {
104
+ if (u.folderId != null) fullSet.add(`${u.accountId}:${u.folderId}:${u.uid}`);
105
+ else looseSet.add(`${u.accountId}:${u.uid}`);
106
+ }
107
+ const isRemoved = (m: { accountId: string; uid: number; folderId?: number }): boolean =>
108
+ (m.folderId != null && fullSet.has(`${m.accountId}:${m.folderId}:${m.uid}`)) ||
109
+ looseSet.has(`${m.accountId}:${m.uid}`);
110
+ const focusedWasRemoved = currentlyFocused !== null && isRemoved(currentlyFocused);
97
111
 
98
112
  let nextSurvivor: ListMessage | null = null;
99
113
  if (focusedWasRemoved) {
100
114
  let lastRemovedIdx = -1;
101
115
  for (let i = 0; i < messages.length; i++) {
102
- if (removeSet.has(`${messages[i].accountId}:${messages[i].uid}`)) lastRemovedIdx = i;
116
+ if (isRemoved(messages[i])) lastRemovedIdx = i;
103
117
  }
104
118
  for (let i = lastRemovedIdx + 1; i < messages.length; i++) {
105
- if (!removeSet.has(`${messages[i].accountId}:${messages[i].uid}`)) {
119
+ if (!isRemoved(messages[i])) {
106
120
  nextSurvivor = messages[i];
107
121
  break;
108
122
  }
109
123
  }
110
124
  if (!nextSurvivor) {
111
125
  for (let i = lastRemovedIdx - 1; i >= 0; i--) {
112
- if (!removeSet.has(`${messages[i].accountId}:${messages[i].uid}`)) {
126
+ if (!isRemoved(messages[i])) {
113
127
  nextSurvivor = messages[i];
114
128
  break;
115
129
  }
@@ -123,11 +137,11 @@ export function removeMessages(
123
137
  // messageId matches a removed one.
124
138
  const removedIds = new Set<string>();
125
139
  for (const m of messages) {
126
- if (removeSet.has(`${m.accountId}:${m.uid}`) && m.messageId) {
140
+ if (isRemoved(m) && m.messageId) {
127
141
  removedIds.add(m.messageId);
128
142
  }
129
143
  }
130
- messages = messages.filter(m => !removeSet.has(`${m.accountId}:${m.uid}`));
144
+ messages = messages.filter(m => !isRemoved(m));
131
145
  if (removedIds.size > 0) {
132
146
  for (const m of messages) {
133
147
  if (m.messageId && removedIds.has(m.messageId) && typeof (m as any).dupeCount === "number") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/rmfmail",
3
- "version": "1.1.249",
3
+ "version": "1.1.251",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -38,7 +38,7 @@
38
38
  "dependencies": {
39
39
  "@bobfrankston/iflow-direct": "^0.1.53",
40
40
  "@bobfrankston/mailx-host": "^0.1.13",
41
- "@bobfrankston/mailx-imap": "^0.1.90",
41
+ "@bobfrankston/mailx-imap": "^0.1.91",
42
42
  "@bobfrankston/mailx-store-web": "^0.1.27",
43
43
  "@bobfrankston/mailx-sync": "^0.1.22",
44
44
  "@bobfrankston/miscinfo": "^1.0.13",
@@ -118,7 +118,7 @@
118
118
  "dependencies": {
119
119
  "@bobfrankston/iflow-direct": "^0.1.53",
120
120
  "@bobfrankston/mailx-host": "^0.1.13",
121
- "@bobfrankston/mailx-imap": "^0.1.90",
121
+ "@bobfrankston/mailx-imap": "^0.1.91",
122
122
  "@bobfrankston/mailx-store-web": "^0.1.27",
123
123
  "@bobfrankston/mailx-sync": "^0.1.22",
124
124
  "@bobfrankston/miscinfo": "^1.0.13",