|
import { useRecoilState } from 'recoil'; |
|
import * as Tabs from '@radix-ui/react-tabs'; |
|
import React, { useState, useContext, useEffect, useCallback, useRef } from 'react'; |
|
import { useClearConversationsMutation } from 'librechat-data-provider'; |
|
import { |
|
ThemeContext, |
|
useLocalize, |
|
useOnClickOutside, |
|
useConversation, |
|
useConversations, |
|
} from '~/hooks'; |
|
import type { TDangerButtonProps } from '~/common'; |
|
import DangerButton from './DangerButton'; |
|
import store from '~/store'; |
|
import useLocalStorage from '~/hooks/useLocalStorage'; |
|
|
|
export const ThemeSelector = ({ |
|
theme, |
|
onChange, |
|
}: { |
|
theme: string; |
|
onChange: (value: string) => void; |
|
}) => { |
|
const localize = useLocalize(); |
|
|
|
return ( |
|
<div className="flex items-center justify-between"> |
|
<div>{localize('com_nav_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">{localize('com_nav_theme_system')}</option> |
|
<option value="dark">{localize('com_nav_theme_dark')}</option> |
|
<option value="light">{localize('com_nav_theme_light')}</option> |
|
</select> |
|
</div> |
|
); |
|
}; |
|
|
|
export const ClearChatsButton = ({ |
|
confirmClear, |
|
className = '', |
|
showText = true, |
|
mutation, |
|
onClick, |
|
}: Pick< |
|
TDangerButtonProps, |
|
'confirmClear' | 'mutation' | 'className' | 'showText' | 'onClick' |
|
>) => { |
|
return ( |
|
<DangerButton |
|
id="clearConvosBtn" |
|
mutation={mutation} |
|
confirmClear={confirmClear} |
|
className={className} |
|
showText={showText} |
|
infoTextCode="com_nav_clear_all_chats" |
|
actionTextCode="com_ui_clear" |
|
confirmActionTextCode="com_nav_confirm_clear" |
|
dataTestIdInitial="clear-convos-initial" |
|
dataTestIdConfirm="clear-convos-confirm" |
|
onClick={onClick} |
|
/> |
|
); |
|
}; |
|
|
|
export const LangSelector = ({ |
|
langcode, |
|
onChange, |
|
}: { |
|
langcode: string; |
|
onChange: (value: string) => void; |
|
}) => { |
|
const localize = useLocalize(); |
|
|
|
return ( |
|
<div className="flex items-center justify-between"> |
|
<div>{localize('com_nav_language')}</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={langcode} |
|
> |
|
<option value="auto">{localize('com_nav_lang_auto')}</option> |
|
<option value="en-US">{localize('com_nav_lang_english')}</option> |
|
<option value="zh-CN">{localize('com_nav_lang_chinese')}</option> |
|
<option value="zh-TC">{localize('com_nav_lang_traditionalchinese')}</option> |
|
<option value="de-DE">{localize('com_nav_lang_german')}</option> |
|
<option value="es-ES">{localize('com_nav_lang_spanish')}</option> |
|
<option value="fr-FR">{localize('com_nav_lang_french')}</option> |
|
<option value="it-IT">{localize('com_nav_lang_italian')}</option> |
|
<option value="pl-PL">{localize('com_nav_lang_polish')}</option> |
|
<option value="pt-BR">{localize('com_nav_lang_brazilian_portuguese')}</option> |
|
<option value="ru-RU">{localize('com_nav_lang_russian')}</option> |
|
<option value="ja-JP">{localize('com_nav_lang_japanese')}</option> |
|
<option value="sv-SE">{localize('com_nav_lang_swedish')}</option> |
|
<option value="ko-KR">{localize('com_nav_lang_korean')}</option> |
|
</select> |
|
</div> |
|
); |
|
}; |
|
|
|
function General() { |
|
const { theme, setTheme } = useContext(ThemeContext); |
|
const clearConvosMutation = useClearConversationsMutation(); |
|
const [confirmClear, setConfirmClear] = useState(false); |
|
const [langcode, setLangcode] = useRecoilState(store.lang); |
|
const [selectedLang, setSelectedLang] = useLocalStorage('selectedLang', langcode); |
|
const { newConversation } = useConversation(); |
|
const { refreshConversations } = useConversations(); |
|
|
|
const contentRef = useRef(null); |
|
useOnClickOutside(contentRef, () => confirmClear && setConfirmClear(false), []); |
|
|
|
useEffect(() => { |
|
if (clearConvosMutation.isSuccess) { |
|
newConversation(); |
|
refreshConversations(); |
|
} |
|
}, [clearConvosMutation.isSuccess, newConversation, refreshConversations]); |
|
|
|
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], |
|
); |
|
|
|
const changeLang = useCallback( |
|
(value: string) => { |
|
setSelectedLang(value); |
|
if (value === 'auto') { |
|
const userLang = navigator.language || navigator.languages[0]; |
|
setLangcode(userLang); |
|
localStorage.setItem('lang', userLang); |
|
} else { |
|
setLangcode(value); |
|
localStorage.setItem('lang', value); |
|
} |
|
}, |
|
[setLangcode, setSelectedLang], |
|
); |
|
|
|
return ( |
|
<Tabs.Content |
|
value="general" |
|
role="tabpanel" |
|
className="w-full md:min-h-[300px]" |
|
ref={contentRef} |
|
> |
|
<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"> |
|
<LangSelector langcode={selectedLang} onChange={changeLang} /> |
|
</div> |
|
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700"> |
|
<ClearChatsButton |
|
confirmClear={confirmClear} |
|
onClick={clearConvos} |
|
showText={true} |
|
mutation={clearConvosMutation} |
|
/> |
|
</div> |
|
</div> |
|
</Tabs.Content> |
|
); |
|
} |
|
|
|
export default React.memo(General); |
|
|