From 5bfcd9e091ce2c3976f0a6af479a7e1cfc656f7c Mon Sep 17 00:00:00 2001 From: Cogent Apps <127109874+cogentapps@users.noreply.github.com> Date: Fri, 10 Mar 2023 14:00:37 -0800 Subject: [PATCH 1/3] initial redux work --- package.json | 3 + src/components/header.tsx | 77 +++++---- src/components/input.tsx | 38 +++-- src/components/markdown.tsx | 21 ++- src/components/message.tsx | 120 +++++++------- src/components/page.tsx | 2 +- src/components/pages/landing.tsx | 13 +- src/components/settings.tsx | 246 ---------------------------- src/components/settings/index.tsx | 113 +++++++++++++ src/components/settings/option.tsx | 13 ++ src/components/settings/options.tsx | 55 +++++++ src/components/settings/speech.tsx | 64 ++++++++ src/components/settings/tab.tsx | 64 ++++++++ src/components/settings/user.tsx | 30 ++++ src/context.tsx | 129 +++------------ src/index.tsx | 13 +- src/message-tree.ts | 14 ++ src/store/api-keys.ts | 33 ++++ src/store/index.ts | 35 ++++ src/store/message.ts | 22 +++ src/store/parameters.ts | 30 ++++ src/store/settings-ui.ts | 41 +++++ src/store/voices.ts | 23 +++ src/tts/defaults.ts | 49 ++++++ src/{ => tts}/elevenlabs.tsx | 75 ++------- 25 files changed, 794 insertions(+), 529 deletions(-) delete mode 100644 src/components/settings.tsx create mode 100644 src/components/settings/index.tsx create mode 100644 src/components/settings/option.tsx create mode 100644 src/components/settings/options.tsx create mode 100644 src/components/settings/speech.tsx create mode 100644 src/components/settings/tab.tsx create mode 100644 src/components/settings/user.tsx create mode 100644 src/store/api-keys.ts create mode 100644 src/store/index.ts create mode 100644 src/store/message.ts create mode 100644 src/store/parameters.ts create mode 100644 src/store/settings-ui.ts create mode 100644 src/store/voices.ts create mode 100644 src/tts/defaults.ts rename src/{ => tts}/elevenlabs.tsx (74%) diff --git a/package.json b/package.json index 3d36757..dad3e6c 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "@mantine/modals": "^5.10.5", "@mantine/notifications": "^5.10.5", "@mantine/spotlight": "^5.10.5", + "@reduxjs/toolkit": "^1.9.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -35,9 +36,11 @@ "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-markdown": "^8.0.5", + "react-redux": "^8.0.5", "react-router-dom": "^6.8.2", "react-scripts": "5.0.1", "react-syntax-highlighter": "^15.5.0", + "redux-persist": "^6.0.0", "rehype-katex": "^6.0.2", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", diff --git a/src/components/header.tsx b/src/components/header.tsx index c8dd200..86d85b0 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -2,12 +2,15 @@ import styled from '@emotion/styled'; import Helmet from 'react-helmet'; import { useSpotlight } from '@mantine/spotlight'; import { Button, ButtonProps } from '@mantine/core'; -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { APP_NAME } from '../values'; import { useAppContext } from '../context'; import { backend } from '../backend'; import { MenuItem, primaryMenu, secondaryMenu } from '../menus'; +import { useAppDispatch, useAppSelector } from '../store'; +import { selectOpenAIApiKey } from '../store/api-keys'; +import { setTab } from '../store/settings-ui'; const HeaderContainer = styled.div` display: flex; @@ -129,6 +132,8 @@ export default function Header(props: HeaderProps) { const navigate = useNavigate(); const spotlight = useSpotlight(); const [loading, setLoading] = useState(false); + const openAIApiKey = useAppSelector(selectOpenAIApiKey); + const dispatch = useAppDispatch(); const onNewChat = useCallback(async () => { setLoading(true); @@ -137,33 +142,37 @@ export default function Header(props: HeaderProps) { }, [navigate]); const openSettings = useCallback(() => { - context.settings.open(context.apiKeys.openai ? 'options' : 'user'); - }, [context]); + dispatch(setTab(openAIApiKey ? 'options' : 'user')); + }, [openAIApiKey]); - return - - {props.title ? `${props.title} - ` : ''}{APP_NAME} - Unofficial ChatGPT app - - {props.title &&

{props.title}

} - {!props.title && (

-
- {APP_NAME}
- An unofficial ChatGPT app -
-

)} -
- - - {backend && !props.share && props.canShare && typeof navigator.share !== 'undefined' && - Share - } - {backend && !context.authenticated && ( - backend.current?.signIn()}>Sign in to sync - )} - - New Chat - - ; + const header = useMemo(() => ( + + + {props.title ? `${props.title} - ` : ''}{APP_NAME} - Unofficial ChatGPT app + + {props.title &&

{props.title}

} + {!props.title && (

+
+ {APP_NAME}
+ An unofficial ChatGPT app +
+

)} +
+ + + {backend && !props.share && props.canShare && typeof navigator.share !== 'undefined' && + Share + } + {backend && !context.authenticated && ( + backend.current?.signIn()}>Sign in to sync + )} + + New Chat + + + ), [props.title, props.share, props.canShare, props.onShare, openSettings, onNewChat, loading, context.authenticated, spotlight.openSpotlight]); + + return header; } function SubHeaderMenuItem(props: { item: MenuItem }) { @@ -176,9 +185,13 @@ function SubHeaderMenuItem(props: { item: MenuItem }) { } export function SubHeader(props: any) { - return - {primaryMenu.map(item => )} -
- {secondaryMenu.map(item => )} - ; + const elem = useMemo(() => ( + + {primaryMenu.map(item => )} +
+ {secondaryMenu.map(item => )} + + ), []); + + return elem; } \ No newline at end of file diff --git a/src/components/input.tsx b/src/components/input.tsx index 32b1eb9..650b3bb 100644 --- a/src/components/input.tsx +++ b/src/components/input.tsx @@ -3,6 +3,10 @@ import { Button, ActionIcon, Textarea } from '@mantine/core'; import { useCallback, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; import { useAppContext } from '../context'; +import { useAppDispatch, useAppSelector } from '../store'; +import { selectMessage, setMessage } from '../store/message'; +import { selectTemperature } from '../store/parameters'; +import { openSystemPromptPanel, openTemperaturePanel } from '../store/settings-ui'; const Container = styled.div` background: #292933; @@ -40,18 +44,25 @@ export interface MessageInputProps { } export default function MessageInput(props: MessageInputProps) { + const temperature = useAppSelector(selectTemperature); + const message = useAppSelector(selectMessage); + const context = useAppContext(); + const dispatch = useAppDispatch(); + + const onCustomizeSystemPromptClick = useCallback(() => dispatch(openSystemPromptPanel()), []); + const onTemperatureClick = useCallback(() => dispatch(openTemperaturePanel()), []); + const onChange = useCallback((e: React.ChangeEvent) => { + dispatch(setMessage(e.target.value)); + }, []); + const pathname = useLocation().pathname; - const onChange = useCallback((e: React.ChangeEvent) => { - context.setMessage(e.target.value); - }, [context]); - const onSubmit = useCallback(async () => { - if (await context.onNewMessage(context.message)) { - context.setMessage(''); + if (await context.onNewMessage(message)) { + dispatch(setMessage('')); } - }, [context]); + }, [context, message, dispatch]); const onKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter' && e.shiftKey === false && !props.disabled) { @@ -71,9 +82,6 @@ export default function MessageInput(props: MessageInputProps) { ); }, [onSubmit, props.disabled]); - const openSystemPromptPanel = useCallback(() => context.settings.open('options', 'system-prompt'), [context.settings]); - const openTemperaturePanel = useCallback(() => context.settings.open('options', 'temperature'), [context.settings]); - const messagesToDisplay = context.currentChat.messagesToDisplay; const disabled = context.generating || messagesToDisplay[messagesToDisplay.length - 1]?.role === 'user' @@ -83,7 +91,7 @@ export default function MessageInput(props: MessageInputProps) { if (context.isShare || (!isLandingPage && !context.id)) { return null; } - + return