import Workspace from "@/models/workspace";
import paths from "@/utils/paths";
import showToast from "@/utils/toast";
import {
ArrowCounterClockwise,
DotsThree,
PencilSimple,
Trash,
X,
} from "@phosphor-icons/react";
import { useEffect, useRef, useState } from "react";
import { useParams } from "react-router-dom";
const THREAD_CALLOUT_DETAIL_WIDTH = 26;
export default function ThreadItem({
idx,
activeIdx,
isActive,
workspace,
thread,
onRemove,
toggleMarkForDeletion,
hasNext,
ctrlPressed = false,
}) {
const { slug, threadSlug = null } = useParams();
const optionsContainer = useRef(null);
const [showOptions, setShowOptions] = useState(false);
const linkTo = !thread.slug
? paths.workspace.chat(slug)
: paths.workspace.thread(slug, thread.slug);
return (
{/* Curved line Element and leader if required */}
{/* Downstroke border for next item */}
{hasNext && (
)}
{/* Curved line inline placeholder for spacing - not visible */}
{thread.deleted ? (
{ctrlPressed && (
toggleMarkForDeletion(thread.id)}
>
)}
) : (
{thread.name}
)}
{!!thread.slug && !thread.deleted && (
{" "}
{/* Added flex and items-center */}
{ctrlPressed ? (
toggleMarkForDeletion(thread.id)}
>
) : (
setShowOptions(!showOptions)}
aria-label="Thread options"
>
)}
{showOptions && (
setShowOptions(false)}
currentThreadSlug={threadSlug}
/>
)}
)}
);
}
function OptionsMenu({
containerRef,
workspace,
thread,
onRemove,
close,
currentThreadSlug,
}) {
const menuRef = useRef(null);
// Ref menu options
const outsideClick = (e) => {
if (!menuRef.current) return false;
if (
!menuRef.current?.contains(e.target) &&
!containerRef.current?.contains(e.target)
)
close();
return false;
};
const isEsc = (e) => {
if (e.key === "Escape" || e.key === "Esc") close();
};
function cleanupListeners() {
window.removeEventListener("click", outsideClick);
window.removeEventListener("keyup", isEsc);
}
// end Ref menu options
useEffect(() => {
function setListeners() {
if (!menuRef?.current || !containerRef.current) return false;
window.document.addEventListener("click", outsideClick);
window.document.addEventListener("keyup", isEsc);
}
setListeners();
return cleanupListeners;
}, [menuRef.current, containerRef.current]);
const renameThread = async () => {
const name = window
.prompt("What would you like to rename this thread to?")
?.trim();
if (!name || name.length === 0) {
close();
return;
}
const { message } = await Workspace.threads.update(
workspace.slug,
thread.slug,
{ name }
);
if (!!message) {
showToast(`Thread could not be updated! ${message}`, "error", {
clear: true,
});
close();
return;
}
thread.name = name;
close();
};
const handleDelete = async () => {
if (
!window.confirm(
"Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this."
)
)
return;
const success = await Workspace.threads.delete(workspace.slug, thread.slug);
if (!success) {
showToast("Thread could not be deleted!", "error", { clear: true });
return;
}
if (success) {
showToast("Thread deleted successfully!", "success", { clear: true });
onRemove(thread.id);
// Redirect if deleting the active thread
if (currentThreadSlug === thread.slug) {
window.location.href = paths.workspace.chat(workspace.slug);
}
return;
}
};
return (
);
}