From a03c6b60d5a738c584d13f8a71d0274ee9ce4a0d Mon Sep 17 00:00:00 2001 From: Jonathan Krebs Date: Wed, 7 May 2025 14:21:44 +0200 Subject: [PATCH] add deep linking to a new persona chat --- .../chat/create/persona/[id]/loading.tsx | 5 ++ .../chat/create/persona/[id]/page.tsx | 26 +++++++++ .../copy-start-new-persona-chat.tsx | 41 ++++++++++++++ .../persona-card/persona-card.tsx | 3 +- .../persona-services/persona-service.ts | 54 +++++++++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/app/(authenticated)/chat/create/persona/[id]/loading.tsx create mode 100644 src/app/(authenticated)/chat/create/persona/[id]/page.tsx create mode 100644 src/features/persona-page/persona-card/copy-start-new-persona-chat.tsx diff --git a/src/app/(authenticated)/chat/create/persona/[id]/loading.tsx b/src/app/(authenticated)/chat/create/persona/[id]/loading.tsx new file mode 100644 index 000000000..ee5ecd7bf --- /dev/null +++ b/src/app/(authenticated)/chat/create/persona/[id]/loading.tsx @@ -0,0 +1,5 @@ +import { PageLoader } from "@/features/ui/page-loader"; + +export default function Loading() { + return ; +} \ No newline at end of file diff --git a/src/app/(authenticated)/chat/create/persona/[id]/page.tsx b/src/app/(authenticated)/chat/create/persona/[id]/page.tsx new file mode 100644 index 000000000..1d533ac75 --- /dev/null +++ b/src/app/(authenticated)/chat/create/persona/[id]/page.tsx @@ -0,0 +1,26 @@ +import { DisplayError } from "@/features/ui/error/display-error"; +import { + CreatePersonaChat, + FindPersonaForCurrentUser +} from "@/features/persona-page/persona-services/persona-service"; +import {RedirectToChatThread} from "@/features/common/navigation-helpers"; + +interface HomeParams { + params: { + id: string; + }; +} + +export default async function Home(props: HomeParams) { + const { id } = props.params; + const personaResponse = await FindPersonaForCurrentUser(id); + if (personaResponse.status !== "OK") { + return ; + } + const response = await CreatePersonaChat(id); + if (response.status === "OK") { + RedirectToChatThread(response.response.id); + } else { + return ; + } +} \ No newline at end of file diff --git a/src/features/persona-page/persona-card/copy-start-new-persona-chat.tsx b/src/features/persona-page/persona-card/copy-start-new-persona-chat.tsx new file mode 100644 index 000000000..53691a95d --- /dev/null +++ b/src/features/persona-page/persona-card/copy-start-new-persona-chat.tsx @@ -0,0 +1,41 @@ +"use client"; +import { Button } from "@/features/ui/button"; +import {ClipboardCheckIcon, LinkIcon} from "lucide-react"; +import {FC, useEffect, useState} from "react"; + +interface Props { + id: string; +} + +export const CopyStartNewPersonaChat: FC = (props) => { + const id = props.id; + const [hostUrl, setHostUrl] = useState(""); + const [isIconChecked, setIsIconChecked] = useState(false); + + useEffect(() => { + if (id) { + setHostUrl(window.location.origin); + } + }, [id]); + + const handleButtonClick = () => { + if(hostUrl && id) { + const urlToCopy = `${hostUrl}/chat/create/persona/${id}` + navigator.clipboard.writeText(urlToCopy).then(() => {setIsIconChecked(true);}); + } + }; + + return ( + + ); +}; \ No newline at end of file diff --git a/src/features/persona-page/persona-card/persona-card.tsx b/src/features/persona-page/persona-card/persona-card.tsx index 52ca8ca74..28c61fe73 100644 --- a/src/features/persona-page/persona-card/persona-card.tsx +++ b/src/features/persona-page/persona-card/persona-card.tsx @@ -10,6 +10,7 @@ import { PersonaModel } from "../persona-services/models"; import { PersonaCardContextMenu } from "./persona-card-context-menu"; import { ViewPersona } from "./persona-view"; import { StartNewPersonaChat } from "./start-new-persona-chat"; +import {CopyStartNewPersonaChat} from "@/features/persona-page/persona-card/copy-start-new-persona-chat"; interface Props { persona: PersonaModel; @@ -33,7 +34,7 @@ export const PersonaCard: FC = (props) => { {props.showContextMenu && } - + diff --git a/src/features/persona-page/persona-services/persona-service.ts b/src/features/persona-page/persona-services/persona-service.ts index 57de4d943..8e8b9bf55 100644 --- a/src/features/persona-page/persona-services/persona-service.ts +++ b/src/features/persona-page/persona-services/persona-service.ts @@ -239,6 +239,60 @@ export const UpsertPersona = async ( } }; +export const FindPersonaForCurrentUser = async ( + id: string +): Promise> => { + try { + const querySpec: SqlQuerySpec = { + query: + "SELECT * FROM root r WHERE r.type=@type AND (r.isPublished=@isPublished OR r.userId=@userId) AND r.id=@id", + parameters: [ + { + name: "@type", + value: PERSONA_ATTRIBUTE, + }, + { + name: "@isPublished", + value: true, + }, + { + name: "@userId", + value: await userHashedId(), + }, + { + name: "@id", + value: id, + }, + ], + }; + + const { resources } = await HistoryContainer() + .items.query(querySpec) + .fetchAll(); + + if (resources.length === 0) { + return { + status: "NOT_FOUND", + errors: [{ message: `Persona not found. Please ensure the persona exists and is published for your account.` }], + }; + } + + return { + status: "OK", + response: resources[0], + }; + } catch (error) { + return { + status: "ERROR", + errors: [ + { + message: `Error finding persona: ${error}`, + }, + ], + }; + } +}; + export const FindAllPersonaForCurrentUser = async (): Promise< ServerActionResponse> > => {