diff --git a/api/package.json b/api/package.json index 8311456f41..71f38cc780 100644 --- a/api/package.json +++ b/api/package.json @@ -24,6 +24,7 @@ "@waylaidwanderer/chatgpt-api": "^1.28.2", "axios": "^1.3.4", "cors": "^2.8.5", + "crypto": "^1.0.1", "dotenv": "^16.0.3", "express": "^4.18.2", "express-session": "^1.17.3", diff --git a/client/src/components/Main/Footer.jsx b/client/src/components/Main/Footer.jsx index 21e6fff89a..0015596749 100644 --- a/client/src/components/Main/Footer.jsx +++ b/client/src/components/Main/Footer.jsx @@ -2,7 +2,7 @@ import React from 'react'; export default function Footer() { return ( -
+
+

ChatGPT Clone diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx index 8d7db4e71a..4459645d2a 100644 --- a/client/src/components/Main/TextChat.jsx +++ b/client/src/components/Main/TextChat.jsx @@ -35,7 +35,7 @@ export default function TextChat({ messages }) { const { error, latestMessage } = convo; const { ask, regenerate, stopGenerating } = useMessageHandler(); - const isNotAppendable = (!isSubmitting && latestMessage?.submitting) || latestMessage?.error; + const isNotAppendable = latestMessage?.cancelled || latestMessage?.error; // auto focus to input, when enter a conversation. useEffect(() => { @@ -43,7 +43,6 @@ export default function TextChat({ messages }) { }, [convo?.conversationId]); const messageHandler = (data, currentState, currentMsg) => { - const { messages, _currentMsg, message, sender, isRegenerate } = currentState; if (isRegenerate) @@ -75,6 +74,38 @@ export default function TextChat({ messages }) { ); }; + const cancelHandler = (data, currentState, currentMsg) => { + const { messages, _currentMsg, message, sender, isRegenerate } = currentState; + + if (isRegenerate) + dispatch( + setMessages([ + ...messages, + { + sender, + text: data, + parentMessageId: message?.overrideParentMessageId, + messageId: message?.overrideParentMessageId + '_', + cancelled: true + } + ]) + ); + else + dispatch( + setMessages([ + ...messages, + currentMsg, + { + sender, + text: data, + parentMessageId: currentMsg?.messageId, + messageId: currentMsg?.messageId + '_', + cancelled: true + } + ]) + ); + }; + const createdHandler = (data, currentState, currentMsg) => { const { conversationId } = currentMsg; dispatch( @@ -93,9 +124,11 @@ export default function TextChat({ messages }) { const { model, chatGptLabel, promptPrefix } = message; if (isRegenerate) dispatch(setMessages([...messages, responseMessage])); else dispatch(setMessages([...messages, requestMessage, responseMessage])); + dispatch(setSubmitState(false)); const isBing = model === 'bingai' || model === 'sydney'; + // refresh title if (requestMessage.parentMessageId == '00000000-0000-0000-0000-000000000000') { setTimeout(() => { dispatch(refreshConversation()); @@ -163,8 +196,6 @@ export default function TextChat({ messages }) { }) ); } - - dispatch(setSubmitState(false)); }; const errorHandler = (data, currentState, currentMsg) => { @@ -193,7 +224,9 @@ export default function TextChat({ messages }) { } const currentState = submission; + let currentMsg = { ...currentState.message }; + let latestResponseText = ''; const { server, payload } = createPayload(submission); const onMessage = (e) => { @@ -216,6 +249,7 @@ export default function TextChat({ messages }) { console.log(data); } if (data.message) { + latestResponseText = text; messageHandler(text, currentState, currentMsg); } // console.log('dataStream', data); @@ -233,6 +267,10 @@ export default function TextChat({ messages }) { events.onmessage = onMessage; + events.oncancel = (e) => { + cancelHandler(latestResponseText, currentState, currentMsg); + }; + events.onerror = function (e) { console.log('error in opening conn.'); events.close(); @@ -245,9 +283,13 @@ export default function TextChat({ messages }) { events.stream(); return () => { - dispatch(setSubmitState(false)); events.removeEventListener('message', onMessage); + const isCancelled = events.readyState <= 1; events.close(); + if (isCancelled) { + const e = new Event('cancel'); + events.dispatchEvent(e); + } }; }, [submission]); @@ -290,9 +332,9 @@ export default function TextChat({ messages }) { const changeHandler = (e) => { const { value } = e.target; - if (isSubmitting && (value === '' || value === '\n')) { - return; - } + // if (isSubmitting && (value === '' || value === '\n')) { + // return; + // } dispatch(setText(value)); }; @@ -301,20 +343,8 @@ export default function TextChat({ messages }) { dispatch(setError(false)); }; - const placeholder = () => { - if (disabled && isSubmitting) { - return 'Choose another model or customize GPT again'; - } else if (!isSubmitting && latestMessage?.submitting) { - return 'Message in progress...'; - // } else if (latestMessage?.error) { - // return 'Error...'; - } else { - return ''; - } - }; - return ( -
+
@@ -352,13 +382,19 @@ export default function TextChat({ messages }) { ref={inputRef} // style={{maxHeight: '200px', height: '24px', overflowY: 'hidden'}} rows="1" - value={text} + value={disabled || isNotAppendable ? '' : text} onKeyUp={handleKeyUp} onKeyDown={handleKeyDown} onChange={changeHandler} onCompositionStart={handleCompositionStart} onCompositionEnd={handleCompositionEnd} - placeholder={placeholder()} + placeholder={ + disabled + ? 'Choose another model or customize GPT again' + : isNotAppendable + ? 'Edit your message or Regenerate.' + : '' + } disabled={disabled || isNotAppendable} className="m-0 h-auto max-h-52 resize-none overflow-auto border-0 bg-transparent p-0 pl-12 pr-8 leading-6 placeholder:text-sm focus:outline-none focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:pl-8" /> diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx index 28b2fbfeda..114096b2fe 100644 --- a/client/src/components/Messages/index.jsx +++ b/client/src/components/Messages/index.jsx @@ -63,7 +63,7 @@ export default function Messages({ messages, messageTree }) { return (
diff --git a/client/src/components/Models/ModelMenu.jsx b/client/src/components/Models/ModelMenu.jsx index 300df6abba..9bc8aa5fdd 100644 --- a/client/src/components/Models/ModelMenu.jsx +++ b/client/src/components/Models/ModelMenu.jsx @@ -38,7 +38,8 @@ export default function ModelMenu() { const { data, isLoading, mutate } = swr(`/api/customGpts`, (res) => { const fetchedModels = res.map((modelItem) => ({ ...modelItem, - name: modelItem.chatGptLabel + name: modelItem.chatGptLabel, + model: 'chatgptCustom' })); dispatch(setModels(fetchedModels)); @@ -100,7 +101,7 @@ export default function ModelMenu() { localStorage.setItem('model', JSON.stringify(model)); }, [model]); - const filteredModels = models.filter(({model, _id }) => initial[model] || _id.length > 1 ); + const filteredModels = models.filter(({model, _id }) => initial[model] ); const onChange = (value) => { if (!value) { diff --git a/client/src/components/Nav/MobileNav.jsx b/client/src/components/Nav/MobileNav.jsx index 0c846524fc..558c88f1a4 100644 --- a/client/src/components/Nav/MobileNav.jsx +++ b/client/src/components/Nav/MobileNav.jsx @@ -23,7 +23,7 @@ export default function MobileNav({ setNavVisible }) { } return ( -
+