@@ -22,6 +22,8 @@ const QUICK_REACTION_DOUBLE_TAP_DELAY = 200;
22
22
const QUICK_REACTION_AREA_WIDTH = 3 * REM ;
23
23
const QUICK_REACTION_AREA_HEIGHT = Number ( REM ) ;
24
24
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 ;
25
27
26
28
export default function useOuterHandlers (
27
29
selectMessage : ( e ?: React . MouseEvent < HTMLDivElement , MouseEvent > , groupedId ?: string ) => void ,
@@ -37,18 +39,58 @@ export default function useOuterHandlers(
37
39
quickReactionRef : RefObject < HTMLDivElement > ,
38
40
shouldHandleMouseLeave : boolean ,
39
41
getIsMessageListReady : Signal < boolean > ,
42
+ isSelected : boolean | undefined ,
40
43
) {
41
44
const { updateDraftReplyInfo, sendDefaultReaction } = getActions ( ) ;
42
45
43
46
const [ isQuickReactionVisible , markQuickReactionVisible , unmarkQuickReactionVisible ] = useFlag ( ) ;
44
47
const [ isSwiped , markSwiped , unmarkSwiped ] = useFlag ( ) ;
45
48
const doubleTapTimeoutRef = useRef < NodeJS . Timeout > ( ) ;
49
+ const longClickTimeoutRef = useRef < NodeJS . Timeout > ( ) ;
50
+ const isSelectedRecently = useRef < boolean > ( ) ;
51
+ const isLockMouseOverSelection = useRef < boolean > ( ) ;
46
52
47
53
function handleMouseDown ( e : React . MouseEvent < HTMLDivElement , MouseEvent > ) {
48
54
preventMessageInputBlur ( e ) ;
49
55
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
+ }
50
71
}
51
72
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
+
52
94
const handleMouseMove = useThrottledCallback ( ( e : React . MouseEvent ) => {
53
95
const quickReactionContainer = quickReactionRef . current ;
54
96
if ( ! quickReactionContainer ) return ;
@@ -102,6 +144,12 @@ export default function useOuterHandlers(
102
144
}
103
145
104
146
function handleClick ( e : React . MouseEvent < HTMLDivElement , MouseEvent > ) {
147
+ // prevent select and deselect
148
+ if ( isSelectedRecently . current ) {
149
+ isSelectedRecently . current = false ;
150
+ return ;
151
+ }
152
+
105
153
if ( isInSelectMode ) {
106
154
selectMessage ( e ) ;
107
155
return ;
@@ -184,21 +232,28 @@ export default function useOuterHandlers(
184
232
] ) ;
185
233
186
234
function handleMouseLeave ( e : React . MouseEvent < HTMLDivElement > ) {
235
+ if ( longClickTimeoutRef . current ) {
236
+ clearTimeout ( longClickTimeoutRef . current ) ;
237
+ }
238
+ isSelectedRecently . current = false ;
239
+
187
240
// Because `mousemove` event is throttled, we need to also throttle `mouseleave` event,
188
241
// so the order of events is preserved
189
242
requestMeasure ( unmarkQuickReactionVisible ) ;
190
243
if ( shouldHandleMouseLeave ) handleDocumentGroupMouseLeave ( e ) ;
191
244
}
192
245
193
246
return {
194
- handleMouseDown : ! isInSelectMode ? handleMouseDown : undefined ,
247
+ handleMouseDown,
195
248
handleClick,
196
249
handleContextMenu : ! isInSelectMode ? handleContextMenu : ( isProtected ? stopEvent : undefined ) ,
197
250
handleDoubleClick : ! isInSelectMode ? handleContainerDoubleClick : undefined ,
198
251
handleContentDoubleClick : ! IS_TOUCH_ENV ? stopPropagation : undefined ,
199
252
handleMouseMove,
253
+ handleMouseUp,
200
254
handleSendQuickReaction,
201
255
handleMouseLeave,
256
+ handleMouseOver,
202
257
isSwiped,
203
258
isQuickReactionVisible,
204
259
handleDocumentGroupMouseEnter,
0 commit comments