Skip to content

Commit

Permalink
✨ feat: Add Settings Intercepting Routes (lobehub#2346)
Browse files Browse the repository at this point in the history
* ♻️ refactor: Refactor Setting Layout

* ♻️ refactor: Update useQueryRoute and add test

* ✅ test: Update test

* ✨ feat: Add Settings Intercepting Routes

* ♻️ refactor: Refactor Setting Layout

* ♻️ refactor: Update useQueryRoute and add test

* ✅ test: Update test

* 🐛 fix: Fix i18n

* 💄 style: Fix style

* 🐛 fix: Fix review problem

* 🐛 fix: Fix review problem

* ✅ test: Add useInterceptingRoutes.test

* 🐛 fix: Fix config

* 🐛 fix: Fix some merge problem

* 🐛 fix: Fix modal layout

* 🐛 fix: Fix cubox

* 🐛 fix: Fix cubox

* ✅ test: Fix test

* 🐛 fix: Fix redirect

* 🐛 fix: Fix review problem

---------

Co-authored-by: Arvin Xu <arvinx@foxmail.com>
  • Loading branch information
canisminor1990 and arvinxx authored May 4, 2024
1 parent 6946733 commit 29b6442
Show file tree
Hide file tree
Showing 53 changed files with 683 additions and 112 deletions.
1 change: 1 addition & 0 deletions locales/ar/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "تقديم ملاحظات",
"follow": "تابعنا على {{name}}",
"fullscreen": "وضع كامل الشاشة",
"historyRange": "نطاق التاريخ",
"import": "استيراد الإعدادات",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/bg-BG/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Обратна връзка",
"follow": "Следете ни на {{name}}",
"fullscreen": "Цял екран",
"historyRange": "Диапазон на историята",
"import": "Импортирай конфигурация",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/de-DE/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback und Vorschläge",
"follow": "Folge uns auf {{name}}",
"fullscreen": "Vollbildmodus",
"historyRange": "Verlaufsbereich",
"import": "Importieren",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/en-US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback",
"follow": "Follow us on {{name}}",
"fullscreen": "Full Screen Mode",
"historyRange": "History Range",
"import": "Import Configuration",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/es-ES/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Comentarios y sugerencias",
"follow": "Síguenos en {{name}}",
"fullscreen": "Pantalla completa",
"historyRange": "Rango de historial",
"import": "Importar configuración",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/fr-FR/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Retour d'information et suggestions",
"follow": "Suivez-nous sur {{name}}",
"fullscreen": "Mode plein écran",
"historyRange": "Plage d'historique",
"import": "Importer",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/it-IT/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback e suggerimenti",
"follow": "Seguici su {{name}}",
"fullscreen": "Modalità a schermo intero",
"historyRange": "Intervallo cronologico",
"import": "Importa configurazione",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "フィードバック",
"follow": " {{name}} で私たちをフォローする",
"fullscreen": "フルスクリーンモード",
"historyRange": "履歴範囲",
"import": "インポート",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/ko-KR/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "피드백 및 제안",
"follow": "{{name}}에서 우리를 팔로우하세요",
"fullscreen": "전체 화면",
"historyRange": "기록 범위",
"import": "가져오기",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/nl-NL/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback en suggesties",
"follow": "Volg ons op {{name}}",
"fullscreen": "Volledig scherm",
"historyRange": "Geschiedenisbereik",
"import": "Importeren",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/pl-PL/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Opinie i sugestie",
"follow": "Zaobserwuj nas na {{name}}",
"fullscreen": "Tryb pełnoekranowy",
"historyRange": "Zakres historii",
"import": "Importuj ustawienia",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/pt-BR/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback e sugestões",
"follow": "Siga-nos no {{name}}",
"fullscreen": "Modo de Tela Cheia",
"historyRange": "Intervalo de histórico",
"import": "Importar configuração",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/ru-RU/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Обратная связь и предложения",
"follow": "Подпишитесь на нас на {{name}}",
"fullscreen": "Полноэкранный режим",
"historyRange": "История",
"import": "Импорт настроек",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/tr-TR/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Feedback",
"follow": "Bizi {{name}} üzerinde takip edin",
"fullscreen": "Tam Ekran Modu",
"historyRange": "Geçmiş Aralığı",
"import": "İçe Aktar",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/vi-VN/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "Phản hồi và đề xuất",
"follow": "Theo dõi chúng tôi trên {{name}}",
"fullscreen": "Chế độ toàn màn hình",
"historyRange": "Phạm vi lịch sử",
"import": "Nhập cấu hình",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/zh-CN/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "反馈与建议",
"follow": "在 {{name}} 上关注我们",
"fullscreen": "全屏模式",
"historyRange": "历史范围",
"import": "导入配置",
"importModal": {
Expand Down
1 change: 1 addition & 0 deletions locales/zh-TW/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"feedback": "回饋與建議",
"follow": "在 {{name}} 上關注我們",
"fullscreen": "全螢幕模式",
"historyRange": "歷史範圍",
"import": "匯入設定",
"importModal": {
Expand Down
13 changes: 4 additions & 9 deletions src/app/(main)/chat/(desktop)/features/ChatHeader/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { Avatar, ChatHeaderTitle } from '@lobehub/ui';
import { Skeleton } from 'antd';
import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
import { useSessionStore } from '@/store/session';
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
import { pathString } from '@/utils/url';

import Tags from './Tags';

const Main = memo(() => {
const { t } = useTranslation('chat');

const router = useRouter();

const [init, isInbox, title, description, avatar, backgroundColor] = useSessionStore((s) => [
sessionSelectors.isSomeSessionActive(s),
sessionSelectors.isInboxSession(s),
Expand All @@ -25,6 +22,8 @@ const Main = memo(() => {
sessionMetaSelectors.currentAgentBackgroundColor(s),
]);

const openChatSettings = useOpenChatSettings();

const displayTitle = isInbox ? t('inbox.title') : title;
const displayDesc = isInbox ? t('inbox.desc') : description;

Expand All @@ -42,11 +41,7 @@ const Main = memo(() => {
<Avatar
avatar={avatar}
background={backgroundColor}
onClick={() =>
isInbox
? router.push('/settings/agent')
: router.push(pathString('/chat/settings', { search: location.search }))
}
onClick={openChatSettings}
size={40}
title={title}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import { ActionIcon, EditableMessage } from '@lobehub/ui';
import { Skeleton } from 'antd';
import { Edit } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import useMergeState from 'use-merge-value';

import SidebarHeader from '@/components/SidebarHeader';
import AgentInfo from '@/features/AgentInfo';
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/selectors';
import { useGlobalStore } from '@/store/global';
import { ChatSettingsTabs } from '@/store/global/initialState';
import { useSessionStore } from '@/store/session';
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
import { pathString } from '@/utils/url';

import SidebarHeader from '../../../../components/SidebarHeader';
import { useStyles } from './style';

const SystemRole = memo(() => {
const router = useRouter();
const [editing, setEditing] = useState(false);
const { styles } = useStyles();

const openChatSettings = useOpenChatSettings(ChatSettingsTabs.Prompt);
const [init, meta] = useSessionStore((s) => [
sessionSelectors.isSomeSessionActive(s),
sessionMetaSelectors.currentAgentMeta(s),
Expand Down Expand Up @@ -93,7 +92,7 @@ const SystemRole = memo(() => {
onAvatarClick={() => {
setOpen(false);
setEditing(false);
router.push(pathString('/chat/settings', { search: location.search }));
openChatSettings();
}}
style={{ marginBottom: 16 }}
/>
Expand Down
20 changes: 3 additions & 17 deletions src/app/(main)/chat/features/SettingButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,16 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';

import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { useQueryRoute } from '@/hooks/useQueryRoute';
import { useGlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { useSessionStore } from '@/store/session';
import { sessionSelectors } from '@/store/session/selectors';
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';

const SettingButton = memo<{ mobile?: boolean }>(({ mobile }) => {
const isInbox = useSessionStore(sessionSelectors.isInboxSession);
const { t } = useTranslation('common');
const router = useQueryRoute();
const openChatSettings = useOpenChatSettings();

return (
<ActionIcon
icon={AlignJustify}
onClick={() => {
if (isInbox) {
useGlobalStore.setState({
sidebarKey: SidebarTabKey.Setting,
});
router.push('/settings/agent');
} else {
router.push('/chat/settings');
}
}}
onClick={openChatSettings}
size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}
title={t('header.session', { ns: 'setting' })}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(main)/chat/features/TopicListContent/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import SidebarHeader from '@/components/SidebarHeader';
import { useChatStore } from '@/store/chat';
import { topicSelectors } from '@/store/chat/selectors';

import SidebarHeader from '../../components/SidebarHeader';
import TopicSearchBar from './TopicSearchBar';

const Header = memo(() => {
Expand Down
3 changes: 2 additions & 1 deletion src/app/(main)/chat/settings/_layout/Desktop/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';

import HeaderContent from '@/app/(main)/chat/settings/features/HeaderContent';
import { pathString } from '@/utils/url';

import HeaderContent from '../../features/HeaderContent';

const Header = memo(() => {
const { t } = useTranslation('setting');
const router = useRouter();
Expand Down
3 changes: 2 additions & 1 deletion src/app/(main)/chat/settings/_layout/Mobile/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';

import HeaderContent from '@/app/(main)/chat/settings/features/HeaderContent';
import { mobileHeaderSticky } from '@/styles/mobileHeader';
import { pathString } from '@/utils/url';

import HeaderContent from '../../features/HeaderContent';

const Header = memo(() => {
const { t } = useTranslation('setting');
const router = useRouter();
Expand Down
23 changes: 23 additions & 0 deletions src/app/(main)/chat/settings/modal/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

import { useLayoutEffect } from 'react';

import { useQueryRoute } from '@/hooks/useQueryRoute';

/**
* @description: Chat Settings Modal (intercepting routes fallback when hard refresh)
* @example: /chat/settings/modal?tab=prompt => /chat/settings
* @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
*/

const ChatSettingsModalFallback = () => {
const router = useQueryRoute();

useLayoutEffect(() => {
router.replace('/chat/settings', { query: { tab: '' } });
}, []);

return null;
};

export default ChatSettingsModalFallback;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
<Menu
items={cateItems}
onClick={({ key }) => {
router.push(urlJoin('/settings', key));
if (modal) {
router.replace('/settings/modal', { query: { tab: key } });
} else {
router.push(urlJoin('/settings', key));
}
}}
selectable
selectedKeys={[modal ? tab : (activeTab as any)]}
Expand Down
27 changes: 27 additions & 0 deletions src/app/(main)/settings/modal/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

import { useLayoutEffect } from 'react';
import urlJoin from 'url-join';

import { useQuery } from '@/hooks/useQuery';
import { useQueryRoute } from '@/hooks/useQueryRoute';
import { SettingsTabs } from '@/store/global/initialState';

/**
* @description: Settings Modal (intercepting routes fallback when hard refresh)
* @example: /settings/modal?tab=common => /settings/common
* @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
*/

const SettingsModalFallback = () => {
const { tab = SettingsTabs.Common } = useQuery();
const router = useQueryRoute();

useLayoutEffect(() => {
router.replace(urlJoin('/settings', tab as SettingsTabs), { query: { tab: '' } });
}, []);

return null;
};

export default SettingsModalFallback;
40 changes: 40 additions & 0 deletions src/app/@modal/(.)settings/modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import dynamic from 'next/dynamic';
import { memo } from 'react';

import { useQuery } from '@/hooks/useQuery';
import { SettingsTabs } from '@/store/global/initialState';

import Skeleton from './loading';

const loading = () => <Skeleton />;

const Common = dynamic(() => import('@/app/(main)/settings/common'), { loading, ssr: false });
const About = dynamic(() => import('@/app/(main)/settings/about'), { loading, ssr: false });
const LLM = dynamic(() => import('@/app/(main)/settings/llm'), { loading, ssr: false });
const TTS = dynamic(() => import('@/app/(main)/settings/tts'), { loading, ssr: false });
const Agent = dynamic(() => import('@/app/(main)/settings/agent'), { loading, ssr: false });
const Sync = dynamic(() => import('@/app/(main)/settings/sync'), { loading, ssr: false });

interface SettingsModalProps {
browser?: string;
mobile?: boolean;
os?: string;
}

const SettingsModal = memo<SettingsModalProps>(({ browser, os, mobile }) => {
const { tab = SettingsTabs.Common } = useQuery();
return (
<>
{tab === SettingsTabs.Common && <Common />}
{tab === SettingsTabs.Sync && <Sync browser={browser} mobile={mobile} os={os} />}
{tab === SettingsTabs.LLM && <LLM />}
{tab === SettingsTabs.TTS && <TTS />}
{tab === SettingsTabs.Agent && <Agent />}
{tab === SettingsTabs.About && <About mobile={mobile} />}
</>
);
});

export default SettingsModal;
Loading

0 comments on commit 29b6442

Please sign in to comment.