feat: Add focus handling to textarea in Messages component and implement auto-scrolling in ThreadChat

This commit is contained in:
Poovizhi99 2025-06-20 17:59:29 +05:30
parent 128c1148f8
commit 983e9c269d
6 changed files with 61 additions and 24 deletions

View File

@ -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">

View File

@ -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 />

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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}`,
{