From 5af5a97d8fd10adec43f13e5670c092be970ac5c Mon Sep 17 00:00:00 2001 From: Daniel Avila Date: Sat, 11 Feb 2023 10:22:15 -0500 Subject: [PATCH] complete renaming functions, abstracts more svg, sets title to current convo title, adds a try again feature to errors --- models/Conversation.js | 12 ++ models/Prompt.js | 52 +++++++++ package-lock.json | 62 ++++++++-- package.json | 1 + server/index.js | 24 +++- src/App.jsx | 3 +- .../Conversations/Conversation copy.jsx | 108 ++++++++++++++++++ src/components/Conversations/Conversation.jsx | 79 ++++++++++--- src/components/Conversations/DeleteButton.jsx | 8 +- src/components/Conversations/RenameButton.jsx | 24 ++-- src/components/Nav/NewChat.jsx | 2 +- src/components/Prompts/Prompt.jsx | 20 ++++ .../{main => Prompts}/Templates.jsx | 2 +- src/components/main/Landing.jsx | 6 +- src/components/main/Messages.jsx | 7 +- src/components/main/Regenerate.jsx | 38 +++--- src/components/main/TextChat.jsx | 10 +- src/components/svg/CheckMark.jsx | 20 ++++ src/components/svg/ConvoIcon.jsx | 20 ++++ src/components/svg/CrossIcon.jsx | 31 +++++ src/components/svg/RegenerateIcon.jsx | 22 ++++ src/components/svg/RenameIcon.jsx | 21 ++++ src/hooks/useDocumentTitle.js | 20 ++++ src/store/convoSlice.js | 2 +- 24 files changed, 512 insertions(+), 82 deletions(-) create mode 100644 models/Prompt.js create mode 100644 src/components/Conversations/Conversation copy.jsx create mode 100644 src/components/Prompts/Prompt.jsx rename src/components/{main => Prompts}/Templates.jsx (97%) create mode 100644 src/components/svg/CheckMark.jsx create mode 100644 src/components/svg/ConvoIcon.jsx create mode 100644 src/components/svg/CrossIcon.jsx create mode 100644 src/components/svg/RegenerateIcon.jsx create mode 100644 src/components/svg/RenameIcon.jsx create mode 100644 src/hooks/useDocumentTitle.js diff --git a/models/Conversation.js b/models/Conversation.js index d964f1874c..c0387f54f1 100644 --- a/models/Conversation.js +++ b/models/Conversation.js @@ -44,6 +44,18 @@ module.exports = { return { message: 'Error saving conversation' }; } }, + updateConvo: async ({ conversationId, ...update }) => { + try { + return await Conversation.findOneAndUpdate( + { conversationId }, + update, + { new: true } + ).exec(); + } catch (error) { + console.log(error); + return { message: 'Error updating conversation' }; + } + }, getConvos: async () => await Conversation.find({}).exec(), deleteConvos: async (filter) => { diff --git a/models/Prompt.js b/models/Prompt.js new file mode 100644 index 0000000000..612278d038 --- /dev/null +++ b/models/Prompt.js @@ -0,0 +1,52 @@ +const mongoose = require('mongoose'); + +const promptSchema = mongoose.Schema({ + title: { + type: String, + required: true + }, + prompt: { + type: String, + required: true + }, + category: { + type: String, + }, + created: { + type: Date, + default: Date.now + } +}); + +const Prompt = mongoose.models.Prompt || mongoose.model('Prompt', promptSchema); + +module.exports = { + savePrompt: async ({ title, prompt }) => { + try { + await Prompt.create({ + title, + prompt + }); + return { title, prompt }; + } catch (error) { + console.error(error); + return { prompt: 'Error saving prompt' }; + } + }, + getPrompts: async (filter) => { + try { + return await Prompt.find(filter).exec() + } catch (error) { + console.error(error); + return { prompt: 'Error getting prompts' }; + } + }, + deletePrompts: async (filter) => { + try { + return await Prompt.deleteMany(filter).exec() + } catch (error) { + console.error(error); + return { prompt: 'Error deleting prompts' }; + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0d2a121aab..c859f1d7b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "openai": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-helmet": "^6.1.0", "react-redux": "^8.0.5", "react-textarea-autosize": "^8.4.0", "swr": "^2.0.3", @@ -16361,8 +16362,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "optional": true, - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -16372,9 +16371,7 @@ "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "optional": true, - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -16603,6 +16600,25 @@ "react": "^18.2.0" } }, + "node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -16759,6 +16775,14 @@ "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-textarea-autosize": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", @@ -32185,8 +32209,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "optional": true, - "peer": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -32196,9 +32218,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "optional": true, - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, @@ -32369,6 +32389,22 @@ "scheduler": "^0.23.0" } }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + } + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -32487,6 +32523,12 @@ "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" } }, + "react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "requires": {} + }, "react-textarea-autosize": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", diff --git a/package.json b/package.json index 3525b27baf..b01f3230af 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "openai": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-helmet": "^6.1.0", "react-redux": "^8.0.5", "react-textarea-autosize": "^8.4.0", "swr": "^2.0.3", diff --git a/server/index.js b/server/index.js index e4efa46c20..2469632fb0 100644 --- a/server/index.js +++ b/server/index.js @@ -2,7 +2,8 @@ const express = require('express'); const dbConnect = require('../models/dbConnect'); const { ask, titleConversation } = require('../app/chatgpt'); const { saveMessage, getMessages, deleteMessages } = require('../models/Message'); -const { saveConvo, getConvos, deleteConvos } = require('../models/Conversation'); +const { saveConvo, getConvos, deleteConvos, updateConvo } = require('../models/Conversation'); +const { savePrompt, getPrompts, deletePrompts } = require('../models/Prompt'); const crypto = require('crypto'); const path = require('path'); const cors = require('cors'); @@ -25,6 +26,15 @@ app.get('/convos', async (req, res) => { res.status(200).send(await getConvos()); }); +app.get('/prompts', async (req, res) => { + let filter = {}; + // const { search } = req.body.arg; + // if (!!search) { + // filter = { conversationId }; + // } + res.status(200).send(await getPrompts(filter)); +}); + app.get('/messages/:conversationId', async (req, res) => { const { conversationId } = req.params; res.status(200).send(await getMessages({ conversationId })); @@ -46,6 +56,18 @@ app.post('/clear_convos', async (req, res) => { } }); +app.post('/update_convo', async (req, res) => { + const update = req.body.arg; + + try { + const dbResponse = await updateConvo(update); + res.status(201).send(dbResponse); + } catch (error) { + console.error(error); + res.status(500).send(error); + } +}); + app.post('/ask', async (req, res) => { console.log(req.body); const { text, parentMessageId, conversationId } = req.body; diff --git a/src/App.jsx b/src/App.jsx index 9efa7c0128..6352197fdd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,6 +10,7 @@ import useDidMountEffect from './hooks/useDidMountEffect'; const App = () => { const { messages } = useSelector((state) => state.messages); const { conversationId } = useSelector((state) => state.convo); + const { title } = useSelector((state) => state.convo); const { data, error, isLoading, mutate } = swr('http://localhost:3050/convos'); useDidMountEffect(() => mutate(), [conversationId]); @@ -19,7 +20,7 @@ const App = () => {
- +
*/ +} + +export default function Conversation({ id, parentMessageId, title = 'New conversation' }) { + const dispatch = useDispatch(); + const { conversationId } = useSelector((state) => state.convo); + const { trigger, isMutating } = manualSWR(`http://localhost:3050/messages/${id}`, 'get'); + + const clickHandler = async () => { + if (conversationId === id) { + return; + } + + dispatch(setConversation({ error: false, conversationId: id, parentMessageId })); + const data = await trigger(); + dispatch(setMessages(data)); + dispatch(setText('')); + }; + + const aProps = { + className: + 'animate-flash group relative flex cursor-pointer items-center gap-3 break-all rounded-md bg-gray-800 py-3 px-3 pr-14 hover:bg-gray-800' + }; + + if (conversationId !== id) { + aProps.className = + 'group relative flex cursor-pointer items-center gap-3 break-all rounded-md py-3 px-3 hover:bg-[#2A2B32] hover:pr-4'; + } + + return ( +
+ + + + +
+ + +
+
+ ); +} diff --git a/src/components/Conversations/Conversation.jsx b/src/components/Conversations/Conversation.jsx index 53c2c56aac..4dfa96006b 100644 --- a/src/components/Conversations/Conversation.jsx +++ b/src/components/Conversations/Conversation.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useRef } from 'react'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; import { useSelector, useDispatch } from 'react-redux'; @@ -6,23 +6,56 @@ import { setConversation } from '~/store/convoSlice'; import { setMessages } from '~/store/messageSlice'; import { setText } from '~/store/textSlice'; import manualSWR from '~/utils/fetchers'; +import ConvoIcon from '../svg/ConvoIcon'; export default function Conversation({ id, parentMessageId, title = 'New conversation' }) { + const [renaming, setRenaming] = useState(false); + const [titleInput, setTitleInput] = useState(title); + const inputRef = useRef(null); const dispatch = useDispatch(); const { conversationId } = useSelector((state) => state.convo); const { trigger, isMutating } = manualSWR(`http://localhost:3050/messages/${id}`, 'get'); + const rename = manualSWR(`http://localhost:3050/update_convo`, 'post'); const clickHandler = async () => { if (conversationId === id) { return; } - dispatch(setConversation({ error: false, conversationId: id, parentMessageId })); + dispatch(setConversation({ title, error: false, conversationId: id, parentMessageId })); const data = await trigger(); dispatch(setMessages(data)); dispatch(setText('')); }; + const renameHandler = (e) => { + e.preventDefault(); + setRenaming(true); + setTimeout(() => { + inputRef.current.focus(); + }, 25); + }; + + const cancelHandler = (e) => { + e.preventDefault(); + setRenaming(false); + }; + + const onRename = (e) => { + e.preventDefault(); + setRenaming(false); + if (titleInput === title) { + return; + } + rename.trigger({ conversationId, title: titleInput }); + }; + + const handleKeyPress = (e) => { + if (e.key === 'Enter') { + onRename(e); + } + }; + const aProps = { className: 'animate-flash group relative flex cursor-pointer items-center gap-3 break-all rounded-md bg-gray-800 py-3 px-3 pr-14 hover:bg-gray-800' @@ -38,27 +71,35 @@ export default function Conversation({ id, parentMessageId, title = 'New convers onClick={() => clickHandler()} {...aProps} > - - - +
- {title} + {renaming === true ? ( + setTitleInput(e.target.value)} + onBlur={onRename} + onKeyPress={handleKeyPress} + /> + ) : ( + title + )}
{conversationId === id ? (
- - + +
) : (
diff --git a/src/components/Conversations/DeleteButton.jsx b/src/components/Conversations/DeleteButton.jsx index 7fd38fd69f..64ea642c32 100644 --- a/src/components/Conversations/DeleteButton.jsx +++ b/src/components/Conversations/DeleteButton.jsx @@ -1,11 +1,12 @@ import React from 'react'; import TrashIcon from '../svg/TrashIcon'; +import CrossIcon from '../svg/CrossIcon'; import manualSWR from '~/utils/fetchers'; import { useDispatch } from 'react-redux'; import { setConversation } from '~/store/convoSlice'; import { setMessages } from '~/store/messageSlice'; -export default function DeleteButton({ conversationId }) { +export default function DeleteButton({ conversationId, renaming, cancelHandler }) { const dispatch = useDispatch(); const { trigger, isMutating } = manualSWR( 'http://localhost:3050/clear_convos', @@ -17,13 +18,14 @@ export default function DeleteButton({ conversationId }) { ); const clickHandler = () => trigger({ conversationId }); + const handler = renaming ? cancelHandler : clickHandler; return ( ); } diff --git a/src/components/Conversations/RenameButton.jsx b/src/components/Conversations/RenameButton.jsx index 362dfb6c09..d13a5e5db6 100644 --- a/src/components/Conversations/RenameButton.jsx +++ b/src/components/Conversations/RenameButton.jsx @@ -1,23 +1,13 @@ import React from 'react'; +import RenameIcon from '../svg/RenameIcon'; +import CheckMark from '../svg/CheckMark'; + +export default function RenameButton({ onClick, renaming, renameHandler, onRename }) { + const handler = renaming ? onRename : renameHandler; -export default function RenameButton({ onClick, disabled }) { return ( - ); } diff --git a/src/components/Nav/NewChat.jsx b/src/components/Nav/NewChat.jsx index c0131a933a..8535995b13 100644 --- a/src/components/Nav/NewChat.jsx +++ b/src/components/Nav/NewChat.jsx @@ -10,7 +10,7 @@ export default function NewChat() { const clickHandler = () => { dispatch(setText('')); dispatch(setMessages([])); - dispatch(setConversation({ error: false, conversationId: null, parentMessageId: null })); + dispatch(setConversation({ title: 'New Chat', error: false, conversationId: null, parentMessageId: null })); }; return ( diff --git a/src/components/Prompts/Prompt.jsx b/src/components/Prompts/Prompt.jsx new file mode 100644 index 0000000000..588c24d2d7 --- /dev/null +++ b/src/components/Prompts/Prompt.jsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function Prompt({ title, prompt, id }) { + return ( +
+

+ { title } +

+ + Use prompt → +
+ ); +} diff --git a/src/components/main/Templates.jsx b/src/components/Prompts/Templates.jsx similarity index 97% rename from src/components/main/Templates.jsx rename to src/components/Prompts/Templates.jsx index 6dd7102791..409fa1c98d 100644 --- a/src/components/main/Templates.jsx +++ b/src/components/Prompts/Templates.jsx @@ -23,7 +23,7 @@ export default function Templates() {

DAN (Do Anything Now) diff --git a/src/components/main/Landing.jsx b/src/components/main/Landing.jsx index 950827166d..a53929d2ec 100644 --- a/src/components/main/Landing.jsx +++ b/src/components/main/Landing.jsx @@ -1,13 +1,15 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { setText } from '~/store/textSlice'; -import Templates from './Templates'; +import useDocumentTitle from '~/hooks/useDocumentTitle'; +import Templates from '../Prompts/Templates'; import SunIcon from '../svg/SunIcon'; import LightningIcon from '../svg/LightningIcon'; import CautionIcon from '../svg/CautionIcon'; -export default function Landing() { +export default function Landing({ title }) { const dispatch = useDispatch(); + useDocumentTitle(title); const clickHandler = (e) => { e.preventDefault(); diff --git a/src/components/main/Messages.jsx b/src/components/main/Messages.jsx index 3210f3c10d..e274563bb7 100644 --- a/src/components/main/Messages.jsx +++ b/src/components/main/Messages.jsx @@ -1,12 +1,15 @@ import React, { useEffect, useRef } from 'react'; +import useDocumentTitle from '~/hooks/useDocumentTitle'; import Message from './Message'; import Landing from './Landing'; -export default function Messages({ messages }) { +export default function Messages({ messages, title }) { if (messages.length === 0) { - return ; + return ; } + useDocumentTitle(title); + const messagesEndRef = useRef(null); const scrollToBottom = () => { diff --git a/src/components/main/Regenerate.jsx b/src/components/main/Regenerate.jsx index 0b85da4936..6c0eb573cb 100644 --- a/src/components/main/Regenerate.jsx +++ b/src/components/main/Regenerate.jsx @@ -1,6 +1,7 @@ import React from 'react'; +import RegenerateIcon from '../svg/RegenerateIcon'; -export default function Regenerate({ submitMessage }) { +export default function Regenerate({ submitMessage, tryAgain }) { const clickHandler = (e) => { e.preventDefault(); submitMessage(); @@ -8,31 +9,22 @@ export default function Regenerate({ submitMessage }) { return ( <> - + There was an error generating a response - + + Regenerate response + + + ); } diff --git a/src/components/main/TextChat.jsx b/src/components/main/TextChat.jsx index 26a9c3aa47..7ab532cc71 100644 --- a/src/components/main/TextChat.jsx +++ b/src/components/main/TextChat.jsx @@ -88,6 +88,11 @@ export default function TextChat({ messages, reloadConvos }) { dispatch(setText(value)); }; + const tryAgain = (e) => { + e.preventDefault(); + dispatch(setError(false)); + }; + return (
@@ -97,7 +102,10 @@ export default function TextChat({ messages, reloadConvos }) { {/* removed this prop shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] */} {!!error ? ( - + ) : (
+ + + ); +} diff --git a/src/components/svg/ConvoIcon.jsx b/src/components/svg/ConvoIcon.jsx new file mode 100644 index 0000000000..7f682bb928 --- /dev/null +++ b/src/components/svg/ConvoIcon.jsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function ConvoIcon() { + return ( + + + + ); +} diff --git a/src/components/svg/CrossIcon.jsx b/src/components/svg/CrossIcon.jsx new file mode 100644 index 0000000000..ecc1edffda --- /dev/null +++ b/src/components/svg/CrossIcon.jsx @@ -0,0 +1,31 @@ +import React from 'react'; + +export default function CrossIcon() { + return ( + + + + + ); +} diff --git a/src/components/svg/RegenerateIcon.jsx b/src/components/svg/RegenerateIcon.jsx new file mode 100644 index 0000000000..6c982bb0c2 --- /dev/null +++ b/src/components/svg/RegenerateIcon.jsx @@ -0,0 +1,22 @@ +import React from 'react'; + +export default function Regenerate() { + return ( + + + + + + ); +} diff --git a/src/components/svg/RenameIcon.jsx b/src/components/svg/RenameIcon.jsx new file mode 100644 index 0000000000..4936c07a73 --- /dev/null +++ b/src/components/svg/RenameIcon.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +export default function RenameIcon() { + return ( + + + + + ); +} diff --git a/src/hooks/useDocumentTitle.js b/src/hooks/useDocumentTitle.js new file mode 100644 index 0000000000..10162a15f9 --- /dev/null +++ b/src/hooks/useDocumentTitle.js @@ -0,0 +1,20 @@ +// useDocumentTitle.js +import { useRef, useEffect } from 'react'; + +function useDocumentTitle(title, prevailOnUnmount = false) { + const defaultTitle = useRef(document.title); + + useEffect(() => { + document.title = title; + }, [title]); + + useEffect( + () => () => { + if (!prevailOnUnmount) { + document.title = defaultTitle.current; + } + }, [] + ); +} + +export default useDocumentTitle; diff --git a/src/store/convoSlice.js b/src/store/convoSlice.js index 994dbd2379..00cb14b7c5 100644 --- a/src/store/convoSlice.js +++ b/src/store/convoSlice.js @@ -1,8 +1,8 @@ import { createSlice } from '@reduxjs/toolkit'; const initialState = { - active: false, error: false, + title: 'ChatGPT Clone', conversationId: null, parentMessageId: null, };