123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- import {
- useCallback,
- useState,
- } from 'react'
- import { useTranslation } from 'react-i18next'
- import {
- RiEditBoxLine,
- RiExpandRightLine,
- RiLayoutLeft2Line,
- } from '@remixicon/react'
- import { useChatWithHistoryContext } from '../context'
- import AppIcon from '@/app/components/base/app-icon'
- import ActionButton from '@/app/components/base/action-button'
- import Button from '@/app/components/base/button'
- import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
- import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
- import Confirm from '@/app/components/base/confirm'
- import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
- import LogoSite from '@/app/components/base/logo/logo-site'
- import type { ConversationItem } from '@/models/share'
- import cn from '@/utils/classnames'
- type Props = {
- isPanel?: boolean
- }
- const Sidebar = ({ isPanel }: Props) => {
- const { t } = useTranslation()
- const {
- appData,
- handleNewConversation,
- pinnedConversationList,
- conversationList,
- currentConversationId,
- handleChangeConversation,
- handlePinConversation,
- handleUnpinConversation,
- conversationRenaming,
- handleRenameConversation,
- handleDeleteConversation,
- sidebarCollapseState,
- handleSidebarCollapse,
- isMobile,
- isResponding,
- } = useChatWithHistoryContext()
- const isSidebarCollapsed = sidebarCollapseState
- const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
- const [showRename, setShowRename] = useState<ConversationItem | null>(null)
- const handleOperate = useCallback((type: string, item: ConversationItem) => {
- if (type === 'pin')
- handlePinConversation(item.id)
- if (type === 'unpin')
- handleUnpinConversation(item.id)
- if (type === 'delete')
- setShowConfirm(item)
- if (type === 'rename')
- setShowRename(item)
- }, [handlePinConversation, handleUnpinConversation])
- const handleCancelConfirm = useCallback(() => {
- setShowConfirm(null)
- }, [])
- const handleDelete = useCallback(() => {
- if (showConfirm)
- handleDeleteConversation(showConfirm.id, { onSuccess: handleCancelConfirm })
- }, [showConfirm, handleDeleteConversation, handleCancelConfirm])
- const handleCancelRename = useCallback(() => {
- setShowRename(null)
- }, [])
- const handleRename = useCallback((newName: string) => {
- if (showRename)
- handleRenameConversation(showRename.id, newName, { onSuccess: handleCancelRename })
- }, [showRename, handleRenameConversation, handleCancelRename])
- return (
- <div className={cn(
- 'flex w-full grow flex-col',
- isPanel && 'rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-bg shadow-lg',
- )}>
- <div className={cn(
- 'flex shrink-0 items-center gap-3 p-3 pr-2',
- )}>
- <div className='shrink-0'>
- <AppIcon
- size='large'
- iconType={appData?.site.icon_type}
- icon={appData?.site.icon}
- background={appData?.site.icon_background}
- imageUrl={appData?.site.icon_url}
- />
- </div>
- <div className={cn('system-md-semibold grow truncate text-text-secondary')}>{appData?.site.title}</div>
- {!isMobile && isSidebarCollapsed && (
- <ActionButton size='l' onClick={() => handleSidebarCollapse(false)}>
- <RiExpandRightLine className='h-[18px] w-[18px]' />
- </ActionButton>
- )}
- {!isMobile && !isSidebarCollapsed && (
- <ActionButton size='l' onClick={() => handleSidebarCollapse(true)}>
- <RiLayoutLeft2Line className='h-[18px] w-[18px]' />
- </ActionButton>
- )}
- </div>
- <div className='shrink-0 px-3 py-4'>
- <Button variant='secondary-accent' disabled={isResponding} className='w-full justify-center' onClick={handleNewConversation}>
- <RiEditBoxLine className='mr-1 h-4 w-4' />
- {t('share.chat.newChat')}
- </Button>
- </div>
- <div className='h-0 grow space-y-2 overflow-y-auto px-3 pt-4'>
- {/* pinned list */}
- {!!pinnedConversationList.length && (
- <div className='mb-4'>
- <List
- isPin
- title={t('share.chat.pinnedTitle') || ''}
- list={pinnedConversationList}
- onChangeConversation={handleChangeConversation}
- onOperate={handleOperate}
- currentConversationId={currentConversationId}
- />
- </div>
- )}
- {!!conversationList.length && (
- <List
- title={(pinnedConversationList.length && t('share.chat.unpinnedTitle')) || ''}
- list={conversationList}
- onChangeConversation={handleChangeConversation}
- onOperate={handleOperate}
- currentConversationId={currentConversationId}
- />
- )}
- </div>
- <div className='flex shrink-0 items-center justify-between p-3'>
- <MenuDropdown placement='top-start' data={appData?.site} />
- {/* powered by */}
- <div className='shrink-0'>
- {!appData?.custom_config?.remove_webapp_brand && (
- <div className={cn(
- 'flex shrink-0 items-center gap-1.5 px-2',
- )}>
- <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
- {appData?.custom_config?.replace_webapp_logo && (
- <img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
- )}
- {!appData?.custom_config?.replace_webapp_logo && (
- <LogoSite className='!h-5' />
- )}
- </div>
- )}
- </div>
- </div>
- {!!showConfirm && (
- <Confirm
- title={t('share.chat.deleteConversation.title')}
- content={t('share.chat.deleteConversation.content') || ''}
- isShow
- onCancel={handleCancelConfirm}
- onConfirm={handleDelete}
- />
- )}
- {showRename && (
- <RenameModal
- isShow
- onClose={handleCancelRename}
- saveLoading={conversationRenaming}
- name={showRename?.name || ''}
- onSave={handleRename}
- />
- )}
- </div>
- )
- }
- export default Sidebar
|