@lobehub/chat 1.49.1 → 1.49.2

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.49.2](https://github.com/lobehub/lobe-chat/compare/v1.49.1...v1.49.2)
6
+
7
+ <sup>Released on **2025-01-27**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Remove use query.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Remove use query, closes [#5604](https://github.com/lobehub/lobe-chat/issues/5604) ([58c60de](https://github.com/lobehub/lobe-chat/commit/58c60de))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.49.1](https://github.com/lobehub/lobe-chat/compare/v1.49.0...v1.49.1)
6
31
 
7
32
  <sup>Released on **2025-01-27**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Remove use query."
6
+ ]
7
+ },
8
+ "date": "2025-01-27",
9
+ "version": "1.49.2"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.49.1",
3
+ "version": "1.49.2",
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",
@@ -8,7 +8,6 @@ import { memo, useEffect, useState } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
  import urlJoin from 'url-join';
10
10
 
11
- import { useQuery } from '@/hooks/useQuery';
12
11
  import { useQueryRoute } from '@/hooks/useQueryRoute';
13
12
  import { DiscoverTab } from '@/types/discover';
14
13
 
@@ -32,9 +31,8 @@ interface StoreSearchBarProps extends SearchBarProps {
32
31
  const StoreSearchBar = memo<StoreSearchBarProps>(({ mobile, onBlur, onFocus, ...rest }) => {
33
32
  const [active, setActive] = useState(false);
34
33
  const pathname = usePathname();
35
- const { q } = useQuery();
36
34
  const { activeKey } = useNav();
37
- const [searchKey, setSearchKey] = useQueryState('q');
35
+ const [searchKey, setSearchKey] = useQueryState('q', { clearOnDefault: true, defaultValue: '' });
38
36
 
39
37
  const { t } = useTranslation('discover');
40
38
  const { cx, styles } = useStyles();
@@ -45,8 +43,8 @@ const StoreSearchBar = memo<StoreSearchBarProps>(({ mobile, onBlur, onFocus, ...
45
43
  useEffect(() => {
46
44
  if (!pathname.includes('/discover/search')) return;
47
45
  // 使用 useQueryState 时,当 handleSearch 为空时无法回跳
48
- if (!q) router.push(urlJoin('/discover', activeType), { query: {}, replace: true });
49
- }, [q, pathname, activeType]);
46
+ if (!searchKey) router.push(urlJoin('/discover', activeType), { query: {}, replace: true });
47
+ }, [searchKey, pathname, activeType]);
50
48
 
51
49
  const handleSearch = (value: string) => {
52
50
  router.push('/discover/search', { query: { q: value, type: activeType } });
@@ -6,19 +6,19 @@ import { useTranslation } from 'react-i18next';
6
6
  import urlJoin from 'url-join';
7
7
 
8
8
  import type { MenuProps } from '@/components/Menu';
9
- import { useQuery } from '@/hooks/useQuery';
9
+ import { useDiscoverTab } from '@/hooks/useDiscoverTab';
10
10
  import { DiscoverTab } from '@/types/discover';
11
11
 
12
12
  export const useNav = () => {
13
13
  const pathname = usePathname();
14
- const { type } = useQuery();
14
+ const type = useDiscoverTab();
15
15
  const { t } = useTranslation('discover');
16
16
  const iconSize = { fontSize: 16 };
17
17
 
18
18
  const activeKey = useMemo(() => {
19
19
  for (const value of Object.values(DiscoverTab)) {
20
20
  if (pathname === '/discover/search') {
21
- return (type as DiscoverTab) || DiscoverTab.Assistants;
21
+ return type;
22
22
  } else if (pathname.includes(urlJoin('/discover', value))) {
23
23
  return value;
24
24
  }
@@ -5,14 +5,14 @@ import { useRouter } from 'next/navigation';
5
5
  import { memo } from 'react';
6
6
  import urlJoin from 'url-join';
7
7
 
8
- import { useQuery } from '@/hooks/useQuery';
8
+ import { useDiscoverTab } from '@/hooks/useDiscoverTab';
9
9
  import { mobileHeaderSticky } from '@/styles/mobileHeader';
10
10
 
11
11
  import StoreSearchBar from '../../../features/StoreSearchBar';
12
12
 
13
13
  const Header = memo(() => {
14
14
  const router = useRouter();
15
- const { type = 'assistants' } = useQuery();
15
+ const type = useDiscoverTab();
16
16
 
17
17
  return (
18
18
  <MobileNavBar
@@ -5,15 +5,13 @@ import urlJoin from 'url-join';
5
5
 
6
6
  import Menu from '@/components/Menu';
7
7
  import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
8
- import { useQuery } from '@/hooks/useQuery';
9
8
  import { useQueryRoute } from '@/hooks/useQueryRoute';
10
- import { ProfileTabs, SettingsTabs } from '@/store/global/initialState';
9
+ import { ProfileTabs } from '@/store/global/initialState';
11
10
 
12
11
  import { useCategory } from '../../hooks/useCategory';
13
12
 
14
- const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
13
+ const CategoryContent = memo(() => {
15
14
  const activeTab = useActiveSettingsKey();
16
- const { tab = SettingsTabs.Common } = useQuery();
17
15
  const cateItems = useCategory();
18
16
  const router = useQueryRoute();
19
17
 
@@ -22,14 +20,11 @@ const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
22
20
  items={cateItems}
23
21
  onClick={({ key }) => {
24
22
  const activeKey = key === ProfileTabs.Profile ? '/' : key;
25
- if (modal) {
26
- router.replace('/profile/modal', { query: { tab: activeKey } });
27
- } else {
28
- router.push(urlJoin('/profile', activeKey));
29
- }
23
+
24
+ router.push(urlJoin('/profile', activeKey));
30
25
  }}
31
26
  selectable
32
- selectedKeys={[modal ? tab : (activeTab as any)]}
27
+ selectedKeys={[activeTab]}
33
28
  variant={'compact'}
34
29
  />
35
30
  );
@@ -5,15 +5,12 @@ import urlJoin from 'url-join';
5
5
 
6
6
  import Menu from '@/components/Menu';
7
7
  import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
8
- import { useQuery } from '@/hooks/useQuery';
9
8
  import { useQueryRoute } from '@/hooks/useQueryRoute';
10
- import { SettingsTabs } from '@/store/global/initialState';
11
9
 
12
10
  import { useCategory } from '../../hooks/useCategory';
13
11
 
14
- const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
12
+ const CategoryContent = memo(() => {
15
13
  const activeTab = useActiveSettingsKey();
16
- const { tab = SettingsTabs.Common } = useQuery();
17
14
  const cateItems = useCategory();
18
15
  const router = useQueryRoute();
19
16
 
@@ -21,14 +18,10 @@ const CategoryContent = memo<{ modal?: boolean }>(({ modal }) => {
21
18
  <Menu
22
19
  items={cateItems}
23
20
  onClick={({ key }) => {
24
- if (modal) {
25
- router.replace('/settings/modal', { query: { tab: key } });
26
- } else {
27
- router.push(urlJoin('/settings', key));
28
- }
21
+ router.push(urlJoin('/settings', key));
29
22
  }}
30
23
  selectable
31
- selectedKeys={[modal ? tab : (activeTab as any)]}
24
+ selectedKeys={[activeTab]}
32
25
  variant={'compact'}
33
26
  />
34
27
  );
@@ -5,15 +5,14 @@ import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import HeaderContent from '@/app/(main)/chat/settings/features/HeaderContent';
7
7
  import Menu from '@/components/Menu';
8
- import { useQuery } from '@/hooks/useQuery';
8
+ import { useChatSettingsTab } from '@/hooks/useChatSettingsTab';
9
9
  import { useQueryRoute } from '@/hooks/useQueryRoute';
10
- import { ChatSettingsTabs } from '@/store/global/initialState';
11
10
 
12
11
  import { useCategory } from './useCategory';
13
12
 
14
13
  const CategoryContent = memo(() => {
15
14
  const cateItems = useCategory();
16
- const { tab = ChatSettingsTabs.Meta } = useQuery();
15
+ const tab = useChatSettingsTab();
17
16
  const router = useQueryRoute();
18
17
 
19
18
  return (
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
9
9
  import ModalLayout from '@/app/@modal/_layout/ModalLayout';
10
10
  import StoreUpdater from '@/features/AgentSetting/StoreUpdater';
11
11
  import { Provider, createStore } from '@/features/AgentSetting/store';
12
- import { useQuery } from '@/hooks/useQuery';
12
+ import { useChatSettingsTab } from '@/hooks/useChatSettingsTab';
13
13
  import { useAgentStore } from '@/store/agent';
14
14
  import { agentSelectors } from '@/store/agent/slices/chat';
15
15
  import { ChatSettingsTabs } from '@/store/global/initialState';
@@ -24,7 +24,7 @@ const CategoryContent = dynamic(() => import('./features/CategoryContent'), {
24
24
  });
25
25
 
26
26
  const Layout = memo<PropsWithChildren>(({ children }) => {
27
- const { tab = ChatSettingsTabs.Meta } = useQuery();
27
+ const tab = useChatSettingsTab();
28
28
  const { t } = useTranslation('setting');
29
29
  const id = useSessionStore((s) => s.activeId);
30
30
  const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
@@ -2,7 +2,7 @@
2
2
 
3
3
  import dynamic from 'next/dynamic';
4
4
 
5
- import { useQuery } from '@/hooks/useQuery';
5
+ import { useChatSettingsTab } from '@/hooks/useChatSettingsTab';
6
6
  import { ChatSettingsTabs } from '@/store/global/initialState';
7
7
 
8
8
  import Skeleton from './loading';
@@ -37,7 +37,8 @@ const AgentTTS = dynamic(() => import('@/features/AgentSetting/AgentTTS'), { loa
37
37
  */
38
38
 
39
39
  const Page = () => {
40
- const { tab = ChatSettingsTabs.Meta } = useQuery();
40
+ const tab = useChatSettingsTab();
41
+
41
42
  return (
42
43
  <>
43
44
  {tab === ChatSettingsTabs.Meta && <AgentMeta />}
@@ -0,0 +1,8 @@
1
+ import { ComponentType, Suspense } from 'react';
2
+
3
+ // @ts-ignore
4
+ export const withSuspense: <T>(Comp: T) => T = (Component: ComponentType<any>) => (props: any) => (
5
+ <Suspense>
6
+ <Component {...props} />
7
+ </Suspense>
8
+ );
@@ -28,8 +28,8 @@ const AgentTTS = memo(() => {
28
28
  return (all?: boolean) => new VoiceList(all ? undefined : locale);
29
29
  });
30
30
  const [showAllLocaleVoice, ttsService, updateConfig] = useStore((s) => [
31
- s.config.tts.showAllLocaleVoice,
32
- s.config.tts.ttsService,
31
+ s.config.tts?.showAllLocaleVoice,
32
+ s.config.tts?.ttsService,
33
33
  s.setAgentConfig,
34
34
  ]);
35
35
 
@@ -1,6 +1,6 @@
1
1
  import { usePathname } from 'next/navigation';
2
+ import { useQueryState } from 'nuqs';
2
3
 
3
- import { useQuery } from '@/hooks/useQuery';
4
4
  import { ProfileTabs, SettingsTabs, SidebarTabKey } from '@/store/global/initialState';
5
5
 
6
6
  /**
@@ -17,7 +17,7 @@ export const useActiveTabKey = () => {
17
17
  */
18
18
  export const useActiveSettingsKey = () => {
19
19
  const pathname = usePathname();
20
- const { tab } = useQuery();
20
+ const [tab] = useQueryState('tab');
21
21
 
22
22
  const tabs = pathname.split('/').at(-1);
23
23
 
@@ -33,7 +33,7 @@ export const useActiveSettingsKey = () => {
33
33
  */
34
34
  export const useActiveProfileKey = () => {
35
35
  const pathname = usePathname();
36
- const { tab } = useQuery();
36
+ const [tab] = useQueryState('tab');
37
37
 
38
38
  const tabs = pathname.split('/').at(-1);
39
39
 
@@ -0,0 +1,12 @@
1
+ import { useQueryState } from 'nuqs';
2
+
3
+ import { ChatSettingsTabs } from '@/store/global/initialState';
4
+
5
+ export const useChatSettingsTab = () => {
6
+ const [type] = useQueryState('tab', {
7
+ clearOnDefault: true,
8
+ defaultValue: ChatSettingsTabs.Meta,
9
+ });
10
+
11
+ return type as ChatSettingsTabs;
12
+ };
@@ -0,0 +1,12 @@
1
+ import { useQueryState } from 'nuqs';
2
+
3
+ import { DiscoverTab } from '@/types/discover';
4
+
5
+ export const useDiscoverTab = () => {
6
+ const [type] = useQueryState('type', {
7
+ clearOnDefault: true,
8
+ defaultValue: DiscoverTab.Assistants,
9
+ });
10
+
11
+ return type as DiscoverTab;
12
+ };
@@ -1,5 +1,5 @@
1
1
  import { renderHook } from '@testing-library/react';
2
- import { describe, expect, it, vi } from 'vitest';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  import { useQueryRoute } from './useQueryRoute';
5
5
 
@@ -10,13 +10,15 @@ vi.mock('next/navigation', () => ({
10
10
  replace: vi.fn((href) => href),
11
11
  })),
12
12
  }));
13
- vi.mock('@/hooks/useQuery', () => ({
14
- useQuery: vi.fn(() => ({ foo: 'bar' })),
15
- }));
13
+
16
14
  vi.mock('@/utils/env', () => ({
17
15
  isOnServerSide: false,
18
16
  }));
19
17
 
18
+ beforeEach(() => {
19
+ location.search = 'foo=bar';
20
+ });
21
+
20
22
  describe('useQueryRoute', () => {
21
23
  it('should generate correct href without hash and replace', () => {
22
24
  const { result } = renderHook(() =>
@@ -2,7 +2,6 @@ import { useRouter } from 'next/navigation';
2
2
  import qs, { type ParsedQuery } from 'query-string';
3
3
  import { useMemo } from 'react';
4
4
 
5
- import { useQuery } from '@/hooks/useQuery';
6
5
  import { isOnServerSide } from '@/utils/env';
7
6
 
8
7
  interface QueryRouteOptions {
@@ -30,17 +29,19 @@ const genHref = ({ hash, replace, url, prevQuery = {}, query = {} }: GenHrefOpti
30
29
 
31
30
  export const useQueryRoute = () => {
32
31
  const router = useRouter();
33
- const prevQuery = useQuery();
34
32
 
35
33
  return useMemo(
36
34
  () => ({
37
35
  push: (url: string, options: QueryRouteOptions = {}) => {
36
+ const prevQuery = qs.parse(window.location.search);
37
+
38
38
  return router.push(genHref({ prevQuery, url, ...options }));
39
39
  },
40
40
  replace: (url: string, options: QueryRouteOptions = {}) => {
41
+ const prevQuery = qs.parse(window.location.search);
41
42
  return router.replace(genHref({ prevQuery, url, ...options }));
42
43
  },
43
44
  }),
44
- [prevQuery],
45
+ [],
45
46
  );
46
47
  };
@@ -0,0 +1,12 @@
1
+ import { useQueryState } from 'nuqs';
2
+
3
+ import { SettingsTabs } from '@/store/global/initialState';
4
+
5
+ export const useSettingsTab = () => {
6
+ const [type] = useQueryState('tab', {
7
+ clearOnDefault: true,
8
+ defaultValue: SettingsTabs.Common,
9
+ });
10
+
11
+ return type as SettingsTabs;
12
+ };
@@ -4,6 +4,8 @@ import { useSearchParams } from 'next/navigation';
4
4
  import Script from 'next/script';
5
5
  import React, { memo } from 'react';
6
6
 
7
+ import { withSuspense } from '@/components/withSuspense';
8
+
7
9
  const ReactScan = memo(() => {
8
10
  const searchParams = useSearchParams();
9
11
 
@@ -12,4 +14,4 @@ const ReactScan = memo(() => {
12
14
  return !!debug && <Script src="https://unpkg.com/react-scan/dist/auto.global.js" />;
13
15
  });
14
16
 
15
- export default ReactScan;
17
+ export default withSuspense(ReactScan);
@@ -1,19 +0,0 @@
1
- import { renderHook } from '@testing-library/react';
2
- import { describe, expect, it, vi } from 'vitest';
3
-
4
- import { useQuery } from './useQuery';
5
-
6
- // Mocks
7
- vi.mock('next/navigation', () => ({
8
- useSearchParams: vi.fn(() => 'baz=qux&foo=bar'),
9
- }));
10
-
11
- describe('useQuery', () => {
12
- it('should parse query', () => {
13
- const { result } = renderHook(() => useQuery());
14
- expect(result.current).toEqual({
15
- baz: 'qux',
16
- foo: 'bar',
17
- });
18
- });
19
- });
@@ -1,8 +0,0 @@
1
- import { useSearchParams } from 'next/navigation';
2
- import qs from 'query-string';
3
- import { useMemo } from 'react';
4
-
5
- export const useQuery = () => {
6
- const rawQuery = useSearchParams();
7
- return useMemo(() => qs.parse(rawQuery.toString()), [rawQuery]);
8
- };