@lobehub/chat 0.151.7 → 0.151.8

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 (37) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/index.tsx +9 -6
  4. package/src/app/(main)/@nav/_layout/Mobile.tsx +71 -0
  5. package/src/app/(main)/@nav/default.tsx +10 -0
  6. package/src/app/(main)/_layout/Desktop.tsx +33 -0
  7. package/src/app/(main)/_layout/Mobile.tsx +24 -0
  8. package/src/app/(main)/_layout/type.ts +6 -0
  9. package/src/app/(main)/chat/(mobile)/features/ChatInput/index.tsx +1 -1
  10. package/src/app/(main)/chat/(mobile)/index.tsx +4 -3
  11. package/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx +1 -0
  12. package/src/app/(main)/chat/(mobile)/mobile/page.tsx +6 -9
  13. package/src/app/(main)/chat/settings/(mobile)/index.tsx +4 -3
  14. package/src/app/(main)/error.tsx +5 -0
  15. package/src/app/(main)/layout.tsx +11 -0
  16. package/src/app/(main)/market/_layout/Mobile/index.tsx +6 -8
  17. package/src/app/(main)/market/features/AgentSearchBar/index.tsx +15 -12
  18. package/src/app/(main)/not-found.tsx +3 -0
  19. package/src/app/(main)/settings/_layout/Mobile/index.tsx +3 -3
  20. package/src/app/layout.tsx +1 -4
  21. package/src/app/page.tsx +2 -0
  22. package/src/components/404/index.tsx +4 -2
  23. package/src/components/Error/index.tsx +5 -3
  24. package/src/components/server/MobileNavLayout.tsx +60 -0
  25. package/src/components/server/ServerLayout.tsx +6 -7
  26. package/src/database/client/models/__tests__/sessionGroup.test.ts +18 -0
  27. package/src/features/Conversation/index.tsx +3 -3
  28. package/src/styles/mobileHeader.ts +1 -0
  29. package/src/layout/DefaultLayout/Desktop/index.tsx +0 -32
  30. package/src/layout/DefaultLayout/Mobile/index.tsx +0 -59
  31. package/src/layout/DefaultLayout/index.ts +0 -2
  32. package/src/layout/routes/Desktop.tsx +0 -18
  33. package/src/layout/routes/Mobile.tsx +0 -24
  34. package/src/layout/routes/index.tsx +0 -8
  35. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/Avatar.tsx +0 -0
  36. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/BottomActions.tsx +0 -0
  37. /package/src/{layout/DefaultLayout/Desktop/SideBar → app/(main)/@nav/_layout/Desktop}/TopActions.tsx +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.151.8](https://github.com/lobehub/lobe-chat/compare/v0.151.7...v0.151.8)
6
+
7
+ <sup>Released on **2024-04-30**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Move NavBar to `[@nav](https://github.com/nav)` slot route.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Move NavBar to `[@nav](https://github.com/nav)` slot route, closes [#2306](https://github.com/lobehub/lobe-chat/issues/2306) ([aee7231](https://github.com/lobehub/lobe-chat/commit/aee7231))
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 0.151.7](https://github.com/lobehub/lobe-chat/compare/v0.151.6...v0.151.7)
6
31
 
7
32
  <sup>Released on **2024-04-30**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.151.7",
3
+ "version": "0.151.8",
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",
@@ -1,17 +1,16 @@
1
+ 'use client';
2
+
1
3
  import { SideNav } from '@lobehub/ui';
2
4
  import { memo } from 'react';
3
5
 
4
- import { SidebarTabKey } from '@/store/global/initialState';
6
+ import { useActiveTabKey } from '@/hooks/useActiveTabKey';
5
7
 
6
8
  import Avatar from './Avatar';
7
9
  import BottomActions from './BottomActions';
8
10
  import TopActions from './TopActions';
9
11
 
10
- interface Props {
11
- sidebarKey?: SidebarTabKey;
12
- }
13
-
14
- export default memo<Props>(({ sidebarKey }) => {
12
+ const Nav = memo(() => {
13
+ const sidebarKey = useActiveTabKey();
15
14
  return (
16
15
  <SideNav
17
16
  avatar={<Avatar />}
@@ -21,3 +20,7 @@ export default memo<Props>(({ sidebarKey }) => {
21
20
  />
22
21
  );
23
22
  });
23
+
24
+ Nav.displayName = 'DesktopNav';
25
+
26
+ export default Nav;
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+
3
+ import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import { Bot, MessageSquare, User } from 'lucide-react';
6
+ import { useRouter } from 'next/navigation';
7
+ import { rgba } from 'polished';
8
+ import { memo, useMemo } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+
11
+ import { useActiveTabKey } from '@/hooks/useActiveTabKey';
12
+ import { SidebarTabKey } from '@/store/global/initialState';
13
+
14
+ const useStyles = createStyles(({ css, token }) => ({
15
+ active: css`
16
+ svg {
17
+ fill: ${rgba(token.colorPrimary, 0.33)};
18
+ }
19
+ `,
20
+ container: css`
21
+ position: fixed;
22
+ z-index: 100;
23
+ right: 0;
24
+ bottom: 0;
25
+ left: 0;
26
+ `,
27
+ }));
28
+
29
+ const Nav = memo(() => {
30
+ const { t } = useTranslation('common');
31
+ const { styles } = useStyles();
32
+ const activeKey = useActiveTabKey();
33
+ const router = useRouter();
34
+ const items: MobileTabBarProps['items'] = useMemo(
35
+ () => [
36
+ {
37
+ icon: (active) => (
38
+ <Icon className={active ? styles.active : undefined} icon={MessageSquare} />
39
+ ),
40
+ key: SidebarTabKey.Chat,
41
+ onClick: () => {
42
+ router.push('/chat');
43
+ },
44
+ title: t('tab.chat'),
45
+ },
46
+ {
47
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
48
+ key: SidebarTabKey.Market,
49
+ onClick: () => {
50
+ router.push('/market');
51
+ },
52
+ title: t('tab.market'),
53
+ },
54
+ {
55
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={User} />,
56
+ key: SidebarTabKey.Setting,
57
+ onClick: () => {
58
+ router.push('/settings');
59
+ },
60
+ title: t('tab.setting'),
61
+ },
62
+ ],
63
+ [t],
64
+ );
65
+
66
+ return <MobileTabBar activeKey={activeKey} className={styles.container} items={items} safeArea />;
67
+ });
68
+
69
+ Nav.displayName = 'MobileNav';
70
+
71
+ export default Nav;
@@ -0,0 +1,10 @@
1
+ import ServerLayout from '@/components/server/ServerLayout';
2
+
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
5
+
6
+ const Nav = ServerLayout({ Desktop, Mobile });
7
+
8
+ Nav.displayName = 'Nav';
9
+
10
+ export default Nav;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { useTheme } from 'antd-style';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { useIsPWA } from '@/hooks/useIsPWA';
8
+
9
+ import { LayoutProps } from './type';
10
+
11
+ const Layout = memo<LayoutProps>(({ children, nav }) => {
12
+ const isPWA = useIsPWA();
13
+ const theme = useTheme();
14
+
15
+ return (
16
+ <Flexbox
17
+ height={'100%'}
18
+ horizontal
19
+ style={{
20
+ borderTop: isPWA ? `1px solid ${theme.colorBorder}` : undefined,
21
+ position: 'relative',
22
+ }}
23
+ width={'100%'}
24
+ >
25
+ {nav}
26
+ {children}
27
+ </Flexbox>
28
+ );
29
+ });
30
+
31
+ Layout.displayName = 'DesktopMainLayout';
32
+
33
+ export default Layout;
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import { memo } from 'react';
5
+
6
+ import { LayoutProps } from './type';
7
+
8
+ const MOBILE_IGNORE_NAV_ROUTES = ['/settings/', '/chat/'];
9
+
10
+ const Layout = memo(({ children, nav }: LayoutProps) => {
11
+ const pathname = usePathname();
12
+ const hideNav = MOBILE_IGNORE_NAV_ROUTES.some((path) => pathname.startsWith(path));
13
+
14
+ return (
15
+ <>
16
+ {children}
17
+ {!hideNav && nav}
18
+ </>
19
+ );
20
+ });
21
+
22
+ Layout.displayName = 'MobileMainLayout';
23
+
24
+ export default Layout;
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export interface LayoutProps {
4
+ children: ReactNode;
5
+ nav: ReactNode;
6
+ }
@@ -27,7 +27,7 @@ const ChatInputMobileLayout = memo(() => {
27
27
  setExpand={setExpand}
28
28
  style={{
29
29
  background: `linear-gradient(to bottom, ${theme.colorFillQuaternary}, transparent)`,
30
- width: '100vw',
30
+ width: '100%',
31
31
  }}
32
32
  textAreaLeftAddons={<STT mobile />}
33
33
  textAreaRightAddons={
@@ -3,6 +3,8 @@
3
3
  import { useRouter } from 'next/navigation';
4
4
  import { memo, useEffect } from 'react';
5
5
 
6
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
7
+
6
8
  import SessionHeader from './features/SessionHeader';
7
9
  import SessionList from './features/SessionList';
8
10
 
@@ -15,10 +17,9 @@ const ChatMobilePage = memo(() => {
15
17
  }, []);
16
18
 
17
19
  return (
18
- <>
19
- <SessionHeader />
20
+ <MobileContentLayout header={<SessionHeader />} withNav>
20
21
  <SessionList />
21
- </>
22
+ </MobileContentLayout>
22
23
  );
23
24
  });
24
25
 
@@ -48,6 +48,7 @@ const MobileHeader = memo(() => {
48
48
  </>
49
49
  }
50
50
  showBackButton
51
+ style={{ width: '100%' }}
51
52
  />
52
53
  );
53
54
  });
@@ -2,8 +2,8 @@
2
2
 
3
3
  import dynamic from 'next/dynamic';
4
4
  import { memo } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
6
5
 
6
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
7
7
  import Conversation from '@/features/Conversation';
8
8
 
9
9
  import SessionHydration from '../../components/SessionHydration';
@@ -15,15 +15,12 @@ const TopicList = dynamic(() => import('../features/TopicList'));
15
15
 
16
16
  const Chat = memo(() => {
17
17
  return (
18
- <>
19
- <ChatHeader />
20
- <Flexbox height={'calc(100% - 44px)'} horizontal>
21
- <Conversation chatInput={<ChatInput />} mobile />
22
- <TopicList />
23
- <TelemetryNotification mobile />
24
- </Flexbox>
18
+ <MobileContentLayout header={<ChatHeader />}>
19
+ <Conversation chatInput={<ChatInput />} mobile />
20
+ <TopicList />
21
+ <TelemetryNotification mobile />
25
22
  <SessionHydration />
26
- </>
23
+ </MobileContentLayout>
27
24
  );
28
25
  });
29
26
  export default Chat;
@@ -2,14 +2,15 @@
2
2
 
3
3
  import { memo } from 'react';
4
4
 
5
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
6
+
5
7
  import EditPage from '../features/EditPage';
6
8
  import Header from './Header';
7
9
 
8
10
  const ChatSettings = memo(() => (
9
- <>
10
- <Header />
11
+ <MobileContentLayout header={<Header />} withNav={false}>
11
12
  <EditPage />
12
- </>
13
+ </MobileContentLayout>
13
14
  ));
14
15
 
15
16
  export default ChatSettings;
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+
5
+ export default dynamic(() => import('@/components/Error'));
@@ -0,0 +1,11 @@
1
+ import ServerLayout from '@/components/server/ServerLayout';
2
+
3
+ import Desktop from './_layout/Desktop';
4
+ import Mobile from './_layout/Mobile';
5
+ import { LayoutProps } from './_layout/type';
6
+
7
+ const MainLayout = ServerLayout<LayoutProps>({ Desktop, Mobile });
8
+
9
+ MainLayout.displayName = 'MainLayout';
10
+
11
+ export default MainLayout;
@@ -1,18 +1,16 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import { Flexbox } from 'react-layout-kit';
2
+
3
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
3
4
 
4
5
  import AgentSearchBar from '../../features/AgentSearchBar';
5
6
  import Header from './Header';
6
7
 
7
8
  const MobileLayout = ({ children }: PropsWithChildren) => {
8
9
  return (
9
- <>
10
- <Header />
11
- <Flexbox flex={1} gap={16} style={{ padding: 16 }}>
12
- <AgentSearchBar mobile />
13
- {children}
14
- </Flexbox>
15
- </>
10
+ <MobileContentLayout gap={16} header={<Header />} style={{ paddingInline: 16 }} withNav>
11
+ <AgentSearchBar mobile />
12
+ {children}
13
+ </MobileContentLayout>
16
14
  );
17
15
  };
18
16
 
@@ -3,6 +3,7 @@
3
3
  import { SearchBar } from '@lobehub/ui';
4
4
  import { memo, useCallback, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
6
7
 
7
8
  import { useIsMobile } from '@/hooks/useIsMobile';
8
9
  import { useMarketStore } from '@/store/market';
@@ -19,18 +20,20 @@ const AgentSearchBar = memo<{ mobile?: boolean }>(({ mobile: controlledMobile })
19
20
  }, [value, setKeywords]);
20
21
 
21
22
  return (
22
- <SearchBar
23
- allowClear
24
- enableShortKey={!mobile}
25
- onChange={(e) => setValue(e.target.value)}
26
- onPressEnter={handleSearch}
27
- onSubmit={handleSearch}
28
- placeholder={t('search.placeholder')}
29
- shortKey={'k'}
30
- spotlight={!mobile}
31
- type={mobile ? 'block' : 'ghost'}
32
- value={value}
33
- />
23
+ <Flexbox flex={'none'} paddingBlock={mobile ? 8 : 0}>
24
+ <SearchBar
25
+ allowClear
26
+ enableShortKey={!mobile}
27
+ onChange={(e) => setValue(e.target.value)}
28
+ onPressEnter={handleSearch}
29
+ onSubmit={handleSearch}
30
+ placeholder={t('search.placeholder')}
31
+ shortKey={'k'}
32
+ spotlight={!mobile}
33
+ type={mobile ? 'block' : 'ghost'}
34
+ value={value}
35
+ />
36
+ </Flexbox>
34
37
  );
35
38
  });
36
39
 
@@ -0,0 +1,3 @@
1
+ import dynamic from 'next/dynamic';
2
+
3
+ export default dynamic(() => import('@/components/404'));
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { PropsWithChildren } from 'react';
4
4
 
5
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
5
6
  import { useIsSubSlug } from '@/hooks/useIsSubSlug';
6
7
 
7
8
  import SubSettingHeader from './SubSettingHeader';
@@ -11,10 +12,9 @@ const MobileLayout = ({ children }: PropsWithChildren) => {
11
12
 
12
13
  if (isSubPath)
13
14
  return (
14
- <>
15
- <SubSettingHeader />
15
+ <MobileContentLayout header={<SubSettingHeader />} withNav={false}>
16
16
  {children}
17
- </>
17
+ </MobileContentLayout>
18
18
  );
19
19
 
20
20
  return children;
@@ -8,7 +8,6 @@ import Analytics from '@/components/Analytics';
8
8
  import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
9
9
  import AuthProvider from '@/layout/AuthProvider';
10
10
  import GlobalProvider from '@/layout/GlobalProvider';
11
- import LayoutRoutes from '@/layout/routes';
12
11
  import { isMobileDevice } from '@/utils/responsive';
13
12
 
14
13
  const RootLayout = async ({ children }: PropsWithChildren) => {
@@ -21,9 +20,7 @@ const RootLayout = async ({ children }: PropsWithChildren) => {
21
20
  <html dir={direction} lang={lang?.value || DEFAULT_LANG} suppressHydrationWarning>
22
21
  <body>
23
22
  <GlobalProvider>
24
- <AuthProvider>
25
- <LayoutRoutes>{children}</LayoutRoutes>
26
- </AuthProvider>
23
+ <AuthProvider>{children}</AuthProvider>
27
24
  </GlobalProvider>
28
25
  <Analytics />
29
26
  <SpeedInsights />
package/src/app/page.tsx CHANGED
@@ -14,6 +14,8 @@ const Page = () => {
14
14
  );
15
15
  };
16
16
 
17
+ Page.displayName = 'Loading';
18
+
17
19
  export default Page;
18
20
 
19
21
  export const metadata: Metadata = {
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { MAX_WIDTH } from '@/const/layoutTokens';
11
11
 
12
- const Page404 = memo(() => {
12
+ const NotFound = memo(() => {
13
13
  const { t } = useTranslation('error');
14
14
  return (
15
15
  <Flexbox align={'center'} justify={'center'} style={{ height: '100%', width: '100%' }}>
@@ -38,4 +38,6 @@ const Page404 = memo(() => {
38
38
  );
39
39
  });
40
40
 
41
- export default Page404;
41
+ NotFound.displayName = 'NotFound';
42
+
43
+ export default NotFound;
@@ -11,12 +11,12 @@ import { MAX_WIDTH } from '@/const/layoutTokens';
11
11
 
12
12
  import { type ErrorType, sentryCaptureException } from './sentryCaptureException';
13
13
 
14
- interface PageErrorProps {
14
+ interface ErrorCaptureProps {
15
15
  error: ErrorType;
16
16
  reset: () => void;
17
17
  }
18
18
 
19
- const PageError = memo<PageErrorProps>(({ reset, error }) => {
19
+ const ErrorCapture = memo<ErrorCaptureProps>(({ reset, error }) => {
20
20
  const { t } = useTranslation('error');
21
21
 
22
22
  useLayoutEffect(() => {
@@ -53,4 +53,6 @@ const PageError = memo<PageErrorProps>(({ reset, error }) => {
53
53
  );
54
54
  });
55
55
 
56
- export default PageError;
56
+ ErrorCapture.displayName = 'ErrorCapture';
57
+
58
+ export default ErrorCapture;
@@ -0,0 +1,60 @@
1
+ import { ReactNode } from 'react';
2
+ import { Flexbox, type FlexboxProps } from 'react-layout-kit';
3
+
4
+ interface MobileContentLayoutProps extends FlexboxProps {
5
+ header?: ReactNode;
6
+ withNav?: boolean;
7
+ }
8
+
9
+ const MobileContentLayout = ({
10
+ children,
11
+ withNav,
12
+ style,
13
+ header,
14
+ ...rest
15
+ }: MobileContentLayoutProps) => {
16
+ const content = (
17
+ <Flexbox
18
+ height="100%"
19
+ style={{
20
+ overflowX: 'hidden',
21
+ overflowY: 'auto',
22
+ position: 'relative',
23
+ ...style,
24
+ // TabNav Height
25
+ paddingBottom: withNav ? 48 : style?.paddingBottom,
26
+ }}
27
+ width="100%"
28
+ {...rest}
29
+ >
30
+ {children}
31
+ </Flexbox>
32
+ );
33
+
34
+ if (!header) return content;
35
+
36
+ return (
37
+ <Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
38
+ {header}
39
+ <Flexbox
40
+ height="100%"
41
+ style={{
42
+ overflowX: 'hidden',
43
+ overflowY: 'auto',
44
+ position: 'relative',
45
+ ...style,
46
+ // TabNav Height
47
+ paddingBottom: withNav ? 48 : style?.paddingBottom,
48
+ }}
49
+ width="100%"
50
+ {...rest}
51
+ >
52
+ {children}
53
+ </Flexbox>
54
+ </Flexbox>
55
+ );
56
+ };
57
+
58
+ MobileContentLayout.displayName = 'MobileContentLayout';
59
+
60
+ export default MobileContentLayout;
@@ -2,17 +2,16 @@ import { FC, PropsWithChildren } from 'react';
2
2
 
3
3
  import { isMobileDevice } from '@/utils/responsive';
4
4
 
5
- interface ServerLayoutProps {
6
- Desktop: FC<PropsWithChildren>;
7
- Mobile: FC<PropsWithChildren>;
5
+ interface ServerLayoutProps<T> {
6
+ Desktop: FC<T>;
7
+ Mobile: FC<T>;
8
8
  }
9
9
 
10
10
  const ServerLayout =
11
- ({ Desktop, Mobile }: ServerLayoutProps) =>
12
- ({ children }: PropsWithChildren) => {
11
+ <T extends PropsWithChildren>({ Desktop, Mobile }: ServerLayoutProps<T>): FC<T> =>
12
+ (props: T) => {
13
13
  const mobile = isMobileDevice();
14
-
15
- return mobile ? <Mobile>{children}</Mobile> : <Desktop>{children}</Desktop>;
14
+ return mobile ? <Mobile {...props} /> : <Desktop {...props} />;
16
15
  };
17
16
 
18
17
  ServerLayout.displayName = 'ServerLayout';
@@ -89,6 +89,16 @@ describe('SessionGroupModel', () => {
89
89
  expect(session.group).not.toEqual(createdGroup.id);
90
90
  });
91
91
  });
92
+ it('should update associated sessions to default group when a session group is deleted', async () => {
93
+ const createdGroup = await SessionGroupModel.create(
94
+ sessionGroupData.name,
95
+ sessionGroupData.sort,
96
+ );
97
+ const sessionId = await SessionModel.create('agent', {}, createdGroup.id);
98
+ await SessionGroupModel.delete(createdGroup.id);
99
+ const updatedSession = await SessionModel.findById(sessionId.id);
100
+ expect(updatedSession.group).toEqual('default');
101
+ });
92
102
  });
93
103
 
94
104
  describe('query', () => {
@@ -153,6 +163,14 @@ describe('SessionGroupModel', () => {
153
163
  expect(fetchedGroups[0].id).toEqual(group2.id);
154
164
  expect(fetchedGroups[1].id).toEqual(group1.id);
155
165
  });
166
+
167
+ it('should sort session groups correctly when only b has a sort value', async () => {
168
+ const groupA = await SessionGroupModel.create('groupA'); // sort undefined
169
+ const groupB = await SessionGroupModel.create('groupB', 1); // sort defined
170
+ const fetchedGroups = await SessionGroupModel.query();
171
+ expect(fetchedGroups[0].id).toEqual(groupB.id);
172
+ expect(fetchedGroups[1].id).toEqual(groupA.id);
173
+ });
156
174
  });
157
175
 
158
176
  describe('updateOrder', () => {
@@ -9,14 +9,13 @@ import SkeletonList from './components/SkeletonList';
9
9
  const ChatList = lazy(() => import('./components/VirtualizedList'));
10
10
 
11
11
  const useStyles = createStyles(
12
- ({ css, responsive, stylish }) => css`
12
+ ({ css, responsive }) => css`
13
13
  position: relative;
14
14
  overflow-y: auto;
15
15
  height: 100%;
16
16
 
17
17
  ${responsive.mobile} {
18
- ${stylish.noScrollbar}
19
- width: 100vw;
18
+ width: 100%;
20
19
  }
21
20
  `,
22
21
  );
@@ -32,6 +31,7 @@ const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
32
31
  return (
33
32
  <Flexbox
34
33
  flex={1}
34
+ height={'100%'}
35
35
  // `relative` is required, ChatInput's absolute position needs it
36
36
  style={{ position: 'relative' }}
37
37
  >
@@ -3,5 +3,6 @@ import { CSSProperties } from 'react';
3
3
  export const mobileHeaderSticky: CSSProperties = {
4
4
  position: 'sticky',
5
5
  top: 0,
6
+ width: '100%',
6
7
  zIndex: 100,
7
8
  };
@@ -1,32 +0,0 @@
1
- 'use client';
2
-
3
- import { useTheme } from 'antd-style';
4
- import { PropsWithChildren, memo } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
6
-
7
- import ClientResponsiveLayout from '@/components/client/ClientResponsiveLayout';
8
- import { useActiveTabKey } from '@/hooks/useActiveTabKey';
9
- import { useIsPWA } from '@/hooks/useIsPWA';
10
-
11
- import SideBar from './SideBar';
12
-
13
- const Desktop = memo<PropsWithChildren>(({ children }) => {
14
- const isPWA = useIsPWA();
15
- const theme = useTheme();
16
-
17
- const sidebarKey = useActiveTabKey();
18
-
19
- return (
20
- <Flexbox
21
- height={'100%'}
22
- horizontal
23
- style={isPWA ? { borderTop: `1px solid ${theme.colorBorder}` } : {}}
24
- width={'100%'}
25
- >
26
- <SideBar sidebarKey={sidebarKey} />
27
- {children}
28
- </Flexbox>
29
- );
30
- });
31
-
32
- export default ClientResponsiveLayout({ Desktop, Mobile: () => import('../Mobile') });
@@ -1,59 +0,0 @@
1
- 'use client';
2
-
3
- import { type MobileNavBarTitleProps } from '@lobehub/ui';
4
- import { createStyles } from 'antd-style';
5
- import dynamic from 'next/dynamic';
6
- import { CSSProperties, PropsWithChildren, memo } from 'react';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import SafeSpacing from '@/components/SafeSpacing';
10
- import { SidebarTabKey } from '@/store/global/initialState';
11
-
12
- const MobileTabBar = dynamic(() => import('@/features/MobileTabBar'));
13
-
14
- const useStyles = createStyles(({ css, cx, stylish }) => ({
15
- container: cx(
16
- stylish.noScrollbar,
17
- css`
18
- position: relative;
19
- overflow: hidden auto;
20
- width: 100vw;
21
- height: 100%;
22
- `,
23
- ),
24
- mobileTabBar: css`
25
- position: fixed;
26
- z-index: 100;
27
- right: 0;
28
- bottom: 0;
29
- left: 0;
30
- `,
31
- }));
32
-
33
- interface AppMobileLayoutProps extends PropsWithChildren {
34
- className?: string;
35
- showTabBar?: boolean;
36
- style?: CSSProperties;
37
- tabBarKey?: SidebarTabKey;
38
- title?: MobileNavBarTitleProps;
39
- }
40
-
41
- const AppLayoutMobile = memo<AppMobileLayoutProps>(
42
- ({ children, showTabBar, tabBarKey, style, className }) => {
43
- const { styles, cx } = useStyles();
44
-
45
- return (
46
- <Flexbox className={cx(styles.container, className)} style={style}>
47
- {children}
48
- {showTabBar && (
49
- <>
50
- <SafeSpacing mobile position={'bottom'} />
51
- <MobileTabBar className={styles.mobileTabBar} tabBarKey={tabBarKey} />
52
- </>
53
- )}
54
- </Flexbox>
55
- );
56
- },
57
- );
58
-
59
- export default AppLayoutMobile;
@@ -1,2 +0,0 @@
1
- export { default as DefaultLayoutDesktop } from './Desktop';
2
- export { default as DefaultLayoutMobile } from './Mobile';
@@ -1,18 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname } from 'next/navigation';
4
- import { PropsWithChildren, memo } from 'react';
5
-
6
- import { DefaultLayoutDesktop } from '@/layout/DefaultLayout';
7
-
8
- const defaultLayoutRoutes = new Set(['/']);
9
-
10
- const DesktopLayout = memo<PropsWithChildren>(({ children }) => {
11
- const pathname = usePathname();
12
-
13
- if (defaultLayoutRoutes.has(pathname)) return children;
14
-
15
- return <DefaultLayoutDesktop>{children}</DefaultLayoutDesktop>;
16
- });
17
-
18
- export default DesktopLayout;
@@ -1,24 +0,0 @@
1
- 'use client';
2
-
3
- import { usePathname } from 'next/navigation';
4
- import { PropsWithChildren, memo } from 'react';
5
-
6
- import { useActiveTabKey } from '@/hooks/useActiveTabKey';
7
- import { useIsSubSlug } from '@/hooks/useIsSubSlug';
8
- import { DefaultLayoutMobile } from '@/layout/DefaultLayout';
9
-
10
- const MobileLayout = memo<PropsWithChildren>(({ children }) => {
11
- const pathname = usePathname();
12
- const tabBarKey = useActiveTabKey();
13
- const isSubPath = useIsSubSlug();
14
-
15
- if (pathname === '/') return children;
16
-
17
- return (
18
- <DefaultLayoutMobile showTabBar={!isSubPath} tabBarKey={tabBarKey}>
19
- {children}
20
- </DefaultLayoutMobile>
21
- );
22
- });
23
-
24
- export default MobileLayout;
@@ -1,8 +0,0 @@
1
- import ServerLayout from '@/components/server/ServerLayout';
2
-
3
- import Desktop from './Desktop';
4
- import Mobile from './Mobile';
5
-
6
- const LayoutRoutes = ServerLayout({ Desktop, Mobile });
7
-
8
- export default LayoutRoutes;