Skip to content

Commit

Permalink
♻️ refactor: refactor the chat conversation implement (lobehub#4689)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx authored Nov 13, 2024
1 parent 80fbd9a commit 85b1630
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 154 deletions.
4 changes: 2 additions & 2 deletions src/app/(main)/chat/(workspace)/@conversation/default.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Conversation from '@/features/Conversation';
import { isMobileDevice } from '@/utils/server/responsive';

import ChatHydration from './features/ChatHydration';
import DesktopChatInput from './features/ChatInput/Desktop';
import MobileChatInput from './features/ChatInput/Mobile';
import ChatList from './features/ChatList';
import ZenModeToast from './features/ZenModeToast';

const ChatConversation = () => {
Expand All @@ -13,7 +13,7 @@ const ChatConversation = () => {
return (
<>
<ZenModeToast />
<Conversation mobile={mobile} />
<ChatList mobile={mobile} />
<ChatInput />
<ChatHydration />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import { Maximize2, Minimize2 } from 'lucide-react';
import { memo } from 'react';

import ActionBar from '@/features/ChatInput/ActionBar';
import { ActionKeys } from '@/features/ChatInput/types';

interface HeaderProps {
expand: boolean;
leftActions: ActionKeys[];
rightActions: ActionKeys[];
setExpand: (expand: boolean) => void;
}

const Header = memo<HeaderProps>(({ expand, setExpand }) => (
const Header = memo<HeaderProps>(({ expand, setExpand, leftActions, rightActions }) => (
<ActionBar
leftActions={leftActions}
rightActions={rightActions}
rightAreaEndRender={
<ActionIcon
icon={expand ? Minimize2 : Maximize2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { DraggablePanel } from '@lobehub/ui';
import { memo, useState } from 'react';
import { Flexbox } from 'react-layout-kit';

import {
CHAT_TEXTAREA_HEIGHT,
CHAT_TEXTAREA_MAX_HEIGHT,
HEADER_HEIGHT,
} from '@/const/layoutTokens';
import { CHAT_TEXTAREA_HEIGHT, CHAT_TEXTAREA_MAX_HEIGHT } from '@/const/layoutTokens';
import { ActionKeys } from '@/features/ChatInput/ActionBar/config';
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';

Expand All @@ -17,48 +14,71 @@ import Footer from './Footer';
import Head from './Header';
import TextArea from './TextArea';

const DesktopChatInput = memo(() => {
const [expand, setExpand] = useState<boolean>(false);
const defaultLeftActions = [
'model',
'fileUpload',
'knowledgeBase',
'temperature',
'history',
'stt',
'tools',
'token',
] as ActionKeys[];

const [inputHeight, updatePreference] = useGlobalStore((s) => [
systemStatusSelectors.inputHeight(s),
s.updateSystemStatus,
]);
const defaultRightActions = ['clear'] as ActionKeys[];

return (
<>
{!expand && <LocalFiles />}
<DraggablePanel
fullscreen={expand}
headerHeight={HEADER_HEIGHT}
maxHeight={CHAT_TEXTAREA_MAX_HEIGHT}
minHeight={CHAT_TEXTAREA_HEIGHT}
onSizeChange={(_, size) => {
if (!size) return;
interface DesktopChatInputProps {
leftActions?: ActionKeys[];
rightActions?: ActionKeys[];
}
const DesktopChatInput = memo<DesktopChatInputProps>(
({ leftActions = defaultLeftActions, rightActions = defaultRightActions }) => {
const [expand, setExpand] = useState<boolean>(false);

updatePreference({
inputHeight:
typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
});
}}
placement="bottom"
size={{ height: inputHeight, width: '100%' }}
style={{ zIndex: 10 }}
>
<Flexbox
gap={8}
height={'100%'}
padding={'12px 0 16px'}
style={{ minHeight: CHAT_TEXTAREA_HEIGHT, position: 'relative' }}
const [inputHeight, updatePreference] = useGlobalStore((s) => [
systemStatusSelectors.inputHeight(s),
s.updateSystemStatus,
]);

return (
<>
{!expand && <LocalFiles />}
<DraggablePanel
fullscreen={expand}
maxHeight={CHAT_TEXTAREA_MAX_HEIGHT}
minHeight={CHAT_TEXTAREA_HEIGHT}
onSizeChange={(_, size) => {
if (!size) return;

updatePreference({
inputHeight:
typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
});
}}
placement="bottom"
size={{ height: inputHeight, width: '100%' }}
style={{ zIndex: 10 }}
>
<Head expand={expand} setExpand={setExpand} />
<TextArea setExpand={setExpand} />
<Footer expand={expand} setExpand={setExpand} />
</Flexbox>
</DraggablePanel>
</>
);
});
<Flexbox
gap={8}
height={'100%'}
padding={'12px 0 16px'}
style={{ minHeight: CHAT_TEXTAREA_HEIGHT, position: 'relative' }}
>
<Head
expand={expand}
leftActions={leftActions}
rightActions={rightActions}
setExpand={setExpand}
/>
<TextArea setExpand={setExpand} />
<Footer expand={expand} setExpand={setExpand} />
</Flexbox>
</DraggablePanel>
</>
);
},
);

DesktopChatInput.displayName = 'DesktopChatInput';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TextAreaRef } from 'antd/es/input/TextArea';
import { memo, useRef, useState } from 'react';

import ActionBar from '@/features/ChatInput/ActionBar';
import { ActionKeys } from '@/features/ChatInput/ActionBar/config';
import STT from '@/features/ChatInput/STT';
import SaveTopic from '@/features/ChatInput/Topic';
import { useSendMessage } from '@/features/ChatInput/useSend';
Expand All @@ -15,6 +16,18 @@ import Files from './Files';
import InputArea from './InputArea';
import SendButton from './Send';

const defaultLeftActions = [
'model',
'fileUpload',
'knowledgeBase',
'temperature',
'history',
'tools',
'token',
] as ActionKeys[];

const defaultRightActions = ['clear'] as ActionKeys[];

const MobileChatInput = memo(() => {
const theme = useTheme();
const ref = useRef<TextAreaRef>(null);
Expand Down Expand Up @@ -52,7 +65,12 @@ const MobileChatInput = memo(() => {
topAddons={
<>
<Files />
<ActionBar mobile padding={'0 8px'} rightAreaStartRender={<SaveTopic mobile />} />
<ActionBar
leftActions={defaultLeftActions}
padding={'0 8px'}
rightActions={defaultRightActions}
rightAreaStartRender={<SaveTopic mobile />}
/>
</>
}
value={value}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import isEqual from 'fast-deep-equal';
import React, { memo } from 'react';

import { WELCOME_GUIDE_CHAT_ID } from '@/const/session';
import { VirtualizedList } from '@/features/Conversation';
import { useChatStore } from '@/store/chat';
import { chatSelectors } from '@/store/chat/selectors';
import { useSessionStore } from '@/store/session';

interface ListProps {
mobile?: boolean;
}

const Content = memo<ListProps>(({ mobile }) => {
const [activeTopicId, useFetchMessages] = useChatStore((s) => [
s.activeTopicId,
s.useFetchMessages,
]);

const [sessionId] = useSessionStore((s) => [s.activeId]);
useFetchMessages(sessionId, activeTopicId);

const data = useChatStore((s) => {
const showInboxWelcome = chatSelectors.showInboxWelcome(s);
if (showInboxWelcome) return [WELCOME_GUIDE_CHAT_ID];

return chatSelectors.currentChatIDsWithGuideMessage(s);
}, isEqual);

return <VirtualizedList dataSource={data} mobile={mobile} />;
});

export default Content;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Suspense, lazy } from 'react';
import { Flexbox } from 'react-layout-kit';

import { SkeletonList } from '@/features/Conversation';

const Content = lazy(() => import('./Content'));

interface ChatListProps {
mobile?: boolean;
}

const ChatList = ({ mobile }: ChatListProps) => (
<Flexbox
flex={1}
style={{
overflowX: 'hidden',
overflowY: 'auto',
position: 'relative',
}}
width={'100%'}
>
<Suspense fallback={<SkeletonList mobile={mobile} />}>
<Content mobile={mobile} />
</Suspense>
</Flexbox>
);

export default ChatList;
21 changes: 1 addition & 20 deletions src/features/ChatInput/ActionBar/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,4 @@ export const actionMap = {
tools: Tools,
} as const;

type ActionMap = typeof actionMap;

export type ActionKeys = keyof ActionMap;

type getActionList = (mobile?: boolean) => ActionKeys[];

// we can make these action lists configurable in the future
export const getLeftActionList: getActionList = (mobile) =>
[
'model',
'fileUpload',
'knowledgeBase',
'temperature',
'history',
!mobile && 'stt',
'tools',
'token',
].filter(Boolean) as ActionKeys[];

export const getRightActionList: getActionList = () => ['clear'].filter(Boolean) as ActionKeys[];
export type ActionKeys = keyof typeof actionMap;
53 changes: 25 additions & 28 deletions src/features/ChatInput/ActionBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChatInputActionBar } from '@lobehub/ui';
import { ReactNode, memo, useMemo } from 'react';
import { ReactNode, memo } from 'react';

import { ActionKeys, actionMap, getLeftActionList, getRightActionList } from './config';
import { ActionKeys, actionMap } from './config';

const RenderActionList = ({ dataSource }: { dataSource: ActionKeys[] }) => (
<>
Expand All @@ -13,46 +13,43 @@ const RenderActionList = ({ dataSource }: { dataSource: ActionKeys[] }) => (
);

export interface ActionBarProps {
leftActions: ActionKeys[];
leftAreaEndRender?: ReactNode;
leftAreaStartRender?: ReactNode;
mobile?: boolean;
padding?: number | string;
rightActions: ActionKeys[];
rightAreaEndRender?: ReactNode;
rightAreaStartRender?: ReactNode;
}

const ActionBar = memo<ActionBarProps>(
({
padding = '0 16px',
mobile,
rightAreaStartRender,
rightAreaEndRender,
leftAreaStartRender,
leftAreaEndRender,
}) => {
const leftActionList = useMemo(() => getLeftActionList(mobile), [mobile]);
const rightActionList = useMemo(() => getRightActionList(mobile), [mobile]);

return (
<ChatInputActionBar
leftAddons={
<>
{leftAreaStartRender}
<RenderActionList dataSource={leftActionList} />
{leftAreaEndRender}
</>
}
padding={padding}
rightAddons={
<>
{rightAreaStartRender}
<RenderActionList dataSource={rightActionList} />
{rightAreaEndRender}
</>
}
/>
);
},
leftActions,
rightActions,
}) => (
<ChatInputActionBar
leftAddons={
<>
{leftAreaStartRender}
<RenderActionList dataSource={leftActions} />
{leftAreaEndRender}
</>
}
padding={padding}
rightAddons={
<>
{rightAreaStartRender}
<RenderActionList dataSource={rightActions} />
{rightAreaEndRender}
</>
}
/>
),
);

export default ActionBar;
1 change: 1 addition & 0 deletions src/features/ChatInput/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { ActionKeys } from './ActionBar/config';
2 changes: 1 addition & 1 deletion src/features/Conversation/components/ChatItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ const Item = memo<ChatListItemProps>(({ index, id }) => {
return (
item && (
<>
{enableHistoryDivider && <History />}
{enableHistoryDivider && <History />}
<ChatItem
actions={
<ActionsBar
Expand Down
Loading

0 comments on commit 85b1630

Please sign in to comment.