refactor(Settings.jsx) move tabs to separate components, rewrite General in TS, fix react warnings

chore(client): remove unnecessary dependencies from package.json
feat(client): add General tab to Settings component
feat(client): add CogIcon component
refactor(client): move General tab content to separate file
fix(client): fix clearConvosMutation call in Settings component

feat(svg): add CogIcon component
refactor(conversation.js): add useCallback hook to newConversation function
refactor(conversations.js): add useCallback hook to refreshConversations function
chore(tsconfig.json): change jsx option to 'preserve'
This commit is contained in:
Daniel Avila 2023-06-11 13:45:28 -04:00 committed by Danny Avila
parent f171628948
commit 544d72ee1d
10 changed files with 134 additions and 125 deletions

View file

@ -39,10 +39,6 @@
"@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0",
"@types/jest": "^29.5.0",
"@types/node": "^20.2.3",
"@types/react": "^18.0.30",
"@types/react-dom": "^18.0.11",
"@zattoo/use-double-click": "1.2.0",
"axios": "^1.3.4",
"class-variance-authority": "^0.6.0",
@ -94,10 +90,10 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.13",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.0.11",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.0",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
"@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.13",
"babel-jest": "^29.5.0",

View file

@ -1,34 +1,18 @@
import { Dialog } from '../ui/Dialog.tsx';
import * as Tabs from '@radix-ui/react-tabs';
import { DialogContent, DialogHeader, DialogTitle } from '../ui/Dialog.tsx';
import { useEffect, useState, useContext } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/Dialog.tsx';
import { General } from './SettingsTabs/';
import { CogIcon } from '~/components/svg';
import { useEffect, useState } from 'react';
import { cn } from '~/utils/';
import { useClearConversationsMutation } from '~/data-provider';
import { ThemeContext } from '~/hooks/ThemeContext';
import store from '~/store';
import { CheckIcon } from 'lucide-react';
export default function Settings({ open, onOpenChange }) {
const { theme, setTheme } = useContext(ThemeContext);
const { newConversation } = store.useConversation();
const { refreshConversations } = store.useConversations();
const clearConvosMutation = useClearConversationsMutation();
const [isMobile, setIsMobile] = useState(false);
const [confirmClear, setConfirmClear] = useState(false);
const clearConvos = () => {
if (confirmClear) {
console.log('Clearing conversations...');
clearConvosMutation.mutate();
setConfirmClear(false);
} else {
setConfirmClear(true);
}
};
const changeTheme = (e) => {
setTheme(e.target.value);
};
const [isMobile, setIsMobile] = useState(false);
// check if mobile dynamically and update
useEffect(() => {
@ -46,10 +30,10 @@ export default function Settings({ open, onOpenChange }) {
useEffect(() => {
if (clearConvosMutation.isSuccess) {
newConversation();
refreshConversations();
newConversation();
}
}, [clearConvosMutation.isSuccess]);
}, [clearConvosMutation.isSuccess, newConversation, refreshConversations]);
useEffect(() => {
// If the user clicks in the dialog when confirmClear is true, set it to false
@ -71,7 +55,9 @@ export default function Settings({ open, onOpenChange }) {
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className={cn('shadow-2xl dark:bg-gray-900 dark:text-white')}>
<DialogHeader>
<DialogTitle className="text-gray-800 dark:text-white">Settings</DialogTitle>
<DialogTitle className="text-lg font-medium leading-6 text-gray-900 dark:text-gray-200">
Settings
</DialogTitle>
</DialogHeader>
<div className="px-6">
<Tabs.Root
@ -97,71 +83,11 @@ export default function Settings({ open, onOpenChange }) {
)}
value="general"
>
<svg
stroke="currentColor"
fill="currentColor"
strokeWidth="0"
viewBox="0 0 20 20"
className="group-radix-state-active:fill-white h-4 h-5 w-4 w-5 fill-white dark:fill-gray-500"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
clipRule="evenodd"
/>
</svg>
<CogIcon />
General
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="general" role="tabpanel" className="w-full md:min-h-[300px]">
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="flex items-center justify-between">
<div>Theme</div>
<select
className="w-24 rounded border border-black/10 bg-transparent text-sm dark:border-white/20 dark:bg-gray-900"
onChange={changeTheme}
value={theme}
>
<option value="system">System</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</div>
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="flex items-center justify-between">
<div>Clear all chats</div>
<button
className="btn relative bg-red-600 text-white hover:bg-red-800"
type="button"
id="clearConvosBtn"
onClick={clearConvos}
>
{confirmClear ? (
<div
className="flex w-full items-center justify-center gap-2"
id="clearConvosTxt"
>
<CheckIcon className="h-5 w-5" /> Confirm Clear
</div>
) : (
<div
className="flex w-full items-center justify-center gap-2"
id="clearConvosTxt"
>
Clear
</div>
)}
</button>
</div>
</div>
</div>
</Tabs.Content>
<General />
</Tabs.Root>
</div>
</DialogContent>

View file

@ -0,0 +1,77 @@
import * as Tabs from '@radix-ui/react-tabs';
import { CheckIcon } from 'lucide-react';
import { ThemeContext } from '~/hooks/ThemeContext';
import React, { useState, useContext, useCallback } from 'react';
import { useClearConversationsMutation } from '~/data-provider';
const ThemeSelector = ({ theme, onChange }: { theme: string, onChange: (value: string) => void }) => (
<div className="flex items-center justify-between">
<div>Theme</div>
<select
className="w-24 rounded border border-black/10 bg-transparent text-sm dark:border-white/20 dark:bg-gray-900"
onChange={(e) => onChange(e.target.value)}
value={theme}
>
<option value="system">System</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</div>
);
const ClearChatsButton = ({ confirmClear, onClick }: { confirmClear: boolean, onClick: () => void }) => (
<div className="flex items-center justify-between">
<div>Clear all chats</div>
<button
className="btn relative bg-red-600 text-white hover:bg-red-800"
type="button"
id="clearConvosBtn"
onClick={onClick}
>
{confirmClear ? (
<div className="flex w-full items-center justify-center gap-2" id="clearConvosTxt">
<CheckIcon className="h-5 w-5" /> Confirm Clear
</div>
) : (
<div className="flex w-full items-center justify-center gap-2" id="clearConvosTxt">
Clear
</div>
)}
</button>
</div>
);
function General() {
const { theme, setTheme } = useContext(ThemeContext);
const clearConvosMutation = useClearConversationsMutation();
const [confirmClear, setConfirmClear] = useState(false);
const clearConvos = useCallback(() => {
if (confirmClear) {
console.log('Clearing conversations...');
clearConvosMutation.mutate({});
setConfirmClear(false);
} else {
setConfirmClear(true);
}
}, [confirmClear, clearConvosMutation]);
const changeTheme = useCallback((value: string) => {
setTheme(value);
}, [setTheme]);
return (
<Tabs.Content value="general" role="tabpanel" className="w-full md:min-h-[300px]" >
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-300">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ThemeSelector theme={theme} onChange={changeTheme} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<ClearChatsButton confirmClear={confirmClear} onClick={clearConvos} />
</div>
</div>
</Tabs.Content>
);
}
export default React.memo(General);

View file

@ -0,0 +1 @@
export { default as General } from './General';

View file

@ -0,0 +1,22 @@
import * as React from 'react'
export default function CogIcon() {
return (
<svg
stroke="currentColor"
fill="currentColor"
strokeWidth="0"
viewBox="0 0 20 20"
className="group-radix-state-active:fill-white h-4 h-5 w-4 w-5 fill-white dark:fill-gray-500"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
clipRule="evenodd"
/>
</svg>
);
}

View file

@ -1,4 +1,5 @@
export { default as Plugin } from './Plugin';
export { default as GPTIcon } from './GPTIcon';
export { default as BingIcon } from './BingIcon';
export { default as CogIcon } from './CogIcon';
export { default as MessagesSquared } from './MessagesSquared';

View file

@ -1,4 +1,5 @@
import endpoints from './endpoints';
import { useCallback } from 'react';
import {
atom,
selector,
@ -112,7 +113,7 @@ const useConversation = () => {
);
const newConversation = (template = {}, preset) => {
const newConversation = useCallback((template = {}, preset) => {
switchToConversation(
{
conversationId: 'new',
@ -122,7 +123,7 @@ const useConversation = () => {
[],
preset
);
};
}, [switchToConversation]);
const searchPlaceholderConversation = () => {
switchToConversation(

View file

@ -1,4 +1,5 @@
import { atom, useSetRecoilState } from 'recoil';
import { useCallback } from 'react';
const refreshConversationsHint = atom({
key: 'refreshConversationsHint',
@ -8,7 +9,9 @@ const refreshConversationsHint = atom({
const useConversations = () => {
const setRefreshConversationsHint = useSetRecoilState(refreshConversationsHint);
const refreshConversations = () => setRefreshConversationsHint((prevState) => prevState + 1);
const refreshConversations = useCallback(() => {
setRefreshConversationsHint((prevState) => prevState + 1);
}, [setRefreshConversationsHint]);
return { refreshConversations };
};

View file

@ -17,7 +17,7 @@
"noEmit": false,
"declaration": true,
"declarationDir": "./types",
"jsx": "react-jsx",
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"~/*": ["./src/*"]

36
package-lock.json generated
View file

@ -342,10 +342,6 @@
"@radix-ui/react-tabs": "^1.0.3",
"@tailwindcss/forms": "^0.5.3",
"@tanstack/react-query": "^4.28.0",
"@types/jest": "^29.5.0",
"@types/node": "^20.2.3",
"@types/react": "^18.0.30",
"@types/react-dom": "^18.0.11",
"@zattoo/use-double-click": "1.2.0",
"axios": "^1.3.4",
"class-variance-authority": "^0.6.0",
@ -397,10 +393,10 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.13",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.0.11",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.0",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
"@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.13",
"babel-jest": "^29.5.0",
@ -433,12 +429,6 @@
"vite-plugin-html": "^3.2.0"
}
},
"client/node_modules/@types/node": {
"version": "18.16.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.17.tgz",
"integrity": "sha512-QAkjjRA1N7gPJeAP4WLXZtYv6+eMXFNviqktCDt4GLcmCugMr5BcRHfkOjCQzvCsnMp+L79a54zBkbw356xv9Q==",
"dev": true
},
"node_modules/@adobe/css-tools": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz",
@ -32056,7 +32046,7 @@
"jsonwebtoken": "^9.0.0",
"keyv": "^4.5.2",
"keyv-file": "^0.2.0",
"langchain": "0.0.92",
"langchain": "^0.0.92",
"lodash": "^4.17.21",
"meilisearch": "^0.33.0",
"mongoose": "^7.1.1",
@ -32145,10 +32135,10 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.13",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.0.11",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.0",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
"@vitejs/plugin-react": "^4.0.0",
"@zattoo/use-double-click": "1.2.0",
"autoprefixer": "^10.4.13",
@ -32215,14 +32205,6 @@
"url": "^0.11.0",
"vite": "^4.3.9",
"vite-plugin-html": "^3.2.0"
},
"dependencies": {
"@types/node": {
"version": "18.16.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.17.tgz",
"integrity": "sha512-QAkjjRA1N7gPJeAP4WLXZtYv6+eMXFNviqktCDt4GLcmCugMr5BcRHfkOjCQzvCsnMp+L79a54zBkbw356xv9Q==",
"dev": true
}
}
},
"cheerio": {