import React, { useCallback, useMemo, useState } from 'react'; import { Box, Button, Flex, useTheme, IconButton } from '@chakra-ui/react'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useEditTitle } from '@/web/common/hooks/useEditTitle'; import { useRouter } from 'next/router'; import Avatar from '@/components/Avatar'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs'; import { useUserStore } from '@/web/support/user/useUserStore'; import { AppListItemType } from '@fastgpt/global/core/app/type'; import { useI18n } from '@/web/context/I18n'; import MyMenu from '@fastgpt/web/components/common/MyMenu'; import SelectOneResource from '@/components/common/folder/SelectOneResource'; import { GetResourceFolderListProps, GetResourceListItemResponse } from '@fastgpt/global/common/parentFolder/type'; import { getMyApps } from '@/web/core/app/api'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useContextSelector } from 'use-context-selector'; import { ChatContext } from '@/web/core/chat/context/chatContext'; import MyBox from '@fastgpt/web/components/common/MyBox'; type HistoryItemType = { id: string; title: string; customTitle?: string; top?: boolean; }; enum TabEnum { recently = 'recently', 'app' = 'app', 'history' = 'history' } const ChatHistorySlider = ({ appId, appName, appAvatar, apps = [], confirmClearText, onDelHistory, onClearHistory, onSetHistoryTop, onSetCustomTitle }: { appId?: string; appName: string; appAvatar: string; apps?: AppListItemType[]; confirmClearText: string; onDelHistory: (e: { chatId: string }) => void; onClearHistory: () => void; onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void; onSetCustomTitle?: (e: { chatId: string; title: string }) => void; }) => { const theme = useTheme(); const router = useRouter(); const isTeamChat = router.pathname === '/chat/team'; const { t } = useTranslation(); const { appT } = useI18n(); const { isPc } = useSystemStore(); const { userInfo } = useUserStore(); const [currentTab, setCurrentTab] = useState(TabEnum.history); const { histories, onChangeChatId, onChangeAppId, chatId: activeChatId, isLoading } = useContextSelector(ChatContext, (v) => v); const concatHistory = useMemo(() => { const formatHistories: HistoryItemType[] = histories.map((item) => ({ id: item.chatId, title: item.title, customTitle: item.customTitle, top: item.top })); const newChat: HistoryItemType = { id: activeChatId, title: t('core.chat.New Chat') }; const activeChat = histories.find((item) => item.chatId === activeChatId); return !activeChat ? [newChat].concat(formatHistories) : formatHistories; }, [activeChatId, histories, t]); const showApps = apps?.length > 0; // custom title edit const { onOpenModal, EditModal: EditTitleModal } = useEditTitle({ title: t('core.chat.Custom History Title'), placeholder: t('core.chat.Custom History Title Description') }); const { openConfirm, ConfirmModal } = useConfirm({ content: confirmClearText }); const canRouteToDetail = useMemo( () => appId && userInfo?.team.permission.hasWritePer, [appId, userInfo?.team.permission.hasWritePer] ); const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => { return getMyApps({ parentId }).then((res) => res.map((item) => ({ id: item._id, name: item.name, avatar: item.avatar, isFolder: item.type === AppTypeEnum.folder })) ); }, []); return ( {isPc && ( canRouteToDetail && router.push({ pathname: '/app/detail', query: { appId } }) } > {appName} )} {/* menu */} {!isPc && appId && ( flex={'1 0 0'} mr={1} inlineStyles={{ px: 1 }} list={[ ...(isTeamChat ? [{ label: t('App'), value: TabEnum.recently }] : [ { label: t('core.chat.Recent use'), value: TabEnum.recently }, { label: t('App'), value: TabEnum.app } ]), { label: t('core.chat.History'), value: TabEnum.history } ]} value={currentTab} onChange={setCurrentTab} /> )} {/* Clear */} {isPc && ( openConfirm(() => { onClearHistory(); })() } > )} {/* chat history */} {(currentTab === TabEnum.history || isPc) && ( <> {concatHistory.map((item, i) => ( { onChangeChatId(item.id); } })} > {item.customTitle || item.title} {!!item.id && ( } aria-label={''} /> } menuList={[ { children: [ ...(onSetHistoryTop ? [ { label: item.top ? t('core.chat.Unpin') : t('core.chat.Pin'), icon: 'core/chat/setTopLight', onClick: () => { onSetHistoryTop({ chatId: item.id, top: !item.top }); } } ] : []), ...(onSetCustomTitle ? [ { label: t('common.Custom Title'), icon: 'common/customTitleLight', onClick: () => { onOpenModal({ defaultVal: item.customTitle || item.title, onSuccess: (e) => onSetCustomTitle({ chatId: item.id, title: e }) }); } } ] : []), { label: t('common.Delete'), icon: 'delete', onClick: () => { onDelHistory({ chatId: item.id }); if (item.id === activeChatId) { onChangeChatId(); } }, type: 'danger' } ] } ]} /> )} ))} )} {currentTab === TabEnum.recently && !isPc && ( <> {Array.isArray(apps) && apps.map((item) => ( onChangeAppId(item._id) })} > {item.name} ))} )} {currentTab === TabEnum.app && !isPc && ( <> { if (!id) return; onChangeAppId(id); }} server={getAppList} /> )} {/* exec */} {!isPc && appId && !isTeamChat && ( router.push('/app/list')} > } bg={'white'} boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} size={'smSquare'} borderRadius={'50%'} aria-label={''} /> {t('core.chat.Exit Chat')} )} ); }; export default ChatHistorySlider;