Skip to content

Commit

Permalink
♻️ refactor: refactor for session server mode (lobehub#2163)
Browse files Browse the repository at this point in the history
* ♻️ refactor: refactor for session server mode

* ✅ test: fix test

* ✅ test: add tests

* 🚨 chore: fix lint
  • Loading branch information
arvinxx authored Apr 23, 2024
1 parent 2d52303 commit e012597
Show file tree
Hide file tree
Showing 36 changed files with 544 additions and 209 deletions.
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=*remark*
public-hoist-pattern[]=*semantic-release*
public-hoist-pattern[]=*stylelint*

public-hoist-pattern[]=@auth/core
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
"@next/eslint-plugin-next": "^14.2.2",
"@peculiar/webcrypto": "^1.4.6",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^15.0.2",
"@testing-library/react": "^15.0.4",
"@types/chroma-js": "^2.4.4",
"@types/debug": "^4.1.12",
"@types/diff": "^5.2.0",
Expand Down
16 changes: 11 additions & 5 deletions src/app/chat/features/SessionListContent/CollapseGroup/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Actions = memo<ActionsProps>(
({ id, openRenameModal, openConfigModal, onOpenChange, isCustomGroup, isPinned }) => {
const { t } = useTranslation('chat');
const { styles } = useStyles();
const { modal } = App.useApp();
const { modal, message } = App.useApp();

const [createSession, removeSessionGroup] = useSessionStore((s) => [
s.createSession,
Expand All @@ -48,9 +48,15 @@ const Actions = memo<ActionsProps>(
icon: <Icon icon={Plus} />,
key: 'newAgent',
label: t('newAgent'),
onClick: ({ domEvent }) => {
onClick: async ({ domEvent }) => {
domEvent.stopPropagation();
createSession({ group: id, pinned: isPinned });
const key = 'createNewAgentInGroup';
message.loading({ content: t('sessionGroup.creatingAgent'), duration: 0, key });

await createSession({ group: id, pinned: isPinned });

message.destroy(key);
message.success({ content: t('sessionGroup.createAgentSuccess') });
},
};

Expand Down Expand Up @@ -83,9 +89,9 @@ const Actions = memo<ActionsProps>(
modal.confirm({
centered: true,
okButtonProps: { danger: true },
onOk: () => {
onOk: async () => {
if (!id) return;
removeSessionGroup(id);
await removeSessionGroup(id);
},
rootClassName: styles.modalRoot,
title: t('sessionGroup.confirmRemoveGroupAlert'),
Expand Down
10 changes: 6 additions & 4 deletions src/app/chat/features/SessionListContent/DefaultMode.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { CollapseProps } from 'antd';
import isEqual from 'fast-deep-equal';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { useGlobalStore } from '@/store/global';
import { preferenceSelectors } from '@/store/global/selectors';
import { useSessionStore } from '@/store/session';
import { sessionSelectors } from '@/store/session/selectors';
import { SessionDefaultGroup } from '@/types/session';

import Actions from '../SessionListContent/CollapseGroup/Actions';
Expand All @@ -22,11 +24,11 @@ const SessionListContent = memo(() => {
const [configGroupModalOpen, setConfigGroupModalOpen] = useState(false);

const [useFetchSessions] = useSessionStore((s) => [s.useFetchSessions]);
const { data } = useFetchSessions();
useFetchSessions();

const pinnedSessions = data?.pinned;
const defaultSessions = data?.default;
const customSessionGroups = data?.customGroup;
const defaultSessions = useSessionStore(sessionSelectors.defaultSessions, isEqual);
const customSessionGroups = useSessionStore(sessionSelectors.customSessionGroups, isEqual);
const pinnedSessions = useSessionStore(sessionSelectors.pinnedSessions, isEqual);

const [sessionGroupKeys, updatePreference] = useGlobalStore((s) => [
preferenceSelectors.sessionGroupKeys(s),
Expand Down
13 changes: 4 additions & 9 deletions src/app/chat/features/SessionListContent/List/AddButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,13 @@ const AddButton = memo<{ groupId?: string }>(({ groupId }) => {
const { t } = useTranslation('chat');
const createSession = useSessionStore((s) => s.createSession);

const { mutate, isValidating } = useActionSWR('session.createSession', (groupId) =>
createSession({ group: groupId }),
);
const { mutate, isValidating } = useActionSWR(['session.createSession', groupId], () => {
return createSession({ group: groupId });
});

return (
<Flexbox style={{ margin: '12px 16px' }}>
<Button
block
icon={<Icon icon={Plus} />}
loading={isValidating}
onClick={() => mutate(groupId)}
>
<Button block icon={<Icon icon={Plus} />} loading={isValidating} onClick={() => mutate()}>
{t('newAgent')}
</Button>
</Flexbox>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ActionIcon, EditableText, SortableList } from '@lobehub/ui';
import { App, Popconfirm } from 'antd';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { PencilLine, Trash } from 'lucide-react';
import { memo, useState } from 'react';
Expand All @@ -25,7 +25,7 @@ const useStyles = createStyles(({ css }) => ({
const GroupItem = memo<SessionGroupItem>(({ id, name }) => {
const { t } = useTranslation('chat');
const { styles } = useStyles();
const { message } = App.useApp();
const { message, modal } = App.useApp();

const [editing, setEditing] = useState(false);
const [updateSessionGroupName, removeSessionGroup] = useSessionStore((s) => [
Expand All @@ -40,29 +40,34 @@ const GroupItem = memo<SessionGroupItem>(({ id, name }) => {
<>
<span className={styles.title}>{name}</span>
<ActionIcon icon={PencilLine} onClick={() => setEditing(true)} size={'small'} />
<Popconfirm
arrow={false}
okButtonProps={{
danger: true,
type: 'primary',
<ActionIcon
icon={Trash}
onClick={() => {
modal.confirm({
centered: true,
okButtonProps: {
danger: true,
type: 'primary',
},
onOk: async () => {
await removeSessionGroup(id);
},
title: t('sessionGroup.confirmRemoveGroupAlert'),
});
}}
onConfirm={() => {
removeSessionGroup(id);
}}
title={t('sessionGroup.confirmRemoveGroupAlert')}
>
<ActionIcon icon={Trash} size={'small'} />
</Popconfirm>
size={'small'}
/>
</>
) : (
<EditableText
editing={editing}
onChangeEnd={(input) => {
onChangeEnd={async (input) => {
if (name !== input) {
if (!input) return;
if (input.length === 0 || input.length > 20)
return message.warning(t('sessionGroup.tooLong'));
updateSessionGroupName(id, input);

await updateSessionGroupName(id, input);
message.success(t('sessionGroup.renameSuccess'));
}
setEditing(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Button } from 'antd';
import { createStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { Plus } from 'lucide-react';
import { memo } from 'react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

Expand Down Expand Up @@ -35,6 +35,7 @@ const ConfigGroupModal = memo<ModalProps>(({ open, onCancel }) => {
s.addSessionGroup,
s.updateSessionGroupSort,
]);
const [loading, setLoading] = useState(false);

return (
<Modal
Expand Down Expand Up @@ -67,7 +68,12 @@ const ConfigGroupModal = memo<ModalProps>(({ open, onCancel }) => {
<Button
block
icon={<Icon icon={Plus} />}
onClick={() => addSessionGroup(t('sessionGroup.newGroup'))}
loading={loading}
onClick={async () => {
setLoading(true);
await addSessionGroup(t('sessionGroup.newGroup'));
setLoading(false);
}}
>
{t('sessionGroup.createGroup')}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,29 @@ const CreateGroupModal = memo<CreateGroupModalProps>(
s.addSessionGroup,
]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);

return (
<div onClick={(e) => e.stopPropagation()}>
<Modal
allowFullscreen
onCancel={onCancel}
destroyOnClose
okButtonProps={{ loading }}
onCancel={(e) => {
setInput('');
onCancel?.(e);
}}
onOk={async (e: MouseEvent<HTMLButtonElement>) => {
if (!input) return;

if (input.length === 0 || input.length > 20)
return message.warning(t('sessionGroup.tooLong'));

setLoading(true);
const groupId = await addCustomGroup(input);
await updateSessionGroup(id, groupId);
toggleExpandSessionGroup(groupId, true);
setLoading(false);

message.success(t('sessionGroup.createSuccess'));
onCancel?.(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,27 @@ const RenameGroupModal = memo<RenameGroupModalProps>(({ id, open, onCancel }) =>
const group = useSessionStore((s) => sessionGroupSelectors.getGroupById(id)(s), isEqual);

const [input, setInput] = useState<string>();
const [loading, setLoading] = useState(false);

const { message } = App.useApp();
return (
<Modal
allowFullscreen
onCancel={onCancel}
onOk={(e) => {
destroyOnClose
okButtonProps={{ loading }}
onCancel={(e) => {
setInput(group?.name);
onCancel?.(e);
}}
onOk={async (e) => {
if (!input) return;
if (input.length === 0 || input.length > 20)
return message.warning(t('sessionGroup.tooLong'));
updateSessionGroupName(id, input);
setLoading(true);
await updateSessionGroupName(id, input);
message.success(t('sessionGroup.renameSuccess'));
setLoading(false);

onCancel?.(e);
}}
open={open}
Expand Down
4 changes: 2 additions & 2 deletions src/app/home/Redirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { sessionService } from '@/services/session';

const checkHasConversation = async () => {
const hasMessages = await messageService.hasMessages();
const hasAgents = await sessionService.countSessions();
return hasMessages || hasAgents === 0;
const hasAgents = await sessionService.hasSessions();
return hasMessages || hasAgents;
};

const Redirect = memo(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/app/welcome/(desktop)/features/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Footer = memo(() => {
return (
<Flexbox align={'center'} horizontal justify={'space-between'} style={{ padding: 16 }}>
<span style={{ color: theme.colorTextDescription }}>
©{new Date().getFullYear()} LobeHub
© 2023 - {new Date().getFullYear()} LobeHub, LLC
</span>
<Flexbox horizontal>
<ActionIcon
Expand Down
14 changes: 5 additions & 9 deletions src/app/welcome/features/Banner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import { Icon } from '@lobehub/ui';
import { Button } from 'antd';
import { SendHorizonal } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import DataImporter from '@/features/DataImporter';
import { useGlobalStore } from '@/store/global';

import Hero from './Hero';
Expand All @@ -32,15 +32,11 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
justify={'center'}
width={'100%'}
>
<DataImporter
onFinishImport={() => {
router.push('/chat');
}}
>
<Button block={mobile} size={'large'}>
{t('button.import')}
<Link href={'/market'}>
<Button block={mobile} size={'large'} type={'default'}>
{t('button.market')}
</Button>
</DataImporter>
</Link>
<Button
block={mobile}
onClick={() => (isMobile ? router.push('/chat') : switchBackToChat())}
Expand Down
4 changes: 2 additions & 2 deletions src/database/client/models/__tests__/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ describe('SessionModel', () => {
await SessionModel.create('agent', sessionData);

const sessionsWithGroups = await SessionModel.queryWithGroups();
expect(sessionsWithGroups.all).toHaveLength(1);
expect(sessionsWithGroups.all[0]).toEqual(expect.objectContaining(sessionData));
expect(sessionsWithGroups.sessions).toHaveLength(1);
expect(sessionsWithGroups.sessions[0]).toEqual(expect.objectContaining(sessionData));
});
});

Expand Down
18 changes: 4 additions & 14 deletions src/database/client/models/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,11 @@ class _SessionModel extends BaseModel {
}

async queryWithGroups(): Promise<ChatSessionList> {
const groups = await SessionGroupModel.query();
const customGroups = await this.queryByGroupIds(groups.map((item) => item.id));
const defaultItems = await this.querySessionsByGroupId(SessionDefaultGroup.Default);
const pinnedItems = await this.getPinnedSessions();
const sessionGroups = await SessionGroupModel.query();

const all = await this.query();
return {
all,
customGroup: groups.map((group) => ({
...group,
children: customGroups[group.id],
})),
default: defaultItems,
pinned: pinnedItems,
};
const sessions = await this.query();

return { sessionGroups, sessions };
}

/**
Expand Down
8 changes: 6 additions & 2 deletions src/locales/default/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ export default {
sessionGroup: {
config: '分组管理',
confirmRemoveGroupAlert: '即将删除该分组,删除后该分组的助手将移动到默认列表,请确认你的操作',
createAgentSuccess: '助手创建成功',
createGroup: '添加新分组',
createSuccess: '创建成功',
createSuccess: '分组创建成功',
creatingAgent: '助手创建中...',
inputPlaceholder: '请输入分组名称...',
moveGroup: '移动到分组',
newGroup: '新分组',
rename: '重命名分组',
renameSuccess: '重命名成功',
sortSuccess: '重新排序成功',
sorting: '分组排序更新中...',
tooLong: '分组名称长度需在 1-20 之内',
},
shareModal: {
Expand Down Expand Up @@ -126,6 +130,6 @@ export default {
dragDesc: '拖拽文件到这里,支持上传多个图片。按住 Shift 直接发送图片',
dragFileDesc: '拖拽图片和文件到这里,支持上传多个图片和文件。按住 Shift 直接发送图片或文件',
dragFileTitle: '上传文件',
dragTitle: '上传图片'
dragTitle: '上传图片',
},
};
1 change: 1 addition & 0 deletions src/locales/default/welcome.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
button: {
import: '导入配置',
market: '逛逛市场',
start: '立即开始',
},
header: '欢迎使用',
Expand Down
Loading

0 comments on commit e012597

Please sign in to comment.