Skip to content

Commit f8dcfbe

Browse files
committed
implement select message with mouse drag
1 parent c42b9f4 commit f8dcfbe

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

‎src/components/middle/message/Message.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,11 +544,13 @@ const Message: FC<OwnProps & StateProps> = ({
544544

545545
const {
546546
handleMouseDown,
547+
handleMouseUp,
547548
handleClick,
548549
handleContextMenu,
549550
handleDoubleClick,
550551
handleContentDoubleClick,
551552
handleMouseMove,
553+
handleMouseOver,
552554
handleSendQuickReaction,
553555
handleMouseLeave,
554556
isSwiped,
@@ -568,6 +570,7 @@ const Message: FC<OwnProps & StateProps> = ({
568570
quickReactionRef,
569571
isInDocumentGroupNotLast,
570572
getIsMessageListReady,
573+
isSelected,
571574
);
572575

573576
const {
@@ -1353,7 +1356,9 @@ const Message: FC<OwnProps & StateProps> = ({
13531356
className={containerClassName}
13541357
data-message-id={messageId}
13551358
onCopy={isProtected ? stopEvent : undefined}
1359+
onMouseOver={handleMouseOver}
13561360
onMouseDown={handleMouseDown}
1361+
onMouseUp={handleMouseUp}
13571362
onClick={handleClick}
13581363
onContextMenu={handleContextMenu}
13591364
onDoubleClick={handleDoubleClick}

‎src/components/middle/message/hooks/useOuterHandlers.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const QUICK_REACTION_DOUBLE_TAP_DELAY = 200;
2222
const QUICK_REACTION_AREA_WIDTH = 3 * REM;
2323
const QUICK_REACTION_AREA_HEIGHT = Number(REM);
2424
const GROUP_MESSAGE_HOVER_ATTRIBUTE = 'data-is-document-group-hover';
25+
const MESSAGE_ITEM_CLASS = 'message-list-item';
26+
const LONG_CLICK_SELECTION_TIMEOUT = 400;
2527

2628
export default function useOuterHandlers(
2729
selectMessage: (e?: React.MouseEvent<HTMLDivElement, MouseEvent>, groupedId?: string) => void,
@@ -37,18 +39,58 @@ export default function useOuterHandlers(
3739
quickReactionRef: RefObject<HTMLDivElement>,
3840
shouldHandleMouseLeave: boolean,
3941
getIsMessageListReady: Signal<boolean>,
42+
isSelected: boolean | undefined,
4043
) {
4144
const { updateDraftReplyInfo, sendDefaultReaction } = getActions();
4245

4346
const [isQuickReactionVisible, markQuickReactionVisible, unmarkQuickReactionVisible] = useFlag();
4447
const [isSwiped, markSwiped, unmarkSwiped] = useFlag();
4548
const doubleTapTimeoutRef = useRef<NodeJS.Timeout>();
49+
const longClickTimeoutRef = useRef<NodeJS.Timeout>();
50+
const isSelectedRecently = useRef<boolean>();
51+
const isLockMouseOverSelection = useRef<boolean>();
4652

4753
function handleMouseDown(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
4854
preventMessageInputBlur(e);
4955
handleBeforeContextMenu(e);
56+
57+
if (e.target instanceof HTMLElement) {
58+
const isOuter = e.target.classList?.contains(MESSAGE_ITEM_CLASS);
59+
if (isOuter) {
60+
const timeoutId: undefined | ReturnType<typeof setTimeout> = setTimeout(() => {
61+
isLockMouseOverSelection.current = true;
62+
selectMessage(e);
63+
setTimeout(() => {
64+
isLockMouseOverSelection.current = false;
65+
}, 50);
66+
isSelectedRecently.current = true;
67+
}, isSelected ? 100 : LONG_CLICK_SELECTION_TIMEOUT);
68+
longClickTimeoutRef.current = timeoutId;
69+
}
70+
}
5071
}
5172

73+
const handleMouseUp = () => {
74+
if (longClickTimeoutRef.current) {
75+
clearTimeout(longClickTimeoutRef.current);
76+
}
77+
setTimeout(() => {
78+
isSelectedRecently.current = false;
79+
}, 50);
80+
};
81+
82+
const handleMouseOver = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
83+
if (e.target instanceof HTMLElement) {
84+
const isOuter = e.target.classList.contains(MESSAGE_ITEM_CLASS);
85+
if (e.buttons === 1 && isInSelectMode && isOuter && !isLockMouseOverSelection.current) {
86+
isSelectedRecently.current = true;
87+
selectMessage(e);
88+
} else {
89+
isSelectedRecently.current = false;
90+
}
91+
}
92+
};
93+
5294
const handleMouseMove = useThrottledCallback((e: React.MouseEvent) => {
5395
const quickReactionContainer = quickReactionRef.current;
5496
if (!quickReactionContainer) return;
@@ -102,6 +144,12 @@ export default function useOuterHandlers(
102144
}
103145

104146
function handleClick(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
147+
// prevent select and deselect
148+
if (isSelectedRecently.current) {
149+
isSelectedRecently.current = false;
150+
return;
151+
}
152+
105153
if (isInSelectMode) {
106154
selectMessage(e);
107155
return;
@@ -184,21 +232,28 @@ export default function useOuterHandlers(
184232
]);
185233

186234
function handleMouseLeave(e: React.MouseEvent<HTMLDivElement>) {
235+
if (longClickTimeoutRef.current) {
236+
clearTimeout(longClickTimeoutRef.current);
237+
}
238+
isSelectedRecently.current = false;
239+
187240
// Because `mousemove` event is throttled, we need to also throttle `mouseleave` event,
188241
// so the order of events is preserved
189242
requestMeasure(unmarkQuickReactionVisible);
190243
if (shouldHandleMouseLeave) handleDocumentGroupMouseLeave(e);
191244
}
192245

193246
return {
194-
handleMouseDown: !isInSelectMode ? handleMouseDown : undefined,
247+
handleMouseDown,
195248
handleClick,
196249
handleContextMenu: !isInSelectMode ? handleContextMenu : (isProtected ? stopEvent : undefined),
197250
handleDoubleClick: !isInSelectMode ? handleContainerDoubleClick : undefined,
198251
handleContentDoubleClick: !IS_TOUCH_ENV ? stopPropagation : undefined,
199252
handleMouseMove,
253+
handleMouseUp,
200254
handleSendQuickReaction,
201255
handleMouseLeave,
256+
handleMouseOver,
202257
isSwiped,
203258
isQuickReactionVisible,
204259
handleDocumentGroupMouseEnter,

0 commit comments

Comments
 (0)