feat: settings dialog and moved theme settings to dialog from sidebar (#570)
* feat: settings dialog and moved theme settings to dialog from sidebar. * chore(locale): move some labels to settings from sidebar
This commit is contained in:
parent
836c24680b
commit
ba1dacb899
|
@ -1,10 +1,17 @@
|
|||
import { IconFileExport, IconMoon, IconSun } from '@tabler/icons-react';
|
||||
import { useContext } from 'react';
|
||||
import {
|
||||
IconFileExport,
|
||||
IconMoon,
|
||||
IconSettings,
|
||||
IconSun,
|
||||
} from '@tabler/icons-react';
|
||||
import { useContext, useState } from 'react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import HomeContext from '@/pages/api/home/home.context';
|
||||
|
||||
import { SettingDialog } from '@/components/Settings/SettingDialog';
|
||||
|
||||
import { Import } from '../../Settings/Import';
|
||||
import { Key } from '../../Settings/Key';
|
||||
import { SidebarButton } from '../../Sidebar/SidebarButton';
|
||||
|
@ -14,6 +21,7 @@ import { PluginKeys } from './PluginKeys';
|
|||
|
||||
export const ChatbarSettings = () => {
|
||||
const { t } = useTranslation('sidebar');
|
||||
const [isSettingDialogOpen, setIsSettingDialog] = useState<boolean>(false);
|
||||
|
||||
const {
|
||||
state: {
|
||||
|
@ -49,16 +57,9 @@ export const ChatbarSettings = () => {
|
|||
/>
|
||||
|
||||
<SidebarButton
|
||||
text={lightMode === 'light' ? t('Dark mode') : t('Light mode')}
|
||||
icon={
|
||||
lightMode === 'light' ? <IconMoon size={18} /> : <IconSun size={18} />
|
||||
}
|
||||
onClick={() =>
|
||||
homeDispatch({
|
||||
field: 'lightMode',
|
||||
value: lightMode === 'light' ? 'dark' : 'light',
|
||||
})
|
||||
}
|
||||
text={t('Settings')}
|
||||
icon={<IconSettings size={18} />}
|
||||
onClick={() => setIsSettingDialog(true)}
|
||||
/>
|
||||
|
||||
{!serverSideApiKeyIsSet ? (
|
||||
|
@ -66,6 +67,13 @@ export const ChatbarSettings = () => {
|
|||
) : null}
|
||||
|
||||
{!serverSidePluginKeysSet ? <PluginKeys /> : null}
|
||||
|
||||
<SettingDialog
|
||||
open={isSettingDialogOpen}
|
||||
onClose={() => {
|
||||
setIsSettingDialog(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
import { FC, useContext, useEffect, useReducer, useRef } from 'react';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
import { useCreateReducer } from '@/hooks/useCreateReducer';
|
||||
|
||||
import { getSettings, saveSettings } from '@/utils/app/settings';
|
||||
|
||||
import { Settings } from '@/types/settings';
|
||||
|
||||
import HomeContext from '@/pages/api/home/home.context';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const SettingDialog: FC<Props> = ({ open, onClose }) => {
|
||||
const { t } = useTranslation('settings');
|
||||
const settings: Settings = getSettings();
|
||||
const { state, dispatch } = useCreateReducer<Settings>({
|
||||
initialState: settings,
|
||||
});
|
||||
const { dispatch: homeDispatch } = useContext(HomeContext);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
||||
window.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (e: MouseEvent) => {
|
||||
window.removeEventListener('mouseup', handleMouseUp);
|
||||
onClose();
|
||||
};
|
||||
|
||||
window.addEventListener('mousedown', handleMouseDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousedown', handleMouseDown);
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
const handleSave = () => {
|
||||
homeDispatch({ field: 'lightMode', value: state.theme });
|
||||
saveSettings(state);
|
||||
};
|
||||
|
||||
// Render nothing if the dialog is not open.
|
||||
if (!open) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
// Render the dialog.
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||
<div className="fixed inset-0 z-10 overflow-hidden">
|
||||
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="hidden sm:inline-block sm:h-screen sm:align-middle"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<div
|
||||
ref={modalRef}
|
||||
className="dark:border-netural-400 inline-block max-h-[400px] transform overflow-y-auto rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:max-h-[600px] sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
|
||||
role="dialog"
|
||||
>
|
||||
<div className="text-lg pb-4 font-bold text-black dark:text-neutral-200">
|
||||
{t('Settings')}
|
||||
</div>
|
||||
|
||||
<div className="text-sm font-bold mb-2 text-black dark:text-neutral-200">
|
||||
{t('Theme')}
|
||||
</div>
|
||||
|
||||
<select
|
||||
className="w-full cursor-pointer bg-transparent p-2 text-neutral-700 dark:text-neutral-200"
|
||||
value={state.theme}
|
||||
onChange={(event) =>
|
||||
dispatch({ field: 'theme', value: event.target.value })
|
||||
}
|
||||
>
|
||||
<option value="dark">{t('Dark mode')}</option>
|
||||
<option value="light">{t('Light mode')}</option>
|
||||
</select>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="w-full px-4 py-2 mt-6 border rounded-lg shadow border-neutral-500 text-neutral-900 hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
|
||||
onClick={() => {
|
||||
handleSave();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -23,6 +23,7 @@ import {
|
|||
} from '@/utils/app/conversation';
|
||||
import { saveFolders } from '@/utils/app/folders';
|
||||
import { savePrompts } from '@/utils/app/prompts';
|
||||
import { getSettings } from '@/utils/app/settings';
|
||||
|
||||
import { Conversation } from '@/types/chat';
|
||||
import { KeyValuePair } from '@/types/data';
|
||||
|
@ -68,7 +69,7 @@ const Home = ({
|
|||
conversations,
|
||||
selectedConversation,
|
||||
prompts,
|
||||
temperature
|
||||
temperature,
|
||||
},
|
||||
dispatch,
|
||||
} = contextValue;
|
||||
|
@ -250,9 +251,12 @@ const Home = ({
|
|||
// ON LOAD --------------------------------------------
|
||||
|
||||
useEffect(() => {
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
dispatch({ field: 'lightMode', value: theme as 'dark' | 'light' });
|
||||
const settings = getSettings();
|
||||
if (settings.theme) {
|
||||
dispatch({
|
||||
field: 'lightMode',
|
||||
value: settings.theme,
|
||||
});
|
||||
}
|
||||
|
||||
const apiKey = localStorage.getItem('apiKey');
|
||||
|
@ -419,6 +423,7 @@ export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
|
|||
'sidebar',
|
||||
'markdown',
|
||||
'promptbar',
|
||||
'settings',
|
||||
])),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "الوضع الداكن",
|
||||
"Light mode": "الوضع الفاتح"
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "ডার্ক মোড",
|
||||
"Light mode": "লাইট মোড"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "আলাপচারিতা ইমপোর্ট",
|
||||
"Are you sure?": "আপনি কি নিশ্চিত?",
|
||||
"Clear conversations": "কথোপকথন পরিষ্কার করুন",
|
||||
"Export data": "আলাপচারিতা এক্সপোর্ট",
|
||||
"Dark mode": "ডার্ক মোড",
|
||||
"Light mode": "লাইট মোড"
|
||||
"Export data": "আলাপচারিতা এক্সপোর্ট"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Dark Mode",
|
||||
"Light mode": "Light Mode"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Konversationen importieren",
|
||||
"Are you sure?": "Bist du sicher?",
|
||||
"Clear conversations": "Konversationen löschen",
|
||||
"Export data": "Konversationen exportieren",
|
||||
"Dark mode": "Dark Mode",
|
||||
"Light mode": "Light Mode"
|
||||
"Export data": "Konversationen exportieren"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Modo oscuro",
|
||||
"Light mode": "Modo claro"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importar conversaciones",
|
||||
"Are you sure?": "¿Estás seguro?",
|
||||
"Clear conversations": "Borrar conversaciones",
|
||||
"Export data": "Exportar conversaciones",
|
||||
"Dark mode": "Modo oscuro",
|
||||
"Light mode": "Modo claro"
|
||||
"Export data": "Exportar conversaciones"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Mode sombre",
|
||||
"Light mode": "Mode clair"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importer des conversations",
|
||||
"Are you sure?": "Êtes-vous sûr ?",
|
||||
"Clear conversations": "Effacer les conversations",
|
||||
"Export data": "Exporter les conversations",
|
||||
"Dark mode": "Mode sombre",
|
||||
"Light mode": "Mode clair"
|
||||
"Export data": "Exporter les conversations"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "מצב כהה",
|
||||
"Light mode": "מצב בהיר"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "ייבוא שיחות",
|
||||
"Are you sure?": "אתה בטוח?",
|
||||
"Clear conversations": "ניקוי שיחות",
|
||||
"Export data": "ייצוא שיחות",
|
||||
"Dark mode": "מצב כהה",
|
||||
"Light mode": "מצב בהיר"
|
||||
"Export data": "ייצוא שיחות"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Mode gelap",
|
||||
"Light mode": "Mode terang"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Impor percakapan",
|
||||
"Are you sure?": "Apakah Anda yakin?",
|
||||
"Clear conversations": "Hapus percakapan",
|
||||
"Export data": "Ekspor percakapan",
|
||||
"Dark mode": "Mode gelap",
|
||||
"Light mode": "Mode terang"
|
||||
"Export data": "Ekspor percakapan"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Modalità scura",
|
||||
"Light mode": "Modalità chiara"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importa dati",
|
||||
"Are you sure?": "Sei sicuro?",
|
||||
"Clear conversations": "Elimina conversazioni",
|
||||
"Export data": "Esporta dati",
|
||||
"Dark mode": "Modalità scura",
|
||||
"Light mode": "Modalità chiara"
|
||||
"Export data": "Esporta dati"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"Settings": "設定",
|
||||
"Theme": "テーマ",
|
||||
"Save": "保存",
|
||||
"Dark mode": "ダークモード",
|
||||
"Light mode": "ライトモード"
|
||||
}
|
|
@ -9,5 +9,6 @@
|
|||
"Clear conversations": " 会話をクリア",
|
||||
"Export data": "会話履歴をエクスポート",
|
||||
"Dark mode": "ダークモード",
|
||||
"Light mode": "ライトモード"
|
||||
"Light mode": "ライトモード",
|
||||
"Settings": "設定"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "다크 모드",
|
||||
"Light mode": "라이트 모드"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "대화 가져오기",
|
||||
"Are you sure?": "확실합니까?",
|
||||
"Clear conversations": "대화 지우기",
|
||||
"Export data": "대화 내보내기",
|
||||
"Dark mode": "다크 모드",
|
||||
"Light mode": "라이트 모드"
|
||||
"Export data": "대화 내보내기"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Tryb ciemny",
|
||||
"Light mode": "Tryb jasny"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Import rozmów",
|
||||
"Are you sure?": "Czy jesteś pewien?",
|
||||
"Clear conversations": "Usuń rozmowy",
|
||||
"Export data": "Eksport rozmów",
|
||||
"Dark mode": "Tryb ciemny",
|
||||
"Light mode": "Tryb jasny"
|
||||
"Export data": "Eksport rozmów"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Modo escuro",
|
||||
"Light mode": "Modo claro"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importar conversas",
|
||||
"Are you sure?": "Tem certeza?",
|
||||
"Clear conversations": "Apagar conversas",
|
||||
"Export data": "Exportar conversas",
|
||||
"Dark mode": "Modo escuro",
|
||||
"Light mode": "Modo claro"
|
||||
"Export data": "Exportar conversas"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Modul întunecat",
|
||||
"Light mode": "Modul de golire"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importați conversații",
|
||||
"Are you sure?": "Esti sigur?",
|
||||
"Clear conversations": "Ștergeți conversațiile",
|
||||
"Export data": "Exportați conversații",
|
||||
"Dark mode": "Modul întunecat",
|
||||
"Light mode": "Modul de golire"
|
||||
}
|
||||
"Export data": "Exportați conversații"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Темный режим",
|
||||
"Light mode": "Светлый режим"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Импортировать чаты",
|
||||
"Are you sure?": "Вы уверены?",
|
||||
"Clear conversations": "Удалить чаты",
|
||||
"Export data": "Экспортировать чаты",
|
||||
"Dark mode": "Темный режим",
|
||||
"Light mode": "Светлый режим"
|
||||
"Export data": "Экспортировать чаты"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "අඳුරු මාදිලිය",
|
||||
"Light mode": "ආලෝක මාදිලිය"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "සංවාද ආයාත කරන්න",
|
||||
"Are you sure?": "ඔබට විශ්වාස ද?",
|
||||
"Clear conversations": "සංවාද මකන්න",
|
||||
"Export data": "සංවාද නිර්යාත කරන්න",
|
||||
"Dark mode": "අඳුරු මාදිලිය",
|
||||
"Light mode": "ආලෝක මාදිලිය"
|
||||
"Export data": "සංවාද නිර්යාත කරන්න"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Mörkt läge",
|
||||
"Light mode": "Ljust läge"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Importera konversationer",
|
||||
"Are you sure?": "Är du säker?",
|
||||
"Clear conversations": "Radera konversationer",
|
||||
"Export data": "Exportera konversationer",
|
||||
"Dark mode": "Mörkt läge",
|
||||
"Light mode": "Ljust läge"
|
||||
"Export data": "Exportera konversationer"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "డార్క్ మోడ్",
|
||||
"Light mode": "లైట్ మోడ్"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "సంభాషణలు దిగుమతి చేయండి",
|
||||
"Are you sure?": "మీరు ఖచ్చితంగా ఉన్నారా?",
|
||||
"Clear conversations": "సంభాషణలు తొలగించు",
|
||||
"Export data": "సంభాషణలు ఎగుమతి చేయండి",
|
||||
"Dark mode": "డార్క్ మోడ్",
|
||||
"Light mode": "లైట్ మోడ్"
|
||||
"Export data": "సంభాషణలు ఎగుమతి చేయండి"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "Chế độ tối",
|
||||
"Light mode": "Chế độ sáng"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "Nhập dữ liệu hội thoại",
|
||||
"Are you sure?": "Bạn chắc chắn chứ?",
|
||||
"Clear conversations": "Xoá các đoạn hội thoại",
|
||||
"Export data": "Xuất dữ liệu hội thoại",
|
||||
"Dark mode": "Chế độ tối",
|
||||
"Light mode": "Chế độ sáng"
|
||||
"Export data": "Xuất dữ liệu hội thoại"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"Dark mode": "深色模式",
|
||||
"Light mode": "浅色模式"
|
||||
}
|
|
@ -7,7 +7,5 @@
|
|||
"Import data": "导入对话",
|
||||
"Are you sure?": "确定吗?",
|
||||
"Clear conversations": "清空对话",
|
||||
"Export data": "导出对话",
|
||||
"Dark mode": "深色模式",
|
||||
"Light mode": "浅色模式"
|
||||
"Export data": "导出对话"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export interface Settings {
|
||||
theme: 'light' | 'dark';
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { Settings } from '@/types/settings';
|
||||
|
||||
const STORAGE_KEY = 'settings';
|
||||
|
||||
export const getSettings = (): Settings => {
|
||||
let settings: Settings = {
|
||||
theme: 'dark',
|
||||
};
|
||||
const settingsJson = localStorage.getItem(STORAGE_KEY);
|
||||
if (settingsJson) {
|
||||
try {
|
||||
let savedSettings = JSON.parse(settingsJson) as Settings;
|
||||
settings = Object.assign(settings, savedSettings);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
};
|
||||
|
||||
export const saveSettings = (settings: Settings) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
};
|
Loading…
Reference in New Issue