@lobehub/chat 0.142.4 → 0.142.5

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,32 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.142.5](https://github.com/lobehub/lobe-chat/compare/v0.142.4...v0.142.5)
6
+
7
+ <sup>Released on **2024-03-26**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix mobile click, fix mobile click issue.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix mobile click ([3775b28](https://github.com/lobehub/lobe-chat/commit/3775b28))
21
+ - **misc**: Fix mobile click issue, closes [#1744](https://github.com/lobehub/lobe-chat/issues/1744) ([a6b1234](https://github.com/lobehub/lobe-chat/commit/a6b1234))
22
+
23
+ </details>
24
+
25
+ <div align="right">
26
+
27
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
28
+
29
+ </div>
30
+
5
31
  ### [Version 0.142.4](https://github.com/lobehub/lobe-chat/compare/v0.142.3...v0.142.4)
6
32
 
7
33
  <sup>Released on **2024-03-26**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.142.4",
3
+ "version": "0.142.5",
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",
@@ -7,6 +7,7 @@ import { Flexbox } from 'react-layout-kit';
7
7
  import Conversation from '@/features/Conversation';
8
8
  import { useSessionStore } from '@/store/session';
9
9
 
10
+ import SessionHydration from '../../components/SessionHydration';
10
11
  import TelemetryNotification from '../../features/TelemetryNotification';
11
12
  import ChatInput from '../features/ChatInput';
12
13
  import ChatHeader from './ChatHeader';
@@ -27,6 +28,7 @@ const Chat = memo(() => {
27
28
  <TopicList />
28
29
  <TelemetryNotification mobile />
29
30
  </Flexbox>
31
+ <SessionHydration />
30
32
  </>
31
33
  );
32
34
  });
@@ -1,9 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { useResponsive } from 'antd-style';
4
- import { useRouter } from 'next/navigation';
5
4
  import { useQueryState } from 'nuqs';
6
- import { parseAsString } from 'nuqs/parsers';
5
+ import { parseAsString } from 'nuqs/server';
7
6
  import { memo, useEffect } from 'react';
8
7
  import { createStoreUpdater } from 'zustand-utils';
9
8
 
@@ -16,10 +15,6 @@ const SessionHydration = memo(() => {
16
15
  const { mobile } = useResponsive();
17
16
  useStoreUpdater('isMobile', mobile);
18
17
 
19
- const router = useRouter();
20
- // TODO: 后续可以把 router 从 useSessionStore 移除
21
- useStoreUpdater('router', router);
22
-
23
18
  // two-way bindings the url and session store
24
19
  const [session, setSession] = useQueryState(
25
20
  'session',
@@ -1,4 +1,3 @@
1
- import { useResponsive } from 'antd-style';
2
1
  import Link from 'next/link';
3
2
  import { memo } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
@@ -6,24 +5,18 @@ import { useTranslation } from 'react-i18next';
6
5
  import { DEFAULT_INBOX_AVATAR } from '@/const/meta';
7
6
  import { INBOX_SESSION_ID } from '@/const/session';
8
7
  import { SESSION_CHAT_URL } from '@/const/url';
8
+ import { useIsMobile } from '@/hooks/useIsMobile';
9
9
  import { useSessionStore } from '@/store/session';
10
10
 
11
11
  import ListItem from '../ListItem';
12
12
 
13
13
  const Inbox = memo(() => {
14
14
  const { t } = useTranslation('chat');
15
- const { mobile } = useResponsive();
16
- const [activeId, switchSession] = useSessionStore((s) => [s.activeId, s.switchSession]);
15
+ const mobile = useIsMobile();
16
+ const activeId = useSessionStore((s) => s.activeId);
17
17
 
18
18
  return (
19
- <Link
20
- aria-label={t('inbox.title')}
21
- href={SESSION_CHAT_URL(INBOX_SESSION_ID, mobile)}
22
- onClick={(e) => {
23
- e.preventDefault();
24
- switchSession(INBOX_SESSION_ID);
25
- }}
26
- >
19
+ <Link aria-label={t('inbox.title')} href={SESSION_CHAT_URL(INBOX_SESSION_ID, mobile)}>
27
20
  <ListItem
28
21
  active={activeId === INBOX_SESSION_ID}
29
22
  avatar={DEFAULT_INBOX_AVATAR}
@@ -24,11 +24,7 @@ interface SessionListProps {
24
24
  showAddButton?: boolean;
25
25
  }
26
26
  const SessionList = memo<SessionListProps>(({ dataSource, groupId, showAddButton = true }) => {
27
- const [activeSession, switchSession, isInit] = useSessionStore((s) => [
28
- s.activeSession,
29
- s.switchSession,
30
- sessionSelectors.isSessionListInit(s),
31
- ]);
27
+ const isInit = useSessionStore((s) => sessionSelectors.isSessionListInit(s));
32
28
  const { styles } = useStyles();
33
29
 
34
30
  const { mobile } = useResponsive();
@@ -38,15 +34,7 @@ const SessionList = memo<SessionListProps>(({ dataSource, groupId, showAddButton
38
34
  ) : dataSource.length > 0 ? (
39
35
  dataSource.map(({ id }) => (
40
36
  <LazyLoad className={styles} key={id}>
41
- <Link
42
- aria-label={id}
43
- href={SESSION_CHAT_URL(id, mobile)}
44
- onClick={(e) => {
45
- e.preventDefault();
46
- if (mobile) switchSession(id);
47
- else activeSession(id);
48
- }}
49
- >
37
+ <Link aria-label={id} href={SESSION_CHAT_URL(id, mobile)}>
50
38
  <SessionItem id={id} />
51
39
  </Link>
52
40
  </LazyLoad>
@@ -5,7 +5,6 @@ import { memo, useEffect } from 'react';
5
5
 
6
6
  import { messageService } from '@/services/message';
7
7
  import { sessionService } from '@/services/session';
8
- import { useSessionStore } from '@/store/session';
9
8
 
10
9
  const checkHasConversation = async () => {
11
10
  const hasMessages = await messageService.hasMessages();
@@ -15,14 +14,11 @@ const checkHasConversation = async () => {
15
14
 
16
15
  const Redirect = memo(() => {
17
16
  const router = useRouter();
18
- const [switchSession] = useSessionStore((s) => [s.switchSession]);
19
17
 
20
18
  useEffect(() => {
21
19
  checkHasConversation().then((hasData) => {
22
20
  if (hasData) {
23
21
  router.replace('/chat');
24
-
25
- switchSession();
26
22
  } else {
27
23
  router.replace('/welcome');
28
24
  }
@@ -14,7 +14,7 @@ const Desktop = memo<PropsWithChildren>(({ children }) => (
14
14
  <SideBar />
15
15
  <Flexbox flex={1} height={'100%'} style={{ position: 'relative' }}>
16
16
  <Header />
17
- <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'scroll' }}>
17
+ <Flexbox align={'center'} flex={1} padding={24} style={{ overflowY: 'scroll' }}>
18
18
  <SafeSpacing />
19
19
  <Center gap={16} width={'100%'}>
20
20
  {children}
@@ -10,7 +10,6 @@ import { Flexbox } from 'react-layout-kit';
10
10
 
11
11
  import DataImporter from '@/features/DataImporter';
12
12
  import { useGlobalStore } from '@/store/global';
13
- import { useSessionStore } from '@/store/session';
14
13
 
15
14
  import Hero from './Hero';
16
15
  import { useStyles } from './style';
@@ -19,7 +18,6 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
19
18
  const { t } = useTranslation('welcome');
20
19
  const router = useRouter();
21
20
  const { styles } = useStyles();
22
- const [switchSession] = useSessionStore((s) => [s.switchSession]);
23
21
  const [switchBackToChat, isMobile] = useGlobalStore((s) => [s.switchBackToChat, s.isMobile]);
24
22
 
25
23
  return (
@@ -36,7 +34,7 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
36
34
  >
37
35
  <DataImporter
38
36
  onFinishImport={() => {
39
- switchSession();
37
+ router.push('/chat');
40
38
  }}
41
39
  >
42
40
  <Button block={mobile} size={'large'}>
@@ -1,7 +1,8 @@
1
1
  import { useResponsive } from 'antd-style';
2
+ import { useMemo } from 'react';
2
3
 
3
4
  export const useIsMobile = (): boolean => {
4
5
  const { mobile } = useResponsive();
5
6
 
6
- return !!mobile;
7
+ return useMemo(() => !!mobile, [mobile]);
7
8
  };
@@ -31,9 +31,6 @@ beforeEach(() => {
31
31
  vi.clearAllMocks();
32
32
  useSessionStore.setState({
33
33
  refreshSessions: mockRefresh,
34
- router: {
35
- push: mockRouterPush,
36
- } as unknown as AppRouterInstance,
37
34
  });
38
35
  });
39
36
 
@@ -72,9 +69,6 @@ describe('SessionAction', () => {
72
69
  expect(call[1]).toMatchObject({ config: { displayMode: 'docs' } });
73
70
 
74
71
  expect(createdSessionId).toBe(newSessionId);
75
- expect(mockRouterPush).toHaveBeenCalledWith(
76
- SESSION_CHAT_URL(newSessionId, result.current.isMobile),
77
- );
78
72
  });
79
73
 
80
74
  it('should create a new session but not switch to it if isSwitchSession is false', async () => {
@@ -131,35 +125,6 @@ describe('SessionAction', () => {
131
125
  });
132
126
  });
133
127
 
134
- describe('switchSession', () => {
135
- it('should switch to the provided session id', async () => {
136
- const { result } = renderHook(() => useSessionStore());
137
- const sessionId = 'session-id';
138
-
139
- act(() => {
140
- result.current.switchSession(sessionId);
141
- });
142
-
143
- expect(result.current.activeId).toBe(sessionId);
144
- expect(mockRouterPush).toHaveBeenCalledWith(
145
- SESSION_CHAT_URL(sessionId, result.current.isMobile),
146
- );
147
- });
148
-
149
- it('should switch to the inbox session id if none is provided', async () => {
150
- const { result } = renderHook(() => useSessionStore());
151
-
152
- act(() => {
153
- result.current.switchSession();
154
- });
155
-
156
- expect(result.current.activeId).toBe(INBOX_SESSION_ID);
157
- expect(mockRouterPush).toHaveBeenCalledWith(
158
- SESSION_CHAT_URL(INBOX_SESSION_ID, result.current.isMobile),
159
- );
160
- });
161
- });
162
-
163
128
  describe('activeSession', () => {
164
129
  it('should set the provided session id as active', async () => {
165
130
  const { result } = renderHook(() => useSessionStore());
@@ -5,7 +5,6 @@ import { DeepPartial } from 'utility-types';
5
5
  import { StateCreator } from 'zustand/vanilla';
6
6
 
7
7
  import { INBOX_SESSION_ID } from '@/const/session';
8
- import { SESSION_CHAT_URL } from '@/const/url';
9
8
  import { useClientDataSWR } from '@/libs/swr';
10
9
  import { sessionService } from '@/services/session';
11
10
  import { useGlobalStore } from '@/store/global';
@@ -56,10 +55,6 @@ export interface SessionAction {
56
55
  * @param id - sessionId
57
56
  */
58
57
  removeSession: (id: string) => void;
59
- /**
60
- * switch session url
61
- */
62
- switchSession: (sessionId?: string) => void;
63
58
  /**
64
59
  * A custom hook that uses SWR to fetch sessions data.
65
60
  */
@@ -86,9 +81,9 @@ export const createSessionSlice: StateCreator<
86
81
  },
87
82
 
88
83
  createSession: async (agent, isSwitchSession = true) => {
89
- const { switchSession, refreshSessions } = get();
84
+ const { activeSession, refreshSessions } = get();
90
85
 
91
- // 合并 settings 里的 defaultAgent
86
+ // merge the defaultAgent in settings
92
87
  const defaultAgent = merge(
93
88
  initLobeSession,
94
89
  settingsSelectors.defaultAgent(useGlobalStore.getState()),
@@ -99,14 +94,14 @@ export const createSessionSlice: StateCreator<
99
94
  const id = await sessionService.createNewSession(LobeSessionType.Agent, newSession);
100
95
  await refreshSessions();
101
96
 
102
- // 创建后是否跳转到对应会话,默认跳转
103
- if (isSwitchSession) switchSession(id);
97
+ // Whether to goto to the new session after creation, the default is to switch to
98
+ if (isSwitchSession) activeSession(id);
104
99
 
105
100
  return id;
106
101
  },
107
102
 
108
103
  duplicateSession: async (id) => {
109
- const { switchSession, refreshSessions } = get();
104
+ const { activeSession, refreshSessions } = get();
110
105
  const session = sessionSelectors.getSessionById(id)(get());
111
106
 
112
107
  if (!session) return;
@@ -123,7 +118,7 @@ export const createSessionSlice: StateCreator<
123
118
  }
124
119
 
125
120
  await refreshSessions();
126
- switchSession(newId);
121
+ activeSession(newId);
127
122
  },
128
123
 
129
124
  pinSession: async (sessionId, pinned) => {
@@ -140,20 +135,12 @@ export const createSessionSlice: StateCreator<
140
135
  await sessionService.removeSession(sessionId);
141
136
  await get().refreshSessions();
142
137
 
138
+ // If the active session deleted, switch to the inbox session
143
139
  if (sessionId === get().activeId) {
144
- get().switchSession();
140
+ get().activeSession(INBOX_SESSION_ID);
145
141
  }
146
142
  },
147
143
 
148
- switchSession: (sessionId = INBOX_SESSION_ID) => {
149
- const { isMobile, router } = get();
150
-
151
- get().activeSession(sessionId);
152
-
153
- // TODO: 后续可以把 router 移除
154
- router?.push(SESSION_CHAT_URL(sessionId, isMobile));
155
- },
156
-
157
144
  useFetchSessions: () =>
158
145
  useClientDataSWR<ChatSessionList>(FETCH_SESSIONS_KEY, sessionService.getSessionsWithGroup, {
159
146
  onSuccess: (data) => {
@@ -1,5 +1,3 @@
1
- import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
2
-
3
1
  import { DEFAULT_AGENT_META } from '@/const/meta';
4
2
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
5
3
  import { CustomSessionGroup, LobeAgentSession, LobeSessionType } from '@/types/session';
@@ -25,11 +23,6 @@ export interface SessionState {
25
23
  isSearching: boolean;
26
24
  isSessionsFirstFetchFinished: boolean;
27
25
  pinnedSessions: LobeAgentSession[];
28
- /**
29
- * 后续看看是否可以将 router 部分的逻辑移出去
30
- * @deprecated
31
- */
32
- router?: AppRouterInstance;
33
26
  searchKeywords: string;
34
27
  searchSessions: LobeAgentSession[];
35
28
  /**