diff --git a/README.md b/README.md index dfc8933473..20853f6392 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,13 @@ Here are my planned/recently finished features. - [x] AI model change handling (start new convos within existing convo) - [x] Server convo pagination (limit fetch and load more with 'show more' button) - [ ] Config file for easy startup +- [ ] Conversation Search (by title) +- [ ] Resubmit/edit sent messages +- [ ] Semantic Search Option (requires more tokens) - [ ] Bing AI Styling (for suggested responses, convo end, etc.) -- [ ] Prompt Templates -- [ ] Conversation/Prompt Search +- [ ] Prompt Templates/Search - [ ] Refactor/clean up code (tech debt) - [ ] Mobile styling (half-finished) -- [ ] Semantic Search Option (requires more tokens) ### Features @@ -79,12 +80,18 @@ Here are my planned/recently finished features. ## Use Cases ## - ![use case example](./public/use_case.png "GPT is down! Plus is too expensive!") - One stop shop for all conversational AIs, with the added bonus of searching past conversations. - Using the official API, you'd have to generate 7.5 million words to expense the same cost as ChatGPT Plus ($20). - - ChatGPT Free is down. - ChatGPT/Google Bard/Bing AI conversations are lost in space or cannot be searched past a certain timeframe. + - ChatGPT Free (at [chat.openai.com](https://chat.openai.com/chat)) is more limited than the API + + ![use case example](./public/use_case2.png "chat.openai.com is getting more limited by the day!") + + - ChatGPT Free is down. + + ![use case example](./public/use_case.png "GPT is down! Plus is too expensive!") + ## Origin ## This project was originally created as a Minimum Viable Product (or MVP) for the [@HackReactor](https://github.com/hackreactor/) Bootcamp. It was built with OpenAI response streaming and most of the UI completed in under 20 hours. During the end of that time, I had most of the UI and basic functionality done. This was created without using any boilerplates or templates, including create-react-app and other toolchains. I didn't follow any 'un-official chatgpt' video tutorials, and simply referenced the official site for the UI. The purpose of the exercise was to learn setting up a full stack project from scratch. Please feel free to give feedback, suggestions, or fork the project for your own use. diff --git a/models/Conversation.js b/models/Conversation.js index e45ab68000..32adac4654 100644 --- a/models/Conversation.js +++ b/models/Conversation.js @@ -75,13 +75,14 @@ module.exports = { }, // getConvos: async () => await Conversation.find({}).sort({ created: -1 }).exec(), getConvos: async (pageNumber = 1, pageSize = 12) => { - // const skip = (pageNumber - 1) * pageSize; - const limit = pageNumber * pageSize; + const skip = (pageNumber - 1) * pageSize; + // const limit = pageNumber * pageSize; const conversations = await Conversation.find({}) .sort({ created: -1 }) - // .skip(skip) - .limit(limit) + .skip(skip) + // .limit(limit) + .limit(pageSize) .exec(); return conversations; diff --git a/package.json b/package.json index d6fab9eb41..e9d791cb4e 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,10 @@ "main": "index.js", "scripts": { "start": "webpack-dev-server .", - "build": "Webpack . --watch", - "server": "npx nodemon server/index.js", + "build": "Webpack .", + "server": "npx node server/index.js", + "build-dev": "Webpack . --watch", + "server-dev": "npx nodemon server/index.js", "test": "test" }, "repository": { diff --git a/public/use_case2.png b/public/use_case2.png new file mode 100644 index 0000000000..cd1d715c10 Binary files /dev/null and b/public/use_case2.png differ diff --git a/src/components/Models/ModelDialog.jsx b/src/components/Models/ModelDialog.jsx index b613734248..3de7370a26 100644 --- a/src/components/Models/ModelDialog.jsx +++ b/src/components/Models/ModelDialog.jsx @@ -16,7 +16,7 @@ import { DialogTitle } from '../ui/Dialog.tsx'; -export default function ModelDialog({ mutate }) { +export default function ModelDialog({ mutate, modelMap }) { const dispatch = useDispatch(); const [chatGptLabel, setChatGptLabel] = useState(''); const [promptPrefix, setPromptPrefix] = useState(''); @@ -50,7 +50,7 @@ export default function ModelDialog({ mutate }) { updateCustomGpt.trigger({ value, chatGptLabel, promptPrefix }); mutate(); - setSaveText('Saved!'); + setSaveText((prev) => prev + 'd!'); setTimeout(() => { setSaveText('Save'); }, 2500); @@ -60,6 +60,10 @@ export default function ModelDialog({ mutate }) { // dispatch(setDisabled(false)); }; + if (modelMap[chatGptLabel.toLowerCase()] && saveText === 'Save') { + setSaveText('Update'); + } + const requiredProp = required ? { required: true } : {}; return ( diff --git a/src/components/Models/ModelMenu.jsx b/src/components/Models/ModelMenu.jsx index 180354c51d..a53472f9fb 100644 --- a/src/components/Models/ModelMenu.jsx +++ b/src/components/Models/ModelMenu.jsx @@ -40,15 +40,12 @@ export default function ModelMenu() { }); useEffect(() => { + trigger(); const lastSelected = JSON.parse(localStorage.getItem('model')); if (lastSelected && lastSelected !== 'chatgptCustom' && initial[lastSelected]) { dispatch(setModel(lastSelected)); } - const cachedModels = JSON.parse(localStorage.getItem('models')); - if (cachedModels) { - dispatch(setModels(cachedModels)); - } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -56,10 +53,6 @@ export default function ModelMenu() { localStorage.setItem('model', JSON.stringify(model)); }, [model]); - useEffect(() => { - localStorage.setItem('models', JSON.stringify(models.slice(4))); - }, [models]); - const onChange = (value) => { if (!value) { return; @@ -140,7 +133,7 @@ export default function ModelMenu() { - + ); } diff --git a/src/components/Nav/index.jsx b/src/components/Nav/index.jsx index db85b7076c..6316973680 100644 --- a/src/components/Nav/index.jsx +++ b/src/components/Nav/index.jsx @@ -6,15 +6,19 @@ import NavLinks from './NavLinks'; import useDidMountEffect from '~/hooks/useDidMountEffect'; import { swr } from '~/utils/fetchers'; import { useDispatch, useSelector } from 'react-redux'; -import { incrementPage } from '~/store/convoSlice'; +import { incrementPage, setConvos } from '~/store/convoSlice'; export default function Nav() { const dispatch = useDispatch(); const [isHovering, setIsHovering] = useState(false); - const { conversationId, pageNumber } = useSelector((state) => state.convo); + const { conversationId, convos, pageNumber } = useSelector((state) => state.convo); + const onSuccess = (data) => { + dispatch(setConvos(data)); + }; + const { data, isLoading, mutate } = swr( `http://localhost:3050/convos?pageNumber=${pageNumber}` - ); + , onSuccess); const containerRef = useRef(null); const scrollPositionRef = useRef(null); @@ -63,7 +67,7 @@ export default function Nav() { ) : ( - {!showingTemplates && ( + {/* {!showingTemplates && (
)} - {!!showingTemplates && } + {!!showingTemplates && } */}
diff --git a/src/store/convoSlice.js b/src/store/convoSlice.js index 15e73c1119..5918dfec7b 100644 --- a/src/store/convoSlice.js +++ b/src/store/convoSlice.js @@ -27,8 +27,10 @@ const currentSlice = createSlice({ }, incrementPage: (state) => { state.pageNumber = state.pageNumber + 1; - } - // setConvos: (state, action) => state.convos = action.payload, + }, + setConvos: (state, action) => { + state.convos = [...state.convos, ...action.payload]; + }, } });