Take the message you were reading into the selection
Ctrl-clicking a second message selected only the second. The first stayed highlighted, because it was the one open -- which is a different state wearing a similar colour -- and was never actually selected. So both looked picked, one was, and every action that followed applied to half of what the screen showed, silently. The cause is visible once the two rules sit together: shift-click took the whole run *including* the row it started from, and ctrl-click took only the row clicked. Two branches of one handler that had drifted apart, with nothing asserting they agreed. So they are one function now, and tested. Ctrl-click brings the current row with it while nothing is selected yet; once there is a selection it toggles exactly one row, which is what it is for. Shift-click is unchanged, and keeps its anchor where it is so extending a range twice grows it from the same place rather than from wherever it last reached. The last test asserts the property that failed rather than the branches: whichever modifier begins a selection, the anchor is in it.
This commit is contained in:
@@ -9,6 +9,7 @@ import { formatListDate } from "@/lib/format";
|
||||
import { canEmpty, confirmAndEmpty, emptyLabel } from "@/lib/emptyFolder";
|
||||
import { displayName, shortName } from "@/lib/address";
|
||||
import { Avatar, Empty, useIsMobile, useIsTouch } from "@/ui/misc";
|
||||
import { rowClick } from "@/lib/listSelection";
|
||||
import { MenuItem, MenuSep, MenuTitle, Popover, useMenu } from "@/ui/popover";
|
||||
import { useCompose } from "@/store/compose";
|
||||
import { useCalendar } from "@/store/calendar";
|
||||
@@ -153,29 +154,23 @@ export function MessageList({ title, list, openThreadId, focusId, setFocusId, on
|
||||
|
||||
const onRowClick = useCallback(
|
||||
(e: MouseEvent, rowId: Id) => {
|
||||
if (e.shiftKey && lastClick.current) {
|
||||
const a = ids.indexOf(lastClick.current);
|
||||
const b = ids.indexOf(rowId);
|
||||
if (a >= 0 && b >= 0) {
|
||||
const [s, en] = a < b ? [a, b] : [b, a];
|
||||
select(ids.slice(s, en + 1), true);
|
||||
window.getSelection()?.removeAllRanges();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
select([rowId], !selected[rowId]);
|
||||
const action = rowClick({
|
||||
rowId, ids, anchor: lastClick.current, selected,
|
||||
modifiers: { shift: e.shiftKey, ctrl: e.ctrlKey || e.metaKey },
|
||||
isMobile,
|
||||
});
|
||||
if (action.kind === "open") {
|
||||
lastClick.current = rowId;
|
||||
onOpen(rowId);
|
||||
return;
|
||||
}
|
||||
lastClick.current = rowId;
|
||||
if (selCount > 0 && isMobile) {
|
||||
select([rowId], !selected[rowId]);
|
||||
return;
|
||||
}
|
||||
onOpen(rowId);
|
||||
select(action.ids, action.on);
|
||||
if (action.moveAnchor) lastClick.current = rowId;
|
||||
// Shift-clicking a list also drags a text selection across it, which
|
||||
// leaves the rows looking smeared blue over the selection they meant.
|
||||
else window.getSelection()?.removeAllRanges();
|
||||
},
|
||||
[ids, select, selected, selCount, isMobile, onOpen],
|
||||
[ids, select, selected, isMobile, onOpen],
|
||||
);
|
||||
|
||||
const onContext = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user