feat: Add focus handling to textarea in Messages component and implement auto-scrolling in ThreadChat
This commit is contained in:
parent
128c1148f8
commit
983e9c269d
|
@ -165,6 +165,15 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
|||
}
|
||||
}
|
||||
|
||||
const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
|
||||
requestAnimationFrame(() => {
|
||||
if (textareaRef.current) {
|
||||
const length = textareaRef.current.value.length;
|
||||
textareaRef.current.setSelectionRange(length, length);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isEditComment ? (
|
||||
|
@ -177,6 +186,7 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
|||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
style={{ resize: "none" }}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
</div>
|
||||
<div className="actions-container">
|
||||
|
|
|
@ -29,12 +29,12 @@ const ThreadChat: React.FC = () => {
|
|||
const [messages, setMessages] = useState<Reply[]>([])
|
||||
const { projectId } = useParams();
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [selectedDiv, setSelectedDiv] = useState(true);
|
||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||
const [position, setPosition] = useState({ x: position2Dstate.x, y: position2Dstate.y });
|
||||
console.log('position2Dstate: ', position2Dstate);
|
||||
console.log('position:Comment ', position);
|
||||
const { threadSocket } = useSocketStore();
|
||||
const modeRef = useRef<'create' | 'edit' | null>(null);
|
||||
const messagesRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
modeRef.current = mode;
|
||||
|
@ -42,7 +42,7 @@ const ThreadChat: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (comments.length > 0 && selectedComment) {
|
||||
console.log('comments: ', comments);
|
||||
|
||||
|
||||
const allMessages = comments
|
||||
.flatMap((val: any) =>
|
||||
|
@ -60,7 +60,7 @@ const ThreadChat: React.FC = () => {
|
|||
});
|
||||
|
||||
setMessages(allMessages);
|
||||
console.log('allMessages: ', allMessages);
|
||||
|
||||
}
|
||||
|
||||
}, [selectedComment])
|
||||
|
@ -102,16 +102,17 @@ const ThreadChat: React.FC = () => {
|
|||
wrapper.setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
const handlePointerMove = ({ clientX, clientY }: { clientX: number; clientY: number }) => {
|
||||
|
||||
if (!dragging) return;
|
||||
const updatePosition = (
|
||||
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||
allowMove: boolean = true
|
||||
) => {
|
||||
if (!allowMove || !wrapperRef.current) return;
|
||||
|
||||
const container = document.getElementById("work-space-three-d-canvas");
|
||||
const wrapper = wrapperRef.current;
|
||||
if (!container || !wrapper) return;
|
||||
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const wrapperRect = wrapper.getBoundingClientRect();
|
||||
|
||||
let newX = clientX - containerRect.left - dragOffset.x;
|
||||
let newY = clientY - containerRect.top - dragOffset.y;
|
||||
|
@ -125,10 +126,14 @@ const ThreadChat: React.FC = () => {
|
|||
setPosition({ x: newX, y: newY });
|
||||
};
|
||||
|
||||
const handlePointerMove = (e: { clientX: number; clientY: number }) => {
|
||||
if (dragging) updatePosition(e, true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// handlePointerMove({ clientX: position.x, clientY: position.y })
|
||||
// console.log('wrapperRef: ', wrapperRef.current);
|
||||
}, [selectedComment])
|
||||
updatePosition({ clientX: position.x, clientY: position.y }, true);
|
||||
}, [selectedComment]);
|
||||
|
||||
|
||||
const handlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!dragging) return;
|
||||
|
@ -171,7 +176,6 @@ const ThreadChat: React.FC = () => {
|
|||
} catch {
|
||||
|
||||
}
|
||||
setValue("")
|
||||
setInputActive(false)
|
||||
}
|
||||
const handleDeleteThread = async () => {
|
||||
|
@ -260,6 +264,18 @@ const ThreadChat: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const messagesWrapper = document.querySelector(".messages-wrapper");
|
||||
if (messagesWrapper) {
|
||||
messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (messages.length > 0)
|
||||
scrollToBottom();
|
||||
}, [messages])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
|
@ -306,7 +322,7 @@ const ThreadChat: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="messages-wrapper">
|
||||
<div className="messages-wrapper" ref={messagesRef}>
|
||||
{selectedComment &&
|
||||
<Messages val={selectedComment} i={1} key={selectedComment.creatorId} isEditableThread={true} setEditedThread={setEditedThread} editedThread={editedThread} />
|
||||
}
|
||||
|
@ -332,10 +348,20 @@ const ThreadChat: React.FC = () => {
|
|||
handleCreateComments(e);
|
||||
textareaRef.current?.blur();
|
||||
}
|
||||
setValue('')
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
textareaRef.current?.blur();
|
||||
}
|
||||
}}
|
||||
onFocus={() => setInputActive(true)}
|
||||
onClick={() => {
|
||||
if (!commentPositionState && selectedComment !== null) {
|
||||
setMode("create");
|
||||
}
|
||||
}}
|
||||
autoFocus={selectedComment === null}
|
||||
onBlur={() => setInputActive(false)}
|
||||
onFocus={() => setInputActive(true)}
|
||||
style={{ resize: "none" }}
|
||||
/>
|
||||
<div
|
||||
|
@ -347,6 +373,7 @@ const ThreadChat: React.FC = () => {
|
|||
setMode("create");
|
||||
handleCreateComments(e);
|
||||
}
|
||||
setValue('')
|
||||
}}
|
||||
>
|
||||
<ExpandIcon />
|
||||
|
|
|
@ -36,7 +36,7 @@ function CommentInstance({ comment }: { comment: CommentSchema }) {
|
|||
|
||||
position.project(camera);
|
||||
const x = (position.x * 0.5 + 0.5) * size.width;
|
||||
const y = (-(position.y * 0.5) + 0.3) * size.height;
|
||||
const y = (-(position.y * 0.5) + 0.2) * size.height;
|
||||
setPosition2Dstate({ x, y })
|
||||
|
||||
if (groupRef.current) {
|
||||
|
|
|
@ -32,10 +32,10 @@ function CommentInstances() {
|
|||
: [],
|
||||
}))
|
||||
: [];
|
||||
console.log('formattedThreads: ', formattedThreads);
|
||||
// console.log('formattedThreads: ', formattedThreads);
|
||||
setComments(formattedThreads);
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch threads:", err);
|
||||
// console.error("Failed to fetch threads:", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
// };
|
||||
// threadSocket.on('v1-Comment:response:add', handleAddComment);
|
||||
const handleAddComment = (data: any) => {
|
||||
console.log('Add: ', data);
|
||||
// console.log('Add: ', data);
|
||||
|
||||
const commentData = {
|
||||
replyId: data.data?._id,
|
||||
|
@ -81,7 +81,7 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
: message;
|
||||
})
|
||||
);
|
||||
console.log('data.data?.comment: ', data.data?.comment);
|
||||
// console.log('data.data?.comment: ', data.data?.comment);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
// --- Delete Comment Handler ---
|
||||
|
||||
const handleDeleteComment = (data: any) => {
|
||||
console.log('delete: ', data);
|
||||
// console.log('delete: ', data);
|
||||
|
||||
threadSocket.off('v1-Comment:response:delete', handleDeleteComment);
|
||||
};
|
||||
|
@ -100,7 +100,7 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
|
||||
// --- Create Thread Handler ---
|
||||
const handleCreateThread = (data: any) => {
|
||||
console.log('createThread: ', data);
|
||||
// console.log('createThread: ', data);
|
||||
|
||||
const comment: CommentSchema = {
|
||||
state: 'active',
|
||||
|
@ -113,9 +113,10 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
rotation: [0, 0, 0],
|
||||
comments: [],
|
||||
};
|
||||
setSelectedComment(comment)
|
||||
addComment(comment);
|
||||
setCommentPositionState(null);
|
||||
setSelectedComment(null);
|
||||
// setSelectedComment(null);
|
||||
threadSocket.off('v1-thread:response:create', handleCreateThread);
|
||||
};
|
||||
threadSocket.on('v1-thread:response:create', handleCreateThread);
|
||||
|
@ -142,7 +143,7 @@ const ThreadSocketResponsesDev = ({ setMessages, modeRef, messages }: ThreadSock
|
|||
comments: data.data.comments,
|
||||
};
|
||||
|
||||
console.log('data.data?._id: ', data.data?._id);
|
||||
// console.log('data.data?._id: ', data.data?._id);
|
||||
updateComment(data.data?._id, editedThread);
|
||||
setSelectedComment(editedThread)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
|
|||
|
||||
export const getAllThreads = async (projectId: string) => {
|
||||
try {
|
||||
console.log("projectId: ", projectId);
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v1/Threads/${projectId}`,
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue