@lobehub/chat 0.157.1 → 0.158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/.eslintignore +1 -2
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +14 -14
  4. package/README.zh-CN.md +14 -14
  5. package/locales/ar/auth.json +2 -0
  6. package/locales/ar/chat.json +1 -0
  7. package/locales/ar/common.json +1 -0
  8. package/locales/bg-BG/auth.json +2 -0
  9. package/locales/bg-BG/chat.json +1 -0
  10. package/locales/bg-BG/common.json +1 -0
  11. package/locales/bg-BG/error.json +3 -3
  12. package/locales/de-DE/auth.json +2 -0
  13. package/locales/de-DE/chat.json +1 -0
  14. package/locales/de-DE/common.json +1 -0
  15. package/locales/en-US/auth.json +2 -0
  16. package/locales/en-US/chat.json +1 -0
  17. package/locales/en-US/common.json +1 -0
  18. package/locales/es-ES/auth.json +2 -0
  19. package/locales/es-ES/chat.json +1 -0
  20. package/locales/es-ES/common.json +1 -0
  21. package/locales/fr-FR/auth.json +2 -0
  22. package/locales/fr-FR/chat.json +1 -0
  23. package/locales/fr-FR/common.json +1 -0
  24. package/locales/it-IT/auth.json +2 -0
  25. package/locales/it-IT/chat.json +1 -0
  26. package/locales/it-IT/common.json +1 -0
  27. package/locales/ja-JP/auth.json +2 -0
  28. package/locales/ja-JP/chat.json +1 -0
  29. package/locales/ja-JP/common.json +1 -0
  30. package/locales/ko-KR/auth.json +2 -0
  31. package/locales/ko-KR/chat.json +1 -0
  32. package/locales/ko-KR/common.json +1 -0
  33. package/locales/nl-NL/auth.json +2 -0
  34. package/locales/nl-NL/chat.json +1 -0
  35. package/locales/nl-NL/common.json +50 -49
  36. package/locales/pl-PL/auth.json +2 -0
  37. package/locales/pl-PL/chat.json +1 -0
  38. package/locales/pl-PL/common.json +1 -0
  39. package/locales/pl-PL/error.json +3 -3
  40. package/locales/pt-BR/auth.json +2 -0
  41. package/locales/pt-BR/chat.json +1 -0
  42. package/locales/pt-BR/common.json +1 -0
  43. package/locales/ru-RU/auth.json +2 -0
  44. package/locales/ru-RU/chat.json +1 -0
  45. package/locales/ru-RU/common.json +1 -0
  46. package/locales/ru-RU/error.json +3 -3
  47. package/locales/tr-TR/auth.json +2 -0
  48. package/locales/tr-TR/chat.json +1 -0
  49. package/locales/tr-TR/common.json +1 -0
  50. package/locales/vi-VN/auth.json +2 -0
  51. package/locales/vi-VN/chat.json +1 -0
  52. package/locales/vi-VN/common.json +1 -0
  53. package/locales/zh-CN/auth.json +2 -0
  54. package/locales/zh-CN/chat.json +1 -0
  55. package/locales/zh-CN/common.json +1 -0
  56. package/locales/zh-TW/auth.json +2 -0
  57. package/locales/zh-TW/chat.json +1 -0
  58. package/locales/zh-TW/common.json +1 -0
  59. package/package.json +2 -2
  60. package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +80 -0
  61. package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +116 -0
  62. package/src/app/(main)/(mobile)/me/(home)/features/Category.tsx +15 -0
  63. package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +37 -0
  64. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +95 -0
  65. package/src/app/(main)/(mobile)/me/{page.tsx → (home)/page.tsx} +6 -10
  66. package/src/app/(main)/(mobile)/me/data/features/Category.tsx +48 -0
  67. package/src/app/(main)/(mobile)/me/data/features/Header.tsx +33 -0
  68. package/src/app/(main)/(mobile)/me/data/layout.tsx +13 -0
  69. package/src/app/(main)/(mobile)/me/data/loading.tsx +5 -0
  70. package/src/app/(main)/(mobile)/me/data/page.tsx +17 -0
  71. package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +45 -0
  72. package/src/app/(main)/(mobile)/me/profile/features/Header.tsx +33 -0
  73. package/src/app/(main)/(mobile)/me/profile/layout.tsx +16 -0
  74. package/src/app/(main)/(mobile)/me/profile/loading.tsx +5 -0
  75. package/src/app/(main)/(mobile)/me/profile/page.tsx +17 -0
  76. package/src/app/(main)/(mobile)/me/settings/features/Category.tsx +15 -0
  77. package/src/app/(main)/(mobile)/me/settings/features/Header.tsx +33 -0
  78. package/src/app/(main)/(mobile)/me/settings/features/useCategory.tsx +57 -0
  79. package/src/app/(main)/(mobile)/me/settings/layout.tsx +13 -0
  80. package/src/app/(main)/(mobile)/me/settings/loading.tsx +5 -0
  81. package/src/app/(main)/(mobile)/me/settings/page.tsx +17 -0
  82. package/src/app/(main)/_layout/Mobile.tsx +5 -4
  83. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +21 -1
  84. package/src/app/(main)/profile/[[...slugs]]/Client.tsx +74 -0
  85. package/src/app/(main)/profile/[[...slugs]]/page.tsx +18 -0
  86. package/src/app/(main)/profile/_layout/Mobile/Header.tsx +26 -0
  87. package/src/app/(main)/profile/_layout/Mobile/index.tsx +16 -0
  88. package/src/app/(main)/profile/layout.tsx +20 -0
  89. package/src/app/(main)/profile/loading.tsx +23 -0
  90. package/src/app/(main)/settings/_layout/Mobile/Header.tsx +2 -1
  91. package/src/app/(main)/settings/hooks/useCategory.tsx +7 -13
  92. package/src/app/@modal/layout.tsx +3 -0
  93. package/src/components/Cell/Divider.tsx +3 -2
  94. package/src/components/Cell/index.tsx +28 -18
  95. package/src/features/User/DataStatistics.tsx +3 -1
  96. package/src/features/User/UserLoginOrSignup.tsx +2 -2
  97. package/src/features/User/UserPanel/PanelContent.tsx +9 -3
  98. package/src/features/User/UserPanel/useMenu.tsx +29 -29
  99. package/src/features/User/__tests__/PanelContent.test.tsx +7 -0
  100. package/src/features/User/__tests__/useMenu.test.tsx +142 -0
  101. package/src/layout/AuthProvider/Clerk/useAppearance.ts +5 -4
  102. package/src/libs/agent-runtime/azureOpenai/index.test.ts +161 -27
  103. package/src/libs/agent-runtime/utils/streams/openai.ts +4 -0
  104. package/src/locales/default/auth.ts +2 -0
  105. package/src/locales/default/chat.ts +1 -0
  106. package/src/locales/default/common.ts +1 -0
  107. package/src/store/user/slices/auth/selectors.ts +2 -1
  108. package/src/app/(auth)/profile/[[...slugs]]/PageTitle.tsx +0 -13
  109. package/src/app/(auth)/profile/[[...slugs]]/page.tsx +0 -14
  110. package/src/app/(main)/(mobile)/me/features/Cate.tsx +0 -33
  111. package/src/app/(main)/(mobile)/me/features/ExtraCate.tsx +0 -24
  112. package/src/app/(main)/(mobile)/me/features/useExtraCate.tsx +0 -62
  113. /package/src/app/(main)/(mobile)/me/{features → (home)/features}/Header.tsx +0 -0
  114. /package/src/app/(main)/(mobile)/me/{layout.tsx → (home)/layout.tsx} +0 -0
  115. /package/src/app/(main)/(mobile)/me/{loading.tsx → (home)/loading.tsx} +0 -0
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "Zaloguj się",
3
3
  "loginOrSignup": "Zaloguj się / Zarejestruj się",
4
+ "profile": "Profil użytkownika",
5
+ "security": "Bezpieczeństwo",
4
6
  "signout": "Wyloguj",
5
7
  "signup": "Zarejestruj się"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "Cześć, jestem **{{name}}**, możesz od razu zacząć ze mną rozmawiać, lub udoskonalić moje informacje, przechodząc do [Ustawień asystenta](/chat/settings#session={{id}}).",
6
6
  "agentDefaultMessageWithSystemRole": "Cześć, jestem **{{name}}**, {{systemRole}}, zacznijmy rozmowę!",
7
+ "agentsAndConversations": "Agenci i rozmowy",
7
8
  "backToBottom": "Przewiń na dół",
8
9
  "clearCurrentMessages": "Wyczyść bieżącą rozmowę",
9
10
  "confirmClearCurrentMessages": "Czy na pewno chcesz wyczyścić bieżącą rozmowę? Tej operacji nie można cofnąć.",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "Użytkownik Anonimowy",
171
171
  "billing": "Zarządzanie rachunkami",
172
+ "data": "Przechowywanie danych",
172
173
  "defaultNickname": "Użytkownik Wersji Społecznościowej",
173
174
  "discord": "Wsparcie społeczności",
174
175
  "docs": "Dokumentacja",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "clerkAuth": {
3
3
  "loginSuccess": {
4
- "action": "继续会话",
5
- "desc": "{{greeting}},很高兴能够继续为你服务。让我们接着刚刚的话题聊下去吧",
6
- "title": "欢迎回来, {{nickName}}"
4
+ "action": "Continue session",
5
+ "desc": "{{greeting}}, it's great to continue serving you. Let's continue our previous conversation.",
6
+ "title": "Welcome back, {{nickName}}"
7
7
  }
8
8
  },
9
9
  "error": {
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "Entrar",
3
3
  "loginOrSignup": "Entrar / Registrar",
4
+ "profile": "Perfil",
5
+ "security": "Segurança",
4
6
  "signout": "Sair",
5
7
  "signup": "Cadastre-se"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "Olá, eu sou **{{name}}**, você pode começar a conversar comigo agora ou ir para [Configurações do Assistente](/chat/settings#session={{id}}) para completar minhas informações.",
6
6
  "agentDefaultMessageWithSystemRole": "Olá, eu sou **{{name}}**, {{systemRole}}, vamos conversar!",
7
+ "agentsAndConversations": "Agentes e Conversas",
7
8
  "backToBottom": "Voltar para o início",
8
9
  "clearCurrentMessages": "Limpar mensagens atuais",
9
10
  "confirmClearCurrentMessages": "Você está prestes a limpar as mensagens desta sessão. Depois de limpar, não será possível recuperá-las. Por favor, confirme sua ação.",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "Usuário Anônimo",
171
171
  "billing": "Gerenciamento de faturas",
172
+ "data": "Armazenamento de dados",
172
173
  "defaultNickname": "Usuário da Comunidade",
173
174
  "discord": "Suporte da Comunidade",
174
175
  "docs": "Documentação",
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "Войти",
3
3
  "loginOrSignup": "Войти / Зарегистрироваться",
4
+ "profile": "Профиль",
5
+ "security": "Безопасность",
4
6
  "signout": "Выйти",
5
7
  "signup": "Зарегистрироваться"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "Привет, я **{{name}}**. Ты можешь начать общение со мной прямо сейчас или перейти к [настройкам помощника](/chat/settings#session={{id}}), чтобы улучшить мою информацию.",
6
6
  "agentDefaultMessageWithSystemRole": "Привет, я **{{name}}**, {{systemRole}}. Давай начнем разговор!",
7
+ "agentsAndConversations": "Агенты и беседы",
7
8
  "backToBottom": "Вернуться вниз",
8
9
  "clearCurrentMessages": "Очистить текущий разговор",
9
10
  "confirmClearCurrentMessages": "Вы уверены, что хотите очистить текущий разговор? После этого его нельзя будет восстановить.",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "Анонимный пользователь",
171
171
  "billing": "Управление счетами",
172
+ "data": "Хранилище данных",
172
173
  "defaultNickname": "Пользователь сообщества",
173
174
  "discord": "Поддержка сообщества",
174
175
  "docs": "Документация",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "clerkAuth": {
3
3
  "loginSuccess": {
4
- "action": "继续会话",
5
- "desc": "{{greeting}},很高兴能够继续为你服务。让我们接着刚刚的话题聊下去吧",
6
- "title": "欢迎回来, {{nickName}}"
4
+ "action": "Продолжить разговор",
5
+ "desc": "{{greeting}}, рады снова быть к вашим услугам. Давайте продолжим нашу беседу",
6
+ "title": "Добро пожаловать обратно, {{nickName}}"
7
7
  }
8
8
  },
9
9
  "error": {
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "Giriş Yap",
3
3
  "loginOrSignup": "Giriş Yap / Kayıt Ol",
4
+ "profile": "Profil",
5
+ "security": "Güvenlik",
4
6
  "signout": "Çıkış Yap",
5
7
  "signup": "Kaydol"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "Merhaba, Ben **{{name}}**. Hemen benimle sohbet etmeye başlayabilirsiniz veya [Asistan Ayarları](/chat/settings#session={{id}}) sayfasına giderek bilgilerimi tamamlayabilirsiniz.",
6
6
  "agentDefaultMessageWithSystemRole": "Merhaba, Ben **{{name}}**, {{systemRole}}. Hemen sohbet etmeye başlayalım!",
7
+ "agentsAndConversations": "Ajanlar ve Konuşmalar",
7
8
  "backToBottom": "En alta git",
8
9
  "clearCurrentMessages": "Mevcut oturum mesajlarını temizle",
9
10
  "confirmClearCurrentMessages": "Mevcut oturum mesajlarını temizlemek üzeresiniz. Temizlendikten sonra geri alınamazlar. Lütfen eyleminizi onaylayın.",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "Anonim Kullanıcı",
171
171
  "billing": "Fatura Yönetimi",
172
+ "data": "Veri Depolama",
172
173
  "defaultNickname": "Topluluk Kullanıcısı",
173
174
  "discord": "Topluluk Destek",
174
175
  "docs": "Belgeler",
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "Đăng nhập",
3
3
  "loginOrSignup": "Đăng nhập / Đăng ký",
4
+ "profile": "Hồ sơ cá nhân",
5
+ "security": "Bảo mật",
4
6
  "signout": "Đăng xuất",
5
7
  "signup": "Đăng ký"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "Xin chào, tôi là **{{name}}**, bạn có thể bắt đầu trò chuyện với tôi ngay lập tức, hoặc đến [Cài đặt trợ lý](/chat/settings#session={{id}}) để hoàn thiện thông tin của tôi.",
6
6
  "agentDefaultMessageWithSystemRole": "Xin chào, tôi là **{{name}}**, {{systemRole}}. Hãy bắt đầu trò chuyện ngay!",
7
+ "agentsAndConversations": "Người hỗ trợ và cuộc trò chuyện",
7
8
  "backToBottom": "Quay về dưới cùng",
8
9
  "clearCurrentMessages": "Xóa tin nhắn hiện tại",
9
10
  "confirmClearCurrentMessages": "Bạn sắp xóa tin nhắn hiện tại. Hành động này không thể hoàn tác, vui lòng xác nhận.",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "Người dùng ẩn danh",
171
171
  "billing": "Quản lý hóa đơn",
172
+ "data": "Lưu trữ dữ liệu",
172
173
  "defaultNickname": "Người dùng phiên bản cộng đồng",
173
174
  "discord": "Hỗ trợ cộng đồng",
174
175
  "docs": "Tài liệu sử dụng",
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "登录",
3
3
  "loginOrSignup": "登录 / 注册",
4
+ "profile": "个人资料",
5
+ "security": "安全",
4
6
  "signout": "退出登录",
5
7
  "signup": "注册"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "你好,我是 **{{name}}**,你可以立即与我开始对话,也可以前往 [助手设置](/chat/settings#session={{id}}) 完善我的信息。",
6
6
  "agentDefaultMessageWithSystemRole": "你好,我是 **{{name}}**,{{systemRole}},让我们开始对话吧!",
7
+ "agentsAndConversations": "助手与会话",
7
8
  "backToBottom": "跳转至当前",
8
9
  "clearCurrentMessages": "清空当前会话消息",
9
10
  "confirmClearCurrentMessages": "即将清空当前会话消息,清空后将无法找回,请确认你的操作",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "匿名用户",
171
171
  "billing": "账单管理",
172
+ "data": "数据存储",
172
173
  "defaultNickname": "社区版用户",
173
174
  "discord": "社区支持",
174
175
  "docs": "使用文档",
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "login": "登入",
3
3
  "loginOrSignup": "登入 / 註冊",
4
+ "profile": "個人檔案",
5
+ "security": "安全",
4
6
  "signout": "登出",
5
7
  "signup": "註冊"
6
8
  }
@@ -4,6 +4,7 @@
4
4
  },
5
5
  "agentDefaultMessage": "你好,我是 **{{name}}**,你可以立即與我開始對話,也可以前往 [助手設置](/chat/settings#session={{id}}) 完善我的信息。",
6
6
  "agentDefaultMessageWithSystemRole": "你好,我是 **{{name}}**,{{systemRole}},讓我們開始對話吧!",
7
+ "agentsAndConversations": "助理與對話",
7
8
  "backToBottom": "返回底部",
8
9
  "clearCurrentMessages": "清空當前對話",
9
10
  "confirmClearCurrentMessages": "即將清空當前對話,清空後將無法找回,請確認你的操作",
@@ -169,6 +169,7 @@
169
169
  "userPanel": {
170
170
  "anonymousNickName": "匿名使用者",
171
171
  "billing": "帳單管理",
172
+ "data": "資料儲存",
172
173
  "defaultNickname": "社群版使用者",
173
174
  "discord": "社區支援",
174
175
  "docs": "使用文件",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.157.1",
3
+ "version": "0.158.0",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -90,7 +90,7 @@
90
90
  "@clerk/localizations": "2.0.0",
91
91
  "@clerk/nextjs": "^5.0.8",
92
92
  "@clerk/themes": "^2.1.3",
93
- "@google/generative-ai": "^0.10.0",
93
+ "@google/generative-ai": "^0.11.0",
94
94
  "@icons-pack/react-simple-icons": "^9.5.0",
95
95
  "@lobehub/chat-plugin-sdk": "latest",
96
96
  "@lobehub/chat-plugins-gateway": "latest",
@@ -0,0 +1,80 @@
1
+ import { act, render, screen } from '@testing-library/react';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useUserStore } from '@/store/user';
5
+
6
+ import UserBanner from '../features/UserBanner';
7
+
8
+ // Mock dependencies
9
+ vi.mock('next/navigation', () => ({
10
+ useRouter: vi.fn(() => ({
11
+ push: vi.fn(),
12
+ })),
13
+ }));
14
+
15
+ vi.mock('@/features/User/UserInfo', () => ({
16
+ default: vi.fn(() => <div>Mocked UserInfo</div>),
17
+ }));
18
+
19
+ vi.mock('@/features/User/DataStatistics', () => ({
20
+ default: vi.fn(() => <div>Mocked DataStatistics</div>),
21
+ }));
22
+
23
+ vi.mock('@/features/User/UserLoginOrSignup', () => ({
24
+ default: vi.fn(() => <div>Mocked UserLoginOrSignup</div>),
25
+ }));
26
+
27
+ // 定义一个变量来存储 enableAuth 的值
28
+ let enableAuth = true;
29
+
30
+ // 模拟 @/const/auth 模块
31
+ vi.mock('@/const/auth', () => ({
32
+ get enableAuth() {
33
+ return enableAuth;
34
+ },
35
+ }));
36
+
37
+ afterEach(() => {
38
+ enableAuth = true;
39
+ });
40
+
41
+ describe('UserBanner', () => {
42
+ it('should render UserInfo and DataStatistics when auth is disabled', () => {
43
+ act(() => {
44
+ useUserStore.setState({ isSignedIn: false });
45
+ });
46
+ enableAuth = false;
47
+
48
+ render(<UserBanner />);
49
+
50
+ expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
51
+ expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
52
+ expect(screen.queryByText('Mocked UserLoginOrSignup')).not.toBeInTheDocument();
53
+ });
54
+
55
+ it('should render UserInfo and DataStatistics when user is logged in with auth enabled', () => {
56
+ act(() => {
57
+ useUserStore.setState({ isSignedIn: true });
58
+ });
59
+ enableAuth = true;
60
+
61
+ render(<UserBanner />);
62
+
63
+ expect(screen.getByText('Mocked UserInfo')).toBeInTheDocument();
64
+ expect(screen.getByText('Mocked DataStatistics')).toBeInTheDocument();
65
+ expect(screen.queryByText('Mocked UserLoginOrSignup')).not.toBeInTheDocument();
66
+ });
67
+
68
+ it('should render UserLoginOrSignup when user is not logged in with auth enabled', () => {
69
+ act(() => {
70
+ useUserStore.setState({ isSignedIn: false });
71
+ });
72
+ enableAuth = true;
73
+
74
+ render(<UserBanner />);
75
+
76
+ expect(screen.getByText('Mocked UserLoginOrSignup')).toBeInTheDocument();
77
+ expect(screen.queryByText('Mocked UserInfo')).not.toBeInTheDocument();
78
+ expect(screen.queryByText('Mocked DataStatistics')).not.toBeInTheDocument();
79
+ });
80
+ });
@@ -0,0 +1,116 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useUserStore } from '@/store/user';
5
+
6
+ import { useCategory } from '../features/useCategory';
7
+
8
+ // Mock dependencies
9
+ vi.mock('next/navigation', () => ({
10
+ useRouter: vi.fn(() => ({
11
+ push: vi.fn(),
12
+ })),
13
+ }));
14
+
15
+ vi.mock('react-i18next', () => ({
16
+ useTranslation: vi.fn(() => ({
17
+ t: vi.fn((key) => key),
18
+ })),
19
+ }));
20
+
21
+ vi.mock('../../settings/features/useCategory', () => ({
22
+ useCategory: vi.fn(() => [{ key: 'extraSetting', label: 'Extra Setting' }]),
23
+ }));
24
+
25
+ // 定义一个变量来存储 enableAuth 的值
26
+ let enableAuth = true;
27
+ let enableClerk = true;
28
+ // 模拟 @/const/auth 模块
29
+ vi.mock('@/const/auth', () => ({
30
+ get enableAuth() {
31
+ return enableAuth;
32
+ },
33
+ get enableClerk() {
34
+ return enableClerk;
35
+ },
36
+ }));
37
+
38
+ afterEach(() => {
39
+ enableAuth = true;
40
+ enableClerk = true;
41
+ });
42
+
43
+ describe('useCategory', () => {
44
+ it('should return correct items when the user is logged in with authentication', () => {
45
+ act(() => {
46
+ useUserStore.setState({ isSignedIn: true });
47
+ });
48
+ enableAuth = true;
49
+ enableClerk = false;
50
+
51
+ const { result } = renderHook(() => useCategory());
52
+
53
+ act(() => {
54
+ const items = result.current;
55
+ expect(items.some((item) => item.key === 'profile')).toBe(false);
56
+ expect(items.some((item) => item.key === 'setting')).toBe(true);
57
+ expect(items.some((item) => item.key === 'data')).toBe(true);
58
+ expect(items.some((item) => item.key === 'docs')).toBe(true);
59
+ expect(items.some((item) => item.key === 'feedback')).toBe(true);
60
+ expect(items.some((item) => item.key === 'discord')).toBe(true);
61
+ });
62
+ });
63
+
64
+ it('should return correct items when the user is logged in with Clerk', () => {
65
+ act(() => {
66
+ useUserStore.setState({ isSignedIn: true });
67
+ });
68
+ enableAuth = true;
69
+ enableClerk = true;
70
+
71
+ const { result } = renderHook(() => useCategory());
72
+
73
+ act(() => {
74
+ const items = result.current;
75
+ expect(items.some((item) => item.key === 'profile')).toBe(true);
76
+ expect(items.some((item) => item.key === 'setting')).toBe(true);
77
+ expect(items.some((item) => item.key === 'data')).toBe(true);
78
+ expect(items.some((item) => item.key === 'docs')).toBe(true);
79
+ expect(items.some((item) => item.key === 'feedback')).toBe(true);
80
+ expect(items.some((item) => item.key === 'discord')).toBe(true);
81
+ });
82
+ });
83
+
84
+ it('should return correct items when the user is not logged in', () => {
85
+ act(() => {
86
+ useUserStore.setState({ isSignedIn: false });
87
+ });
88
+ enableAuth = true;
89
+
90
+ const { result } = renderHook(() => useCategory());
91
+
92
+ act(() => {
93
+ const items = result.current;
94
+ expect(items.some((item) => item.key === 'profile')).toBe(false);
95
+ expect(items.some((item) => item.key === 'setting')).toBe(false);
96
+ expect(items.some((item) => item.key === 'data')).toBe(false);
97
+ expect(items.some((item) => item.key === 'docs')).toBe(true);
98
+ expect(items.some((item) => item.key === 'feedback')).toBe(true);
99
+ expect(items.some((item) => item.key === 'discord')).toBe(true);
100
+ });
101
+ });
102
+
103
+ it('should handle settings for non-authenticated users', () => {
104
+ act(() => {
105
+ useUserStore.setState({ isSignedIn: false });
106
+ });
107
+ enableAuth = false;
108
+
109
+ const { result } = renderHook(() => useCategory());
110
+
111
+ act(() => {
112
+ const items = result.current;
113
+ expect(items.some((item) => item.key === 'extraSetting')).toBe(true);
114
+ });
115
+ });
116
+ });
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+
5
+ import Cell from '@/components/Cell';
6
+
7
+ import { useCategory } from './useCategory';
8
+
9
+ const Category = memo(() => {
10
+ const items = useCategory();
11
+
12
+ return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
13
+ });
14
+
15
+ export default Category;
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
3
+ import { useRouter } from 'next/navigation';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { enableAuth } from '@/const/auth';
8
+ import DataStatistics from '@/features/User/DataStatistics';
9
+ import UserInfo from '@/features/User/UserInfo';
10
+ import UserLoginOrSignup from '@/features/User/UserLoginOrSignup';
11
+ import { useUserStore } from '@/store/user';
12
+ import { authSelectors } from '@/store/user/selectors';
13
+
14
+ const UserBanner = memo(() => {
15
+ const router = useRouter();
16
+ const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
17
+
18
+ return (
19
+ <Flexbox gap={12} paddingBlock={8}>
20
+ {!enableAuth ? (
21
+ <>
22
+ <UserInfo />
23
+ <DataStatistics paddingInline={12} />
24
+ </>
25
+ ) : isLoginWithAuth ? (
26
+ <>
27
+ <UserInfo onClick={() => router.push('/me/profile')} />
28
+ <DataStatistics paddingInline={12} />
29
+ </>
30
+ ) : (
31
+ <UserLoginOrSignup onClick={() => router.push('/login')} />
32
+ )}
33
+ </Flexbox>
34
+ );
35
+ });
36
+
37
+ export default UserBanner;
@@ -0,0 +1,95 @@
1
+ import { DiscordIcon } from '@lobehub/ui';
2
+ import { Book, CircleUserRound, Database, Feather, Settings2 } from 'lucide-react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { CellProps } from '@/components/Cell';
7
+ import { enableAuth } from '@/const/auth';
8
+ import { DISCORD, DOCUMENTS, FEEDBACK } from '@/const/url';
9
+ import { useUserStore } from '@/store/user';
10
+ import { authSelectors } from '@/store/user/slices/auth/selectors';
11
+
12
+ import { useCategory as useSettingsCategory } from '../../settings/features/useCategory';
13
+
14
+ export const useCategory = () => {
15
+ const router = useRouter();
16
+ const { t } = useTranslation(['common', 'setting', 'auth']);
17
+ const [isLogin, isLoginWithAuth, isLoginWithClerk] = useUserStore((s) => [
18
+ authSelectors.isLogin(s),
19
+ authSelectors.isLoginWithAuth(s),
20
+ authSelectors.isLoginWithClerk(s),
21
+ ]);
22
+
23
+ const profile: CellProps[] = [
24
+ {
25
+ icon: CircleUserRound,
26
+ key: 'profile',
27
+ label: t('userPanel.profile'),
28
+ onClick: () => router.push('/me/profile'),
29
+ },
30
+ ];
31
+
32
+ const settings: CellProps[] = [
33
+ {
34
+ icon: Settings2,
35
+ key: 'setting',
36
+ label: t('userPanel.setting'),
37
+ onClick: () => router.push('/me/settings'),
38
+ },
39
+ {
40
+ type: 'divider',
41
+ },
42
+ ];
43
+
44
+ const settingsWithoutAuth = [
45
+ ...useSettingsCategory(),
46
+ {
47
+ type: 'divider',
48
+ },
49
+ ];
50
+
51
+ const data: CellProps[] = [
52
+ {
53
+ icon: Database,
54
+ key: 'data',
55
+ label: t('userPanel.data'),
56
+ onClick: () => router.push('/me/data'),
57
+ },
58
+ {
59
+ type: 'divider',
60
+ },
61
+ ];
62
+
63
+ const helps: CellProps[] = [
64
+ {
65
+ icon: Book,
66
+ key: 'docs',
67
+ label: t('document'),
68
+ onClick: () => window.open(DOCUMENTS, '__blank'),
69
+ },
70
+ {
71
+ icon: Feather,
72
+ key: 'feedback',
73
+ label: t('feedback'),
74
+ onClick: () => window.open(FEEDBACK, '__blank'),
75
+ },
76
+ {
77
+ icon: DiscordIcon,
78
+ key: 'discord',
79
+ label: 'Discord',
80
+ onClick: () => window.open(DISCORD, '__blank'),
81
+ },
82
+ ];
83
+
84
+ const mainItems = [
85
+ {
86
+ type: 'divider',
87
+ },
88
+ ...(isLoginWithClerk ? profile : []),
89
+ ...(enableAuth ? (isLoginWithAuth ? settings : []) : settingsWithoutAuth),
90
+ ...(isLogin ? data : []),
91
+ ...helps,
92
+ ].filter(Boolean) as CellProps[];
93
+
94
+ return mainItems;
95
+ };
@@ -2,13 +2,10 @@ import { redirect } from 'next/navigation';
2
2
  import { Center } from 'react-layout-kit';
3
3
 
4
4
  import BrandWatermark from '@/components/BrandWatermark';
5
- import Divider from '@/components/Cell/Divider';
6
- import DataStatistics from '@/features/User/DataStatistics';
7
- import UserInfo from '@/features/User/UserInfo';
8
5
  import { isMobileDevice } from '@/utils/responsive';
9
6
 
10
- import Cate from './features/Cate';
11
- import ExtraCate from './features/ExtraCate';
7
+ import Category from './features/Category';
8
+ import UserBanner from './features/UserBanner';
12
9
 
13
10
  const Page = () => {
14
11
  const mobile = isMobileDevice();
@@ -17,11 +14,8 @@ const Page = () => {
17
14
 
18
15
  return (
19
16
  <>
20
- <UserInfo />
21
- <DataStatistics paddingInline={12} style={{ paddingBottom: 6 }} />
22
- <Divider />
23
- <Cate />
24
- <ExtraCate />
17
+ <UserBanner />
18
+ <Category />
25
19
  <Center padding={16}>
26
20
  <BrandWatermark />
27
21
  </Center>
@@ -29,4 +23,6 @@ const Page = () => {
29
23
  );
30
24
  };
31
25
 
26
+ Page.displayName = 'Me';
27
+
32
28
  export default Page;