From f184b198e5ff4b54756072341500f6c23326cc88 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Wed, 3 Jul 2024 16:41:15 -0700 Subject: [PATCH 1/5] implement delete message feature --- .../HistoricalMessage/Actions/index.jsx | 31 +++++++++++++++++++ .../ChatHistory/HistoricalMessage/index.jsx | 2 ++ .../ChatContainer/ChatHistory/index.jsx | 21 +++++++++++++ server/endpoints/system.js | 2 +- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx index abe1f00e04d..5fdb6473817 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx @@ -7,6 +7,7 @@ import { ArrowsClockwise, Copy, GitMerge, + Trash, } from "@phosphor-icons/react"; import { Tooltip } from "react-tooltip"; import Workspace from "@/models/workspace"; @@ -21,6 +22,7 @@ const Actions = ({ isLastMessage, regenerateMessage, forkThread, + deleteMessage, isEditing, role, }) => { @@ -50,6 +52,12 @@ const Actions = ({ chatId={chatId} /> )} + {chatId && role !== "user" && !isEditing && ( <> + + + + ); +} + export default memo(Actions); diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx index d88a75f3ff4..3863293f26b 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx @@ -24,6 +24,7 @@ const HistoricalMessage = ({ regenerateMessage, saveEditedMessage, forkThread, + deleteMessage, }) => { const { isEditing } = useEditMessage({ chatId, role }); const adjustTextArea = (event) => { @@ -94,6 +95,7 @@ const HistoricalMessage = ({ slug={workspace?.slug} isLastMessage={isLastMessage} regenerateMessage={regenerateMessage} + deleteMessage={deleteMessage} isEditing={isEditing} role={role} forkThread={forkThread} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index 53cbeb64f63..eb857e89e40 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -10,6 +10,8 @@ import Chartable from "./Chartable"; import Workspace from "@/models/workspace"; import { useParams } from "react-router-dom"; import paths from "@/utils/paths"; +import showToast from "@/utils/toast"; +import System from "@/models/system"; export default function ChatHistory({ history = [], @@ -143,6 +145,24 @@ export default function ChatHistory({ newThreadSlug ); }; + const deleteMessage = async (chatId) => { + if ( + !window.confirm( + `Are you sure you want to delete this chat?\n\nThis action is irreversible.` + ) + ) + return false; + + const { success } = await System.deleteChat(chatId); + + if (success) { + const updatedHistory = history.filter((msg) => msg.chatId !== chatId); + updateHistory(updatedHistory); + showToast("Chat deleted successfully.", "success"); + } else { + showToast("Failed to delete chat.", "error"); + } + }; if (history.length === 0) { return ( @@ -231,6 +251,7 @@ export default function ChatHistory({ isLastMessage={isLastBotReply} saveEditedMessage={saveEditedMessage} forkThread={forkThread} + deleteMessage={deleteMessage} /> ); })} diff --git a/server/endpoints/system.js b/server/endpoints/system.js index 05a24e5cade..e32d9bceeee 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -974,7 +974,7 @@ function systemEndpoints(app) { app.delete( "/system/workspace-chats/:id", - [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], + [validatedRequest, flexUserRoleValid([ROLES.all])], async (request, response) => { try { const { id } = request.params; From 46c00b443f473e5ccb76d9c096c1ae6c2be2b064 Mon Sep 17 00:00:00 2001 From: shatfield4 Date: Wed, 3 Jul 2024 17:13:17 -0700 Subject: [PATCH 2/5] add new delete chat endpoint that just hides messages from ui and fix perms --- .../ChatContainer/ChatHistory/index.jsx | 3 +- frontend/src/models/workspace.js | 11 +++++++ server/endpoints/system.js | 30 +++++++++---------- server/endpoints/workspaces.js | 27 +++++++++++++++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index eb857e89e40..15efe2874d9 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -11,7 +11,6 @@ import Workspace from "@/models/workspace"; import { useParams } from "react-router-dom"; import paths from "@/utils/paths"; import showToast from "@/utils/toast"; -import System from "@/models/system"; export default function ChatHistory({ history = [], @@ -153,7 +152,7 @@ export default function ChatHistory({ ) return false; - const { success } = await System.deleteChat(chatId); + const { success } = await Workspace.deleteChat(chatId); if (success) { const updatedHistory = history.filter((msg) => msg.chatId !== chatId); diff --git a/frontend/src/models/workspace.js b/frontend/src/models/workspace.js index 43c723f7901..369ae986c31 100644 --- a/frontend/src/models/workspace.js +++ b/frontend/src/models/workspace.js @@ -384,6 +384,17 @@ const Workspace = { return false; }); }, + deleteChat: async (chatId) => { + return await fetch(`${API_BASE}/workspace/workspace-chats/${chatId}`, { + method: "PUT", + headers: baseHeaders(), + }) + .then((res) => res.json()) + .catch((e) => { + console.error(e); + return { success: false, error: e.message }; + }); + }, forkThread: async function (slug = "", threadSlug = null, chatId = null) { return await fetch(`${API_BASE}/workspace/${slug}/thread/fork`, { method: "POST", diff --git a/server/endpoints/system.js b/server/endpoints/system.js index b7955548136..05a24e5cade 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -75,7 +75,7 @@ function systemEndpoints(app) { const results = await SystemSettings.currentSettings(); response.status(200).json({ results }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } }); @@ -98,7 +98,7 @@ function systemEndpoints(app) { response.sendStatus(200).end(); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -241,7 +241,7 @@ function systemEndpoints(app) { }); } } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } }); @@ -307,7 +307,7 @@ function systemEndpoints(app) { : await VectorDb.totalVectors(); response.status(200).json({ vectorCount }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -322,7 +322,7 @@ function systemEndpoints(app) { await purgeDocument(name); response.sendStatus(200).end(); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -337,7 +337,7 @@ function systemEndpoints(app) { for await (const name of names) await purgeDocument(name); response.sendStatus(200).end(); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -352,7 +352,7 @@ function systemEndpoints(app) { await purgeFolder(name); response.sendStatus(200).end(); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -366,7 +366,7 @@ function systemEndpoints(app) { const localFiles = await viewLocalFiles(); response.status(200).json({ localFiles }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -380,7 +380,7 @@ function systemEndpoints(app) { const online = await new CollectorApi().online(); response.sendStatus(online ? 200 : 503); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -399,7 +399,7 @@ function systemEndpoints(app) { response.status(200).json({ types }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -418,7 +418,7 @@ function systemEndpoints(app) { ); response.status(200).json({ newValues, error }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -452,7 +452,7 @@ function systemEndpoints(app) { } response.status(200).json({ success: !error, error }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -500,7 +500,7 @@ function systemEndpoints(app) { multi_user_mode: false, }); - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } } @@ -511,7 +511,7 @@ function systemEndpoints(app) { const multiUserMode = await SystemSettings.isMultiUserMode(); response.status(200).json({ multiUserMode }); } catch (e) { - console.error(e.message, e); + console.log(e.message, e); response.sendStatus(500).end(); } }); @@ -974,7 +974,7 @@ function systemEndpoints(app) { app.delete( "/system/workspace-chats/:id", - [validatedRequest, flexUserRoleValid([ROLES.all])], + [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], async (request, response) => { try { const { id } = request.params; diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index 460d94222b3..9a95d1b6cf3 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -838,6 +838,33 @@ function workspaceEndpoints(app) { } } ); + + app.put( + "/workspace/workspace-chats/:id", + [validatedRequest, flexUserRoleValid([ROLES.all])], + async (request, response) => { + const user = await userFromSession(request, response); + try { + const { id } = request.params; + const result = await WorkspaceChats._update(Number(id), { + include: false, + user_id: user?.id, + }); + + if (result) { + response.json({ success: true, error: null }); + } else { + response.status(404).json({ + success: false, + error: "Chat not found or user not authorized", + }); + } + } catch (e) { + console.error(e); + response.status(500).json({ success: false, error: "Server error" }); + } + } + ); } module.exports = { workspaceEndpoints }; From 251e390f1bc2cfa64b316a183a17ec8615af8108 Mon Sep 17 00:00:00 2001 From: timothycarambat Date: Wed, 3 Jul 2024 18:02:48 -0700 Subject: [PATCH 3/5] refactor: add delete-event hooks for delete so we dont need to pass so much refactor: implmentation of delete endpoint PUT --- .../Actions/DeleteMessage/index.jsx | 61 +++++++++++++++++ .../HistoricalMessage/Actions/index.jsx | 26 +------- .../ChatHistory/HistoricalMessage/index.jsx | 14 ++-- .../ChatContainer/ChatHistory/index.jsx | 20 ------ frontend/src/index.css | 66 ++++++++++++------- server/endpoints/system.js | 28 ++++---- server/endpoints/workspaces.js | 38 +++++------ 7 files changed, 142 insertions(+), 111 deletions(-) create mode 100644 frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx new file mode 100644 index 00000000000..514c1b6da8a --- /dev/null +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx @@ -0,0 +1,61 @@ +import Workspace from "@/models/workspace"; +import { Trash } from "@phosphor-icons/react"; +import { useState, useEffect } from "react"; +import { Tooltip } from "react-tooltip"; +const DELETE_EVENT = "delete-message"; + +export function useWatchDeleteMessage({ chatId = null }) { + const [isDeleted, setIsDeleted] = useState(false); + + async function onDeleteEvent(e) { + if (e.detail.chatId === chatId) { + setIsDeleted(true); + await Workspace.deleteChat(chatId); + return false; + } + } + + useEffect(() => { + function listenForEvent() { + console.log({ chatId }) + if (!chatId) return; + window.addEventListener(DELETE_EVENT, onDeleteEvent); + } + listenForEvent(); + return () => { + window.removeEventListener(DELETE_EVENT, onDeleteEvent); + }; + }, [chatId]); + + return { isDeleted }; +} + +export function DeleteMessage({ chatId, isEditing, role }) { + if (!chatId || isEditing || role === "user") return null; + + function emitDeleteEvent() { + window.dispatchEvent( + new CustomEvent(DELETE_EVENT, { detail: { chatId } }) + ); + } + + return ( +
+ + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx index 5fdb6473817..715352de545 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx @@ -13,6 +13,7 @@ import { Tooltip } from "react-tooltip"; import Workspace from "@/models/workspace"; import TTSMessage from "./TTSButton"; import { EditMessageAction } from "./EditMessage"; +import { DeleteMessage } from "./DeleteMessage"; const Actions = ({ message, @@ -22,7 +23,6 @@ const Actions = ({ isLastMessage, regenerateMessage, forkThread, - deleteMessage, isEditing, role, }) => { @@ -54,7 +54,6 @@ const Actions = ({ )} @@ -189,27 +188,4 @@ function ForkThread({ chatId, forkThread, isEditing, role }) { ); } -function DeleteMessage({ chatId, deleteMessage, isEditing, role }) { - if (!chatId || isEditing || role === "user") return null; - return ( -
- - -
- ); -} - export default memo(Actions); diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx index 3863293f26b..eb6c0a16004 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx @@ -9,6 +9,7 @@ import { AI_BACKGROUND_COLOR, USER_BACKGROUND_COLOR } from "@/utils/constants"; import { v4 } from "uuid"; import createDOMPurify from "dompurify"; import { EditMessageForm, useEditMessage } from "./Actions/EditMessage"; +import { useWatchDeleteMessage } from "./Actions/DeleteMessage"; const DOMPurify = createDOMPurify(window); const HistoricalMessage = ({ @@ -24,9 +25,9 @@ const HistoricalMessage = ({ regenerateMessage, saveEditedMessage, forkThread, - deleteMessage, }) => { const { isEditing } = useEditMessage({ chatId, role }); + const { isDeleted } = useWatchDeleteMessage({ chatId }); const adjustTextArea = (event) => { const element = event.target; element.style.height = "auto"; @@ -37,9 +38,8 @@ const HistoricalMessage = ({ return (
@@ -62,9 +62,8 @@ const HistoricalMessage = ({ return (
@@ -95,7 +94,6 @@ const HistoricalMessage = ({ slug={workspace?.slug} isLastMessage={isLastMessage} regenerateMessage={regenerateMessage} - deleteMessage={deleteMessage} isEditing={isEditing} role={role} forkThread={forkThread} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index 15efe2874d9..53cbeb64f63 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -10,7 +10,6 @@ import Chartable from "./Chartable"; import Workspace from "@/models/workspace"; import { useParams } from "react-router-dom"; import paths from "@/utils/paths"; -import showToast from "@/utils/toast"; export default function ChatHistory({ history = [], @@ -144,24 +143,6 @@ export default function ChatHistory({ newThreadSlug ); }; - const deleteMessage = async (chatId) => { - if ( - !window.confirm( - `Are you sure you want to delete this chat?\n\nThis action is irreversible.` - ) - ) - return false; - - const { success } = await Workspace.deleteChat(chatId); - - if (success) { - const updatedHistory = history.filter((msg) => msg.chatId !== chatId); - updateHistory(updatedHistory); - showToast("Chat deleted successfully.", "success"); - } else { - showToast("Failed to delete chat.", "error"); - } - }; if (history.length === 0) { return ( @@ -250,7 +231,6 @@ export default function ChatHistory({ isLastMessage={isLastBotReply} saveEditedMessage={saveEditedMessage} forkThread={forkThread} - deleteMessage={deleteMessage} /> ); })} diff --git a/frontend/src/index.css b/frontend/src/index.css index 0d796116aa6..67fe55b7ecd 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -132,21 +132,17 @@ a { @media (prefers-color-scheme: light) { .fade-up-border { - background: linear-gradient( - to bottom, - rgba(220, 221, 223, 10%), - rgb(220, 221, 223) 89% - ); + background: linear-gradient(to bottom, + rgba(220, 221, 223, 10%), + rgb(220, 221, 223) 89%); } } @media (prefers-color-scheme: dark) { .fade-up-border { - background: linear-gradient( - to bottom, - rgba(41, 37, 36, 50%), - rgb(41 37 36) 90% - ); + background: linear-gradient(to bottom, + rgba(41, 37, 36, 50%), + rgb(41 37 36) 90%); } } @@ -273,7 +269,7 @@ a { height: 100px !important; } -.grid-loader > circle { +.grid-loader>circle { fill: #008eff; } @@ -336,11 +332,9 @@ dialog::backdrop { } .login-input-gradient { - background: linear-gradient( - 180deg, - rgba(61, 65, 71, 0.3) 0%, - rgba(44, 47, 53, 0.3) 100% - ) !important; + background: linear-gradient(180deg, + rgba(61, 65, 71, 0.3) 0%, + rgba(44, 47, 53, 0.3) 100%) !important; box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.25); } @@ -365,11 +359,11 @@ dialog::backdrop { top: 100%; } -.user-reply > div:first-of-type { +.user-reply>div:first-of-type { border: 2px solid white; } -.reply > *:last-child::after { +.reply>*:last-child::after { content: "|"; animation: blink 1.5s steps(1) infinite; color: white; @@ -450,7 +444,7 @@ dialog::backdrop { * ============================================== */ .markdown, -.markdown > * { +.markdown>* { font-weight: 400; } @@ -522,6 +516,7 @@ dialog::backdrop { } @media (max-width: 600px) { + .markdown table th, .markdown table td { padding: 10px; @@ -587,7 +582,7 @@ dialog::backdrop { text-decoration: underline; } -.markdown ul li > ul { +.markdown ul li>ul { padding-left: 20px; margin: 0px; } @@ -597,7 +592,7 @@ dialog::backdrop { margin: 0.35rem; } -.markdown > p > a, +.markdown>p>a, .markdown p a { text-decoration: underline; } @@ -681,7 +676,7 @@ does not extend the close button beyond the viewport. */ } /* Recharts rendering styles */ -.recharts-text > * { +.recharts-text>* { fill: #fff; } @@ -746,3 +741,30 @@ does not extend the close button beyond the viewport. */ .search-input::-webkit-search-cancel-button { filter: grayscale(100%) invert(1) brightness(100) opacity(0.5); } + +.animate-remove { + animation: flashAndShrink 800ms forwards; +} + +@keyframes flashAndShrink { + 0% { + height: inherit; + } + + 50% { + background-color: #664c4c; + opacity: 25%; + } + + 75% { + background-color: #664c4c; + opacity: 10%; + } + + 100% { + background-color: #664c4c; + height: 0px; + opacity: 0%; + display: none; + } +} \ No newline at end of file diff --git a/server/endpoints/system.js b/server/endpoints/system.js index 05a24e5cade..ffc5e8205b5 100644 --- a/server/endpoints/system.js +++ b/server/endpoints/system.js @@ -75,7 +75,7 @@ function systemEndpoints(app) { const results = await SystemSettings.currentSettings(); response.status(200).json({ results }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } }); @@ -98,7 +98,7 @@ function systemEndpoints(app) { response.sendStatus(200).end(); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -241,7 +241,7 @@ function systemEndpoints(app) { }); } } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } }); @@ -307,7 +307,7 @@ function systemEndpoints(app) { : await VectorDb.totalVectors(); response.status(200).json({ vectorCount }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -322,7 +322,7 @@ function systemEndpoints(app) { await purgeDocument(name); response.sendStatus(200).end(); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -337,7 +337,7 @@ function systemEndpoints(app) { for await (const name of names) await purgeDocument(name); response.sendStatus(200).end(); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -352,7 +352,7 @@ function systemEndpoints(app) { await purgeFolder(name); response.sendStatus(200).end(); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -366,7 +366,7 @@ function systemEndpoints(app) { const localFiles = await viewLocalFiles(); response.status(200).json({ localFiles }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -380,7 +380,7 @@ function systemEndpoints(app) { const online = await new CollectorApi().online(); response.sendStatus(online ? 200 : 503); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -399,7 +399,7 @@ function systemEndpoints(app) { response.status(200).json({ types }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -418,7 +418,7 @@ function systemEndpoints(app) { ); response.status(200).json({ newValues, error }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -452,7 +452,7 @@ function systemEndpoints(app) { } response.status(200).json({ success: !error, error }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -500,7 +500,7 @@ function systemEndpoints(app) { multi_user_mode: false, }); - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } } @@ -511,7 +511,7 @@ function systemEndpoints(app) { const multiUserMode = await SystemSettings.isMultiUserMode(); response.status(200).json({ multiUserMode }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.sendStatus(500).end(); } }); diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index 9a95d1b6cf3..8358bc0eaee 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -231,8 +231,8 @@ function workspaceEndpoints(app) { message: failedToEmbed.length > 0 ? `${failedToEmbed.length} documents failed to add.\n\n${errors - .map((msg) => `${msg}`) - .join("\n\n")}` + .map((msg) => `${msg}`) + .join("\n\n")}` : null, }); } catch (e) { @@ -779,11 +779,11 @@ function workspaceEndpoints(app) { // and is a valid thread slug. const threadId = !!threadSlug ? ( - await WorkspaceThread.get({ - slug: String(threadSlug), - workspace_id: workspace.id, - }) - )?.id ?? null + await WorkspaceThread.get({ + slug: String(threadSlug), + workspace_id: workspace.id, + }) + )?.id ?? null : null; const chatsToFork = await WorkspaceChats.where( { @@ -833,7 +833,7 @@ function workspaceEndpoints(app) { ); response.status(200).json({ newThreadSlug: newThread.slug }); } catch (e) { - console.log(e.message, e); + console.error(e.message, e); response.status(500).json({ message: "Internal server error" }); } } @@ -843,24 +843,18 @@ function workspaceEndpoints(app) { "/workspace/workspace-chats/:id", [validatedRequest, flexUserRoleValid([ROLES.all])], async (request, response) => { - const user = await userFromSession(request, response); try { const { id } = request.params; - const result = await WorkspaceChats._update(Number(id), { - include: false, - user_id: user?.id, + const user = await userFromSession(request, response); + const validChat = await WorkspaceChats.get({ + id: Number(id), + user_id: user?.id ?? null }); - - if (result) { - response.json({ success: true, error: null }); - } else { - response.status(404).json({ - success: false, - error: "Chat not found or user not authorized", - }); - } + if (!validChat) return response.status(404).json({ success: false, error: 'Chat not found.' }); + await WorkspaceChats._update(validChat.id, { include: false }); + response.json({ success: true, error: null }); } catch (e) { - console.error(e); + console.error(e.message, e); response.status(500).json({ success: false, error: "Server error" }); } } From d131a1c653c09879bbef57f2c3ab44ded171e105 Mon Sep 17 00:00:00 2001 From: timothycarambat Date: Wed, 3 Jul 2024 18:25:09 -0700 Subject: [PATCH 4/5] update animation linting remove element on animiation to prevent popin --- collector/utils/logger/index.js | 8 +-- .../Actions/DeleteMessage/index.jsx | 36 +++++++------ .../HistoricalMessage/Actions/index.jsx | 6 +-- .../ChatHistory/HistoricalMessage/index.jsx | 19 +++++-- frontend/src/index.css | 52 +++++++++---------- server/endpoints/workspaces.js | 22 ++++---- server/utils/logger/index.js | 3 +- 7 files changed, 81 insertions(+), 65 deletions(-) diff --git a/collector/utils/logger/index.js b/collector/utils/logger/index.js index 92878e68890..95c4764f284 100644 --- a/collector/utils/logger/index.js +++ b/collector/utils/logger/index.js @@ -5,7 +5,8 @@ class Logger { static _instance; constructor() { if (Logger._instance) return Logger._instance; - this.logger = process.env.NODE_ENV === 'production' ? this.getWinstonLogger() : console; + this.logger = + process.env.NODE_ENV === "production" ? this.getWinstonLogger() : console; Logger._instance = this; } @@ -19,8 +20,9 @@ class Logger { winston.format.colorize(), winston.format.printf( ({ level, message, service, origin = "" }) => { - return `\x1b[36m[${service}]\x1b[0m${origin ? `\x1b[33m[${origin}]\x1b[0m` : "" - } ${level}: ${message}`; + return `\x1b[36m[${service}]\x1b[0m${ + origin ? `\x1b[33m[${origin}]\x1b[0m` : "" + } ${level}: ${message}`; } ) ), diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx index 514c1b6da8a..61a61e164b2 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx @@ -4,20 +4,12 @@ import { useState, useEffect } from "react"; import { Tooltip } from "react-tooltip"; const DELETE_EVENT = "delete-message"; -export function useWatchDeleteMessage({ chatId = null }) { +export function useWatchDeleteMessage({ chatId = null, role = "user" }) { const [isDeleted, setIsDeleted] = useState(false); - - async function onDeleteEvent(e) { - if (e.detail.chatId === chatId) { - setIsDeleted(true); - await Workspace.deleteChat(chatId); - return false; - } - } + const [completeDelete, setCompleteDelete] = useState(false); useEffect(() => { function listenForEvent() { - console.log({ chatId }) if (!chatId) return; window.addEventListener(DELETE_EVENT, onDeleteEvent); } @@ -27,16 +19,30 @@ export function useWatchDeleteMessage({ chatId = null }) { }; }, [chatId]); - return { isDeleted }; + function onEndAnimation() { + if (!isDeleted) return; + setCompleteDelete(true); + } + + async function onDeleteEvent(e) { + if (e.detail.chatId === chatId) { + setIsDeleted(true); + // Do this to prevent double-emission of the PUT/DELETE api call + // because then there will be a race condition and it will make an error log for nothing + // as one call will complete and the other will fail. + if (role === "assistant") await Workspace.deleteChat(chatId); + return false; + } + } + + return { isDeleted, completeDelete, onEndAnimation }; } export function DeleteMessage({ chatId, isEditing, role }) { if (!chatId || isEditing || role === "user") return null; function emitDeleteEvent() { - window.dispatchEvent( - new CustomEvent(DELETE_EVENT, { detail: { chatId } }) - ); + window.dispatchEvent(new CustomEvent(DELETE_EVENT, { detail: { chatId } })); } return ( @@ -58,4 +64,4 @@ export function DeleteMessage({ chatId, isEditing, role }) { />
); -} \ No newline at end of file +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx index 715352de545..644c65660d2 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx @@ -52,11 +52,7 @@ const Actions = ({ chatId={chatId} /> )} - + {chatId && role !== "user" && !isEditing && ( <> { const { isEditing } = useEditMessage({ chatId, role }); - const { isDeleted } = useWatchDeleteMessage({ chatId }); + const { isDeleted, completeDelete, onEndAnimation } = useWatchDeleteMessage({ + chatId, + role, + }); const adjustTextArea = (event) => { const element = event.target; element.style.height = "auto"; @@ -38,8 +41,9 @@ const HistoricalMessage = ({ return (
@@ -59,11 +63,16 @@ const HistoricalMessage = ({ ); } + if (completeDelete) return null; return (
diff --git a/frontend/src/index.css b/frontend/src/index.css index 67fe55b7ecd..830d8ea672f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -132,17 +132,21 @@ a { @media (prefers-color-scheme: light) { .fade-up-border { - background: linear-gradient(to bottom, - rgba(220, 221, 223, 10%), - rgb(220, 221, 223) 89%); + background: linear-gradient( + to bottom, + rgba(220, 221, 223, 10%), + rgb(220, 221, 223) 89% + ); } } @media (prefers-color-scheme: dark) { .fade-up-border { - background: linear-gradient(to bottom, - rgba(41, 37, 36, 50%), - rgb(41 37 36) 90%); + background: linear-gradient( + to bottom, + rgba(41, 37, 36, 50%), + rgb(41 37 36) 90% + ); } } @@ -269,7 +273,7 @@ a { height: 100px !important; } -.grid-loader>circle { +.grid-loader > circle { fill: #008eff; } @@ -332,9 +336,11 @@ dialog::backdrop { } .login-input-gradient { - background: linear-gradient(180deg, - rgba(61, 65, 71, 0.3) 0%, - rgba(44, 47, 53, 0.3) 100%) !important; + background: linear-gradient( + 180deg, + rgba(61, 65, 71, 0.3) 0%, + rgba(44, 47, 53, 0.3) 100% + ) !important; box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.25); } @@ -359,11 +365,11 @@ dialog::backdrop { top: 100%; } -.user-reply>div:first-of-type { +.user-reply > div:first-of-type { border: 2px solid white; } -.reply>*:last-child::after { +.reply > *:last-child::after { content: "|"; animation: blink 1.5s steps(1) infinite; color: white; @@ -444,7 +450,7 @@ dialog::backdrop { * ============================================== */ .markdown, -.markdown>* { +.markdown > * { font-weight: 400; } @@ -516,7 +522,6 @@ dialog::backdrop { } @media (max-width: 600px) { - .markdown table th, .markdown table td { padding: 10px; @@ -582,7 +587,7 @@ dialog::backdrop { text-decoration: underline; } -.markdown ul li>ul { +.markdown ul li > ul { padding-left: 20px; margin: 0px; } @@ -592,7 +597,7 @@ dialog::backdrop { margin: 0.35rem; } -.markdown>p>a, +.markdown > p > a, .markdown p a { text-decoration: underline; } @@ -676,7 +681,7 @@ does not extend the close button beyond the viewport. */ } /* Recharts rendering styles */ -.recharts-text>* { +.recharts-text > * { fill: #fff; } @@ -743,28 +748,21 @@ does not extend the close button beyond the viewport. */ } .animate-remove { - animation: flashAndShrink 800ms forwards; + animation: fadeAndShrink 800ms forwards; } -@keyframes flashAndShrink { - 0% { - height: inherit; - } - +@keyframes fadeAndShrink { 50% { - background-color: #664c4c; opacity: 25%; } 75% { - background-color: #664c4c; opacity: 10%; } 100% { - background-color: #664c4c; height: 0px; opacity: 0%; display: none; } -} \ No newline at end of file +} diff --git a/server/endpoints/workspaces.js b/server/endpoints/workspaces.js index 8358bc0eaee..aa3ef19b099 100644 --- a/server/endpoints/workspaces.js +++ b/server/endpoints/workspaces.js @@ -231,8 +231,8 @@ function workspaceEndpoints(app) { message: failedToEmbed.length > 0 ? `${failedToEmbed.length} documents failed to add.\n\n${errors - .map((msg) => `${msg}`) - .join("\n\n")}` + .map((msg) => `${msg}`) + .join("\n\n")}` : null, }); } catch (e) { @@ -779,11 +779,11 @@ function workspaceEndpoints(app) { // and is a valid thread slug. const threadId = !!threadSlug ? ( - await WorkspaceThread.get({ - slug: String(threadSlug), - workspace_id: workspace.id, - }) - )?.id ?? null + await WorkspaceThread.get({ + slug: String(threadSlug), + workspace_id: workspace.id, + }) + )?.id ?? null : null; const chatsToFork = await WorkspaceChats.where( { @@ -848,9 +848,13 @@ function workspaceEndpoints(app) { const user = await userFromSession(request, response); const validChat = await WorkspaceChats.get({ id: Number(id), - user_id: user?.id ?? null + user_id: user?.id ?? null, }); - if (!validChat) return response.status(404).json({ success: false, error: 'Chat not found.' }); + if (!validChat) + return response + .status(404) + .json({ success: false, error: "Chat not found." }); + await WorkspaceChats._update(validChat.id, { include: false }); response.json({ success: true, error: null }); } catch (e) { diff --git a/server/utils/logger/index.js b/server/utils/logger/index.js index a0a256effdf..66d20a2a55c 100644 --- a/server/utils/logger/index.js +++ b/server/utils/logger/index.js @@ -5,7 +5,8 @@ class Logger { static _instance; constructor() { if (Logger._instance) return Logger._instance; - this.logger = process.env.NODE_ENV === 'production' ? this.getWinstonLogger() : console; + this.logger = + process.env.NODE_ENV === "production" ? this.getWinstonLogger() : console; Logger._instance = this; } From 6bac9ac32941eda8fa7314a1f5098d217fb780c5 Mon Sep 17 00:00:00 2001 From: timothycarambat Date: Wed, 3 Jul 2024 18:27:48 -0700 Subject: [PATCH 5/5] linting and cleanup imports --- .../HistoricalMessage/Actions/DeleteMessage/index.jsx | 4 ++-- .../ChatHistory/HistoricalMessage/Actions/index.jsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx index 61a61e164b2..262fdb3e7b9 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/DeleteMessage/index.jsx @@ -1,7 +1,7 @@ -import Workspace from "@/models/workspace"; -import { Trash } from "@phosphor-icons/react"; import { useState, useEffect } from "react"; +import { Trash } from "@phosphor-icons/react"; import { Tooltip } from "react-tooltip"; +import Workspace from "@/models/workspace"; const DELETE_EVENT = "delete-message"; export function useWatchDeleteMessage({ chatId = null, role = "user" }) { diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx index 644c65660d2..061f7724a0e 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/index.jsx @@ -7,7 +7,6 @@ import { ArrowsClockwise, Copy, GitMerge, - Trash, } from "@phosphor-icons/react"; import { Tooltip } from "react-tooltip"; import Workspace from "@/models/workspace";