mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-08 05:39:13 +00:00
Unshare public conversations from the title pane on web app
Only show the unshare button on public conversations created by the currently logged in user. Otherwise hide the button Set conversation.isOwner = true only if currently logged in user shared the current conversation. This isOwner information is passed by the get shared conversation API endpoint
This commit is contained in:
@@ -42,6 +42,7 @@ interface ChatHistoryProps {
|
|||||||
setAgent: (agent: AgentData) => void;
|
setAgent: (agent: AgentData) => void;
|
||||||
customClassName?: string;
|
customClassName?: string;
|
||||||
setIsChatSideBarOpen?: (isOpen: boolean) => void;
|
setIsChatSideBarOpen?: (isOpen: boolean) => void;
|
||||||
|
setIsOwner?: (isOwner: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TrainOfThoughtComponentProps {
|
interface TrainOfThoughtComponentProps {
|
||||||
@@ -60,13 +61,13 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
|
|||||||
open: {
|
open: {
|
||||||
height: "auto",
|
height: "auto",
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
transition: { duration: 0.3, ease: "easeOut" }
|
transition: { duration: 0.3, ease: "easeOut" },
|
||||||
},
|
},
|
||||||
closed: {
|
closed: {
|
||||||
height: 0,
|
height: 0,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: { duration: 0.3, ease: "easeIn" }
|
transition: { duration: 0.3, ease: "easeIn" },
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -103,17 +104,14 @@ function TrainOfThoughtComponent(props: TrainOfThoughtComponentProps) {
|
|||||||
))}
|
))}
|
||||||
<AnimatePresence initial={false}>
|
<AnimatePresence initial={false}>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<motion.div
|
<motion.div initial="closed" animate="open" exit="closed" variants={variants}>
|
||||||
initial="closed"
|
|
||||||
animate="open"
|
|
||||||
exit="closed"
|
|
||||||
variants={variants}
|
|
||||||
>
|
|
||||||
{props.trainOfThought.map((train, index) => (
|
{props.trainOfThought.map((train, index) => (
|
||||||
<TrainOfThought
|
<TrainOfThought
|
||||||
key={`train-${index}`}
|
key={`train-${index}`}
|
||||||
message={train}
|
message={train}
|
||||||
primary={index === lastIndex && props.lastMessage && !props.completed}
|
primary={
|
||||||
|
index === lastIndex && props.lastMessage && !props.completed
|
||||||
|
}
|
||||||
agentColor={props.agentColor}
|
agentColor={props.agentColor}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -174,7 +172,6 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
latestUserMessageRef.current?.scrollIntoView({ behavior: "auto", block: "start" });
|
latestUserMessageRef.current?.scrollIntoView({ behavior: "auto", block: "start" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [data, currentPage]);
|
}, [data, currentPage]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -247,6 +244,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((chatData: ChatResponse) => {
|
.then((chatData: ChatResponse) => {
|
||||||
props.setTitle(chatData.response.slug);
|
props.setTitle(chatData.response.slug);
|
||||||
|
props.setIsOwner && props.setIsOwner(chatData?.response?.is_owner);
|
||||||
if (
|
if (
|
||||||
chatData &&
|
chatData &&
|
||||||
chatData.response &&
|
chatData.response &&
|
||||||
@@ -274,6 +272,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
agent: chatData.response.agent,
|
agent: chatData.response.agent,
|
||||||
conversation_id: chatData.response.conversation_id,
|
conversation_id: chatData.response.conversation_id,
|
||||||
slug: chatData.response.slug,
|
slug: chatData.response.slug,
|
||||||
|
is_owner: chatData.response.is_owner,
|
||||||
};
|
};
|
||||||
props.setAgent(chatData.response.agent);
|
props.setAgent(chatData.response.agent);
|
||||||
setData(chatMetadata);
|
setData(chatMetadata);
|
||||||
@@ -312,7 +311,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
|
|
||||||
function constructAgentName() {
|
function constructAgentName() {
|
||||||
if (!data || !data.agent || !data.agent?.name) return `Agent`;
|
if (!data || !data.agent || !data.agent?.name) return `Agent`;
|
||||||
if (data.agent.is_hidden) return 'Khoj';
|
if (data.agent.is_hidden) return "Khoj";
|
||||||
return data.agent?.name;
|
return data.agent?.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +358,8 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
md:h-[calc(100svh-theme(spacing.44))]
|
md:h-[calc(100svh-theme(spacing.44))]
|
||||||
lg:h-[calc(100svh-theme(spacing.72))]
|
lg:h-[calc(100svh-theme(spacing.72))]
|
||||||
`}
|
`}
|
||||||
ref={scrollAreaRef}>
|
ref={scrollAreaRef}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className={`${styles.chatHistory} ${props.customClassName}`}>
|
<div className={`${styles.chatHistory} ${props.customClassName}`}>
|
||||||
<div ref={sentinelRef} style={{ height: "1px" }}>
|
<div ref={sentinelRef} style={{ height: "1px" }}>
|
||||||
@@ -472,7 +471,7 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
|||||||
onDeleteMessage={handleDeleteMessage}
|
onDeleteMessage={handleDeleteMessage}
|
||||||
customClassName="fullHistory"
|
customClassName="fullHistory"
|
||||||
borderLeftColor={`${data?.agent?.color}-500`}
|
borderLeftColor={`${data?.agent?.color}-500`}
|
||||||
isLastMessage={index === (props.incomingMessages!.length - 1)}
|
isLastMessage={index === props.incomingMessages!.length - 1}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ export interface ChatHistoryData {
|
|||||||
agent: AgentData;
|
agent: AgentData;
|
||||||
conversation_id: string;
|
conversation_id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
is_owner: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendFeedback(uquery: string, kquery: string, sentiment: string) {
|
function sendFeedback(uquery: string, kquery: string, sentiment: string) {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s
|
|||||||
import { AppSidebar } from "@/app/components/appSidebar/appSidebar";
|
import { AppSidebar } from "@/app/components/appSidebar/appSidebar";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { KhojLogoType } from "@/app/components/logo/khojLogo";
|
import { KhojLogoType } from "@/app/components/logo/khojLogo";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Trash } from "@phosphor-icons/react";
|
||||||
|
|
||||||
interface ChatBodyDataProps {
|
interface ChatBodyDataProps {
|
||||||
chatOptionsData: ChatOptions | null;
|
chatOptionsData: ChatOptions | null;
|
||||||
@@ -34,6 +36,37 @@ interface ChatBodyDataProps {
|
|||||||
conversationId?: string;
|
conversationId?: string;
|
||||||
setQueryToProcess: (query: string) => void;
|
setQueryToProcess: (query: string) => void;
|
||||||
setImages: (images: string[]) => void;
|
setImages: (images: string[]) => void;
|
||||||
|
setIsOwner: (isOwner: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnshareButton({ slug, className }: { slug: string; className?: string }) {
|
||||||
|
const handleUnshare = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/chat/share?public_conversation_slug=${slug}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.redirected) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
console.error("Failed to unshare conversation");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error unsharing conversation:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
className="p-0 text-sm h-auto text-rose-500 hover:text-rose-600"
|
||||||
|
variant={"ghost"}
|
||||||
|
onClick={handleUnshare}
|
||||||
|
>
|
||||||
|
<Trash className={`${className}`} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChatBodyData(props: ChatBodyDataProps) {
|
function ChatBodyData(props: ChatBodyDataProps) {
|
||||||
@@ -87,6 +120,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
|||||||
conversationId={props.conversationId || ""}
|
conversationId={props.conversationId || ""}
|
||||||
setAgent={setAgentMetadata}
|
setAgent={setAgentMetadata}
|
||||||
setTitle={props.setTitle}
|
setTitle={props.setTitle}
|
||||||
|
setIsOwner={props.setIsOwner}
|
||||||
pendingMessage={processingMessage ? message : ""}
|
pendingMessage={processingMessage ? message : ""}
|
||||||
incomingMessages={props.streamedMessages}
|
incomingMessages={props.streamedMessages}
|
||||||
customClassName={chatHistoryCustomClassName}
|
customClassName={chatHistoryCustomClassName}
|
||||||
@@ -105,7 +139,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
|||||||
agentColor={agentMetadata?.color}
|
agentColor={agentMetadata?.color}
|
||||||
isMobileWidth={props.isMobileWidth}
|
isMobileWidth={props.isMobileWidth}
|
||||||
setUploadedFiles={props.setUploadedFiles}
|
setUploadedFiles={props.setUploadedFiles}
|
||||||
setTriggeredAbort={() => { }}
|
setTriggeredAbort={() => {}}
|
||||||
ref={chatInputRef}
|
ref={chatInputRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,6 +158,7 @@ export default function SharedChat() {
|
|||||||
const [uploadedFiles, setUploadedFiles] = useState<AttachedFileText[] | null>(null);
|
const [uploadedFiles, setUploadedFiles] = useState<AttachedFileText[] | null>(null);
|
||||||
const [paramSlug, setParamSlug] = useState<string | undefined>(undefined);
|
const [paramSlug, setParamSlug] = useState<string | undefined>(undefined);
|
||||||
const [images, setImages] = useState<string[]>([]);
|
const [images, setImages] = useState<string[]>([]);
|
||||||
|
const [isOwner, setIsOwner] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: authenticatedData,
|
data: authenticatedData,
|
||||||
@@ -215,6 +250,12 @@ export default function SharedChat() {
|
|||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
|
{isOwner && authenticatedData && (
|
||||||
|
<UnshareButton
|
||||||
|
slug={paramSlug}
|
||||||
|
className={"h-4 w-4 mt-1"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@@ -237,6 +278,7 @@ export default function SharedChat() {
|
|||||||
setUploadedFiles={setUploadedFiles}
|
setUploadedFiles={setUploadedFiles}
|
||||||
isMobileWidth={isMobileWidth}
|
isMobileWidth={isMobileWidth}
|
||||||
setImages={setImages}
|
setImages={setImages}
|
||||||
|
setIsOwner={setIsOwner}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -93,10 +93,8 @@ div.agentIndicator {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
div.chatTitleWrapper {
|
||||||
div.chatBody {
|
grid-template-columns: 1fr auto;
|
||||||
grid-template-columns: 0fr 1fr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
@@ -108,6 +106,15 @@ div.agentIndicator {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.chatBody {
|
||||||
|
grid-template-columns: 0fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.chatBox {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
div.chatLayout {
|
div.chatLayout {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
Reference in New Issue
Block a user