global update and global fix
This commit is contained in:
106
apps/frontend/hooks/use-chat-message-selection.ts
Normal file
106
apps/frontend/hooks/use-chat-message-selection.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { ChatMessage } from '@/lib/api';
|
||||
import { isSelectableMessage } from '@/lib/chat-message-utils';
|
||||
|
||||
export function useChatMessageSelection(messages: ChatMessage[]) {
|
||||
const [selectionMode, setSelectionMode] = useState(false);
|
||||
const [selectedMessageIds, setSelectedMessageIds] = useState<string[]>([]);
|
||||
const anchorIndexRef = useRef<number | null>(null);
|
||||
|
||||
const exitSelectionMode = useCallback(() => {
|
||||
setSelectionMode(false);
|
||||
setSelectedMessageIds([]);
|
||||
anchorIndexRef.current = null;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectionMode) return;
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
exitSelectionMode();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}, [exitSelectionMode, selectionMode]);
|
||||
|
||||
const enterSelectionMode = useCallback((messageId?: string) => {
|
||||
if (messageId) {
|
||||
const message = messages.find((item) => item.id === messageId);
|
||||
if (!message || !isSelectableMessage(message)) return;
|
||||
}
|
||||
setSelectionMode(true);
|
||||
setSelectedMessageIds(messageId ? [messageId] : []);
|
||||
anchorIndexRef.current = messageId ? messages.findIndex((item) => item.id === messageId) : null;
|
||||
}, [messages]);
|
||||
|
||||
const addSelectedMessage = useCallback((messageId: string) => {
|
||||
const message = messages.find((item) => item.id === messageId);
|
||||
if (!message || !isSelectableMessage(message)) return;
|
||||
setSelectionMode(true);
|
||||
setSelectedMessageIds((current) => (current.includes(messageId) ? current : [...current, messageId]));
|
||||
anchorIndexRef.current = messages.findIndex((item) => item.id === messageId);
|
||||
}, [messages]);
|
||||
|
||||
const toggleSelectedMessage = useCallback((messageId: string) => {
|
||||
setSelectedMessageIds((current) => {
|
||||
const next = current.includes(messageId) ? current.filter((id) => id !== messageId) : [...current, messageId];
|
||||
return next;
|
||||
});
|
||||
anchorIndexRef.current = messages.findIndex((item) => item.id === messageId);
|
||||
}, [messages]);
|
||||
|
||||
const handleSelectionPointer = useCallback(
|
||||
(messageId: string, event: { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean }) => {
|
||||
const index = messages.findIndex((item) => item.id === messageId);
|
||||
if (index < 0) return false;
|
||||
|
||||
const multiKey = Boolean(event.ctrlKey || event.metaKey);
|
||||
if (multiKey) {
|
||||
if (!selectionMode) {
|
||||
enterSelectionMode(messageId);
|
||||
} else {
|
||||
toggleSelectedMessage(messageId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
if (!selectionMode) {
|
||||
enterSelectionMode(messageId);
|
||||
}
|
||||
const anchor = anchorIndexRef.current ?? index;
|
||||
const start = Math.min(anchor, index);
|
||||
const end = Math.max(anchor, index);
|
||||
const rangeIds = messages
|
||||
.slice(start, end + 1)
|
||||
.filter((item) => isSelectableMessage(item))
|
||||
.map((item) => item.id);
|
||||
setSelectedMessageIds(rangeIds);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (selectionMode) {
|
||||
toggleSelectedMessage(messageId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[enterSelectionMode, messages, selectionMode, toggleSelectedMessage]
|
||||
);
|
||||
|
||||
return {
|
||||
selectionMode,
|
||||
selectedMessageIds,
|
||||
setSelectedMessageIds,
|
||||
enterSelectionMode,
|
||||
exitSelectionMode,
|
||||
toggleSelectedMessage,
|
||||
addSelectedMessage,
|
||||
handleSelectionPointer
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user