@bobfrankston/rmfmail 1.2.303 → 1.2.305
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/.commitmsg +34 -41
- package/client/app.bundle.js +57 -7
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +41 -6
- package/client/app.js.map +1 -1
- package/client/app.ts +38 -6
- package/client/components/calendar-sidebar.js +34 -5
- package/client/components/calendar-sidebar.js.map +1 -1
- package/client/components/calendar-sidebar.ts +35 -5
- package/client/components/context-menu.js +49 -1
- package/client/components/context-menu.js.map +1 -1
- package/client/components/context-menu.ts +53 -1
- package/client/compose/compose.bundle.js +31 -1
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/styles/components.css +5 -0
- package/npmchanges.md +102 -0
- package/package.json +5 -5
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-42308 → node_modules.npmglobalize-stash-83696}/.package-lock.json +0 -0
package/.commitmsg
CHANGED
|
@@ -1,47 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
One click on a task disabled Delete for the whole app
|
|
2
2
|
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
INBOX alone, and the desktop's own SQL finds every one of them.
|
|
3
|
+
"I press del and the message is not deleted whether I'm in the letter or
|
|
4
|
+
summary and trashcan is not working" (Bob 2026-09-05).
|
|
6
5
|
|
|
7
|
-
`
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
`deleteSelection()` — the one route for the Delete key, Ctrl+D, and both trash
|
|
7
|
+
buttons — gave tasks an unconditional priority: if `getSelectedTaskUuids()` was
|
|
8
|
+
non-empty it deleted tasks and returned, whatever the user was actually looking
|
|
9
|
+
at. A task selection is set by a single click on a task row in the sidebar, is
|
|
10
|
+
never cleared by anything done in the mail panes, and has no indicator outside
|
|
11
|
+
the sidebar. So one stray click on a task turned off Delete and both trash
|
|
12
|
+
buttons for messages, indefinitely, and the only symptom was nothing happening.
|
|
13
|
+
Invisible modal state deciding what a destructive gesture means — the same
|
|
14
|
+
fault as a filter that reads as a selection.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
newest 50 messages happen to carry no star shows nothing at all, and every
|
|
19
|
-
starred message deeper in the mailbox is unreachable. The overlay then counted
|
|
20
|
-
the rows it was hiding and reported the truth from where it stood — "A 'flagged
|
|
21
|
-
only' filter is hiding 50 messages" — while the filter that could have found
|
|
22
|
-
the other 260 had never run.
|
|
16
|
+
The daemon log shows it exactly: at the moment of the attempt there is a
|
|
17
|
+
getMessage and an updateFlags and no trashMessages at all. Nothing was slow or
|
|
18
|
+
failing; the gesture never left the client.
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
Routing now follows the pane the user last acted in, tracked on capture-phase
|
|
21
|
+
pointerdown so it lands before any handler that stops propagation and before
|
|
22
|
+
focus moves. The toolbar buttons sit in neither pane, so clicking one leaves
|
|
23
|
+
that reading as the user's last real interaction set it — which is the point,
|
|
24
|
+
since the button is how you act on what you were just doing. Select tasks and
|
|
25
|
+
press Delete and the tasks still go, which is what the priority rule was
|
|
26
|
+
written for; the part that was never intended is gone. A delete routed to
|
|
27
|
+
messages also clears any leftover task selection, so the sidebar stops showing
|
|
28
|
+
rows as chosen after the gesture has moved on.
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
I made this worse yesterday and should say so. Before v1.2.304 the swallowed
|
|
31
|
+
delete at least put up the native confirm — misplaced and late, but visible, so
|
|
32
|
+
"my delete went to the tasks" was discoverable. Moving that question to a menu
|
|
33
|
+
anchored over the task pane made the misrouting completely silent whenever the
|
|
34
|
+
sidebar was collapsed or scrolled away. The routing bug was already there; my
|
|
35
|
+
change removed the last evidence of it.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
click to show everything" came out one word per line down the left edge of the
|
|
42
|
-
header. `width: 100%` cannot help there; inside a grid it means 100% of a 28px
|
|
43
|
-
track.
|
|
44
|
-
|
|
45
|
-
Not changed: the ★ button sits in the search row, next to Server and
|
|
46
|
-
Trash/Spam, which is why reaching for search found a list filter. That is a
|
|
47
|
-
placement judgement, not a bug, so it is yours to call.
|
|
37
|
+
Also guarded, from the same cause: the batch confirm measured its anchor with
|
|
38
|
+
getBoundingClientRect on a possibly-hidden sidebar, which is 0x0 at 0,0 — it
|
|
39
|
+
now falls back to the middle of the window rather than asking in a corner
|
|
40
|
+
nobody is looking at.
|
package/client/app.bundle.js
CHANGED
|
@@ -1015,6 +1015,7 @@ var init_edit_menu = __esm({
|
|
|
1015
1015
|
var context_menu_exports = {};
|
|
1016
1016
|
__export(context_menu_exports, {
|
|
1017
1017
|
closeContextMenu: () => closeContextMenu,
|
|
1018
|
+
confirmAt: () => confirmAt,
|
|
1018
1019
|
showContextMenu: () => showContextMenu
|
|
1019
1020
|
});
|
|
1020
1021
|
function closeSubmenu() {
|
|
@@ -1029,6 +1030,11 @@ function closeContextMenu() {
|
|
|
1029
1030
|
activeMenu.remove();
|
|
1030
1031
|
activeMenu = null;
|
|
1031
1032
|
}
|
|
1033
|
+
if (closeHandler) {
|
|
1034
|
+
const fn = closeHandler;
|
|
1035
|
+
closeHandler = null;
|
|
1036
|
+
fn();
|
|
1037
|
+
}
|
|
1032
1038
|
if (dismissListener) {
|
|
1033
1039
|
document.removeEventListener("pointerdown", dismissListener, true);
|
|
1034
1040
|
dismissListener = null;
|
|
@@ -1079,6 +1085,7 @@ function openSubmenu(parentRow, items) {
|
|
|
1079
1085
|
}
|
|
1080
1086
|
function showContextMenu(x, y, items, opts = {}) {
|
|
1081
1087
|
closeContextMenu();
|
|
1088
|
+
closeHandler = opts.onClose ?? null;
|
|
1082
1089
|
if (opts.editTarget !== void 0 || opts.editEngine) {
|
|
1083
1090
|
const edit = editMenuItems({ target: opts.editTarget, engine: opts.editEngine }, { onError: opts.onEditError });
|
|
1084
1091
|
if (edit.length > 0) {
|
|
@@ -1168,7 +1175,29 @@ function showContextMenu(x, y, items, opts = {}) {
|
|
|
1168
1175
|
document.addEventListener("keydown", escapeListener, true);
|
|
1169
1176
|
});
|
|
1170
1177
|
}
|
|
1171
|
-
|
|
1178
|
+
function confirmAt(x, y, confirmLabel, opts = {}) {
|
|
1179
|
+
return new Promise((resolve) => {
|
|
1180
|
+
let settled = false;
|
|
1181
|
+
const done = (v) => {
|
|
1182
|
+
if (!settled) {
|
|
1183
|
+
settled = true;
|
|
1184
|
+
resolve(v);
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
showContextMenu(x, y, [
|
|
1188
|
+
{ label: confirmLabel, action: () => done(true), emphasized: true, tooltip: opts.tooltip },
|
|
1189
|
+
{ label: "Cancel", action: () => done(false) }
|
|
1190
|
+
], {
|
|
1191
|
+
// An item's click calls closeContextMenu() BEFORE item.action(), so
|
|
1192
|
+
// onClose always runs first. Defer the "cancelled" answer by one
|
|
1193
|
+
// microtask: the action is synchronous and finishes within this
|
|
1194
|
+
// task, so by the time the microtask drains, a chosen item has
|
|
1195
|
+
// already settled the promise and this call is a no-op.
|
|
1196
|
+
onClose: () => queueMicrotask(() => done(false))
|
|
1197
|
+
});
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
var activeMenu, dismissListener, escapeListener, activeSubmenu, closeHandler;
|
|
1172
1201
|
var init_context_menu = __esm({
|
|
1173
1202
|
"client/components/context-menu.js"() {
|
|
1174
1203
|
"use strict";
|
|
@@ -1177,6 +1206,7 @@ var init_context_menu = __esm({
|
|
|
1177
1206
|
dismissListener = null;
|
|
1178
1207
|
escapeListener = null;
|
|
1179
1208
|
activeSubmenu = null;
|
|
1209
|
+
closeHandler = null;
|
|
1180
1210
|
document.addEventListener("scroll", closeContextMenu, true);
|
|
1181
1211
|
document.addEventListener("contextmenu", () => {
|
|
1182
1212
|
});
|
|
@@ -6512,6 +6542,7 @@ var init_outbox_view = __esm({
|
|
|
6512
6542
|
// client/components/calendar-sidebar.js
|
|
6513
6543
|
var calendar_sidebar_exports = {};
|
|
6514
6544
|
__export(calendar_sidebar_exports, {
|
|
6545
|
+
clearTaskSelection: () => clearTaskSelection,
|
|
6515
6546
|
deleteSelectedTasks: () => deleteSelectedTasks,
|
|
6516
6547
|
getSelectedTaskUuids: () => getSelectedTaskUuids,
|
|
6517
6548
|
hideCalendarSidebar: () => hideCalendarSidebar,
|
|
@@ -6619,6 +6650,12 @@ async function renderCalendarList() {
|
|
|
6619
6650
|
if (lastEvents.length > 0)
|
|
6620
6651
|
renderEvents(lastEvents);
|
|
6621
6652
|
}
|
|
6653
|
+
function clearTaskSelection() {
|
|
6654
|
+
if (selectedTaskUuids.size === 0)
|
|
6655
|
+
return;
|
|
6656
|
+
selectedTaskUuids.clear();
|
|
6657
|
+
void renderTasks();
|
|
6658
|
+
}
|
|
6622
6659
|
function getSelectedTaskUuids() {
|
|
6623
6660
|
return [...selectedTaskUuids];
|
|
6624
6661
|
}
|
|
@@ -6626,7 +6663,11 @@ async function deleteSelectedTasks() {
|
|
|
6626
6663
|
const uuids = [...selectedTaskUuids];
|
|
6627
6664
|
if (uuids.length === 0)
|
|
6628
6665
|
return;
|
|
6629
|
-
|
|
6666
|
+
const anchorEl = document.querySelector(".cal-side-task.selected") || document.getElementById("cal-side-tasks");
|
|
6667
|
+
const rect = anchorEl?.getBoundingClientRect();
|
|
6668
|
+
const r = rect && rect.width > 0 && rect.height > 0 ? rect : { left: window.innerWidth / 2 - 60, bottom: window.innerHeight / 3 };
|
|
6669
|
+
const label = uuids.length === 1 ? `Delete "${document.querySelector(".cal-side-task.selected .cal-side-task-title")?.textContent || "this task"}"` : `Delete ${uuids.length} tasks`;
|
|
6670
|
+
if (!await confirmAt(r.left + 24, r.bottom, label, { tooltip: TASK_DELETE_NOTE }))
|
|
6630
6671
|
return;
|
|
6631
6672
|
selectedTaskUuids.clear();
|
|
6632
6673
|
for (const uuid of uuids) {
|
|
@@ -6914,7 +6955,7 @@ async function renderTasks(prefetched) {
|
|
|
6914
6955
|
row.querySelector(".cal-side-task-delete")?.addEventListener("click", async (e) => {
|
|
6915
6956
|
e.stopPropagation();
|
|
6916
6957
|
const title = tasks.find((t) => t.uuid === uuid)?.title || "this task";
|
|
6917
|
-
if (!
|
|
6958
|
+
if (!await confirmAt(e.clientX, e.clientY, `Delete "${title}"`, { tooltip: TASK_DELETE_NOTE }))
|
|
6918
6959
|
return;
|
|
6919
6960
|
await deleteTask(uuid);
|
|
6920
6961
|
selectedTaskUuids.delete(uuid);
|
|
@@ -6941,9 +6982,9 @@ async function renderTasks(prefetched) {
|
|
|
6941
6982
|
{ label: "View in Google Tasks", action: () => openInBrowser("https://tasks.google.com/") },
|
|
6942
6983
|
{ label: "", action: () => {
|
|
6943
6984
|
}, separator: true },
|
|
6944
|
-
{ label: "Delete task", action: async () => {
|
|
6985
|
+
{ label: "Delete task", tooltip: TASK_DELETE_NOTE, action: async () => {
|
|
6945
6986
|
const title = tasks.find((t) => t.uuid === uuid)?.title || "this task";
|
|
6946
|
-
if (!
|
|
6987
|
+
if (!await confirmAt(e.clientX, e.clientY, `Delete "${title}"`, { tooltip: TASK_DELETE_NOTE }))
|
|
6947
6988
|
return;
|
|
6948
6989
|
await deleteTask(uuid);
|
|
6949
6990
|
renderTasks();
|
|
@@ -7296,7 +7337,7 @@ function initCalendarSidebar() {
|
|
|
7296
7337
|
}
|
|
7297
7338
|
}
|
|
7298
7339
|
}
|
|
7299
|
-
var SIDEBAR_PREF, SHOW_RECURRING_PREF, SHOW_DONE_PREF, HORIZON_DAYS_PREF, HORIZON_DEFAULT_DAYS, HOLIDAY_HORIZON_MS, viewYear, viewMonth, viewDay, lastEvents, lastOverdueTasks, calById, calendarList, hiddenCalendars, selectedTaskUuids;
|
|
7340
|
+
var SIDEBAR_PREF, SHOW_RECURRING_PREF, SHOW_DONE_PREF, HORIZON_DAYS_PREF, HORIZON_DEFAULT_DAYS, HOLIDAY_HORIZON_MS, viewYear, viewMonth, viewDay, lastEvents, lastOverdueTasks, calById, calendarList, hiddenCalendars, selectedTaskUuids, TASK_DELETE_NOTE;
|
|
7300
7341
|
var init_calendar_sidebar = __esm({
|
|
7301
7342
|
"client/components/calendar-sidebar.js"() {
|
|
7302
7343
|
"use strict";
|
|
@@ -7317,6 +7358,7 @@ var init_calendar_sidebar = __esm({
|
|
|
7317
7358
|
calendarList = [];
|
|
7318
7359
|
hiddenCalendars = /* @__PURE__ */ new Set();
|
|
7319
7360
|
selectedTaskUuids = /* @__PURE__ */ new Set();
|
|
7361
|
+
TASK_DELETE_NOTE = "Removes it from Google Tasks. To mark it done instead, use the checkbox.";
|
|
7320
7362
|
}
|
|
7321
7363
|
});
|
|
7322
7364
|
|
|
@@ -10554,13 +10596,21 @@ function popUndo() {
|
|
|
10554
10596
|
}
|
|
10555
10597
|
return null;
|
|
10556
10598
|
}
|
|
10599
|
+
var lastActionPane = "messages";
|
|
10600
|
+
document.addEventListener("pointerdown", (e) => {
|
|
10601
|
+
const t = e.target;
|
|
10602
|
+
if (!t?.closest) return;
|
|
10603
|
+
if (t.closest("#cal-side-tasks")) lastActionPane = "tasks";
|
|
10604
|
+
else if (t.closest(".message-list, .message-viewer, #ml-body, #message-viewer")) lastActionPane = "messages";
|
|
10605
|
+
}, true);
|
|
10557
10606
|
async function deleteSelection() {
|
|
10558
10607
|
try {
|
|
10559
10608
|
const sidebar = await Promise.resolve().then(() => (init_calendar_sidebar(), calendar_sidebar_exports));
|
|
10560
|
-
if (sidebar.getSelectedTaskUuids().length > 0) {
|
|
10609
|
+
if (lastActionPane === "tasks" && sidebar.getSelectedTaskUuids().length > 0) {
|
|
10561
10610
|
await sidebar.deleteSelectedTasks();
|
|
10562
10611
|
return;
|
|
10563
10612
|
}
|
|
10613
|
+
if (sidebar.getSelectedTaskUuids().length > 0) sidebar.clearTaskSelection();
|
|
10564
10614
|
} catch {
|
|
10565
10615
|
}
|
|
10566
10616
|
await deleteSelectedMessages();
|