mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-03-20 00:52:22 +00:00
Implement thread UI update (#722)
* Implement thread UI update * remove comment
This commit is contained in:
parent
ee3a79fdcf
commit
161dc5f901
3 changed files with 78 additions and 31 deletions
|
@ -7,13 +7,20 @@ import { useParams } from "react-router-dom";
|
|||
import truncate from "truncate";
|
||||
|
||||
const THREAD_CALLOUT_DETAIL_WIDTH = 26;
|
||||
export default function ThreadItem({ workspace, thread, onRemove, hasNext }) {
|
||||
export default function ThreadItem({
|
||||
idx,
|
||||
activeIdx,
|
||||
isActive,
|
||||
workspace,
|
||||
thread,
|
||||
onRemove,
|
||||
hasNext,
|
||||
}) {
|
||||
const optionsContainer = useRef(null);
|
||||
const { slug, threadSlug = null } = useParams();
|
||||
const { slug } = useParams();
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const [name, setName] = useState(thread.name);
|
||||
|
||||
const isActive = threadSlug === thread.slug;
|
||||
const linkTo = !thread.slug
|
||||
? paths.workspace.chat(slug)
|
||||
: paths.workspace.thread(slug, thread.slug);
|
||||
|
@ -23,33 +30,48 @@ export default function ThreadItem({ workspace, thread, onRemove, hasNext }) {
|
|||
{/* Curved line Element and leader if required */}
|
||||
<div
|
||||
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
|
||||
className="border-l border-b border-slate-300 h-[50%] absolute top-0 left-2 rounded-bl-lg"
|
||||
className={`${
|
||||
isActive
|
||||
? "border-l-2 border-b-2 border-white"
|
||||
: "border-l border-b border-slate-300"
|
||||
} h-[50%] absolute top-0 left-2 rounded-bl-lg`}
|
||||
></div>
|
||||
{/* Downstroke border for next item */}
|
||||
{hasNext && (
|
||||
<div
|
||||
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH / 2 }}
|
||||
className="border-l border-slate-300 h-[100%] absolute top-0 left-2"
|
||||
className={`${
|
||||
idx <= activeIdx && !isActive
|
||||
? "border-l-2 border-white"
|
||||
: "border-l border-slate-300"
|
||||
} h-[100%] absolute top-0 left-2`}
|
||||
></div>
|
||||
)}
|
||||
|
||||
{/* Curved line inline placeholder for spacing */}
|
||||
{/* Curved line inline placeholder for spacing - not visible */}
|
||||
<div
|
||||
style={{ width: THREAD_CALLOUT_DETAIL_WIDTH }}
|
||||
className="w-[26px] h-full"
|
||||
/>
|
||||
<div className="flex w-full items-center justify-between pr-2 group relative">
|
||||
<a href={isActive ? "#" : linkTo} className="w-full">
|
||||
<p
|
||||
className={`text-left text-sm ${
|
||||
isActive
|
||||
? "font-semibold text-slate-300"
|
||||
: "text-slate-400 italic"
|
||||
}`}
|
||||
>
|
||||
{truncate(name, 25)}
|
||||
</p>
|
||||
</a>
|
||||
{!!thread.slug && (
|
||||
{thread.deleted ? (
|
||||
<a className="w-full">
|
||||
<p className={`text-left text-sm text-slate-400/50 italic`}>
|
||||
deleted thread
|
||||
</p>
|
||||
</a>
|
||||
) : (
|
||||
<a href={isActive ? "#" : linkTo} className="w-full">
|
||||
<p
|
||||
className={`text-left text-sm ${
|
||||
isActive ? "font-bold text-white" : "text-slate-400"
|
||||
}`}
|
||||
>
|
||||
{truncate(name, 25)}
|
||||
</p>
|
||||
</a>
|
||||
)}
|
||||
{!!thread.slug && !thread.deleted && (
|
||||
<div ref={optionsContainer}>
|
||||
<div className="flex items-center w-fit group-hover:visible md:invisible gap-x-1">
|
||||
<button
|
||||
|
|
|
@ -4,8 +4,10 @@ import showToast from "@/utils/toast";
|
|||
import { Plus, CircleNotch } from "@phosphor-icons/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import ThreadItem from "./ThreadItem";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
export default function ThreadContainer({ workspace }) {
|
||||
const { threadSlug = null } = useParams();
|
||||
const [threads, setThreads] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
@ -20,7 +22,12 @@ export default function ThreadContainer({ workspace }) {
|
|||
}, [workspace.slug]);
|
||||
|
||||
function removeThread(threadId) {
|
||||
setThreads((prev) => prev.filter((thread) => thread.id !== threadId));
|
||||
setThreads((prev) =>
|
||||
prev.map((_t) => {
|
||||
if (_t.id !== threadId) return _t;
|
||||
return { ..._t, deleted: true };
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
|
@ -33,15 +40,26 @@ export default function ThreadContainer({ workspace }) {
|
|||
);
|
||||
}
|
||||
|
||||
const activeThreadIdx = !!threads.find(
|
||||
(thread) => thread?.slug === threadSlug
|
||||
)
|
||||
? threads.findIndex((thread) => thread?.slug === threadSlug) + 1
|
||||
: 0;
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<ThreadItem
|
||||
idx={0}
|
||||
activeIdx={activeThreadIdx}
|
||||
isActive={activeThreadIdx === 0}
|
||||
thread={{ slug: null, name: "default" }}
|
||||
hasNext={threads.length > 0}
|
||||
/>
|
||||
{threads.map((thread, i) => (
|
||||
<ThreadItem
|
||||
key={thread.slug}
|
||||
idx={i + 1}
|
||||
activeIdx={activeThreadIdx}
|
||||
isActive={activeThreadIdx === i + 1}
|
||||
workspace={workspace}
|
||||
onRemove={removeThread}
|
||||
thread={thread}
|
||||
|
@ -54,7 +72,7 @@ export default function ThreadContainer({ workspace }) {
|
|||
}
|
||||
|
||||
function NewThreadButton({ workspace }) {
|
||||
const [loading, setLoading] = useState();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onClick = async () => {
|
||||
setLoading(true);
|
||||
const { thread, error } = await Workspace.threads.new(workspace.slug);
|
||||
|
@ -74,15 +92,22 @@ function NewThreadButton({ workspace }) {
|
|||
className="w-full relative flex h-[40px] items-center border-none hover:bg-slate-600/20 rounded-lg"
|
||||
>
|
||||
<div className="flex w-full gap-x-2 items-center pl-4">
|
||||
<div className="bg-zinc-600 p-2 rounded-lg h-[24px] w-[24px] flex items-center justify-center">
|
||||
{loading ? (
|
||||
<CircleNotch
|
||||
weight="bold"
|
||||
size={14}
|
||||
className="shrink-0 animate-spin text-slate-100"
|
||||
/>
|
||||
) : (
|
||||
<Plus weight="bold" size={14} className="shrink-0 text-slate-100" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<CircleNotch className="animate-spin text-slate-300" />
|
||||
<p className="text-left text-slate-100 text-sm">Starting Thread...</p>
|
||||
) : (
|
||||
<Plus className="text-slate-300" />
|
||||
)}
|
||||
{loading ? (
|
||||
<p className="text-left text-slate-300 text-sm">starting thread...</p>
|
||||
) : (
|
||||
<p className="text-left text-slate-300 text-sm">new thread</p>
|
||||
<p className="text-left text-slate-100 text-sm">New Thread</p>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
@ -80,12 +80,12 @@ export default function ActiveWorkspaces() {
|
|||
href={isActive ? null : paths.workspace.chat(workspace.slug)}
|
||||
className={`
|
||||
transition-all duration-[200ms]
|
||||
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-lg text-slate-200 justify-start items-center border
|
||||
hover:bg-workspace-item-selected-gradient hover:border-slate-100 hover:border-opacity-50
|
||||
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-lg text-slate-200 justify-start items-center
|
||||
hover:bg-workspace-item-selected-gradient
|
||||
${
|
||||
isActive
|
||||
? "bg-workspace-item-selected-gradient border-slate-100 border-opacity-50"
|
||||
: "bg-workspace-item-gradient bg-opacity-60 border-transparent"
|
||||
? "border-2 bg-workspace-item-selected-gradient border-white"
|
||||
: "border bg-workspace-item-gradient bg-opacity-60 border-transparent hover:border-slate-100 hover:border-opacity-50"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
|
|
Loading…
Add table
Reference in a new issue