| @@ -56,16 +56,14 @@ export default function AppNavItem({ | |||
| <div className='overflow-hidden text-ellipsis whitespace-nowrap'>{name}</div> | |||
| </div> | |||
| { | |||
| !isSelected && ( | |||
| <div className={cn(s.opBtn, 'shrink-0')} onClick={e => e.stopPropagation()}> | |||
| <ItemOperation | |||
| isPinned={isPinned} | |||
| togglePin={togglePin} | |||
| isShowDelete={!uninstallable} | |||
| onDelete={() => onDelete(id)} | |||
| /> | |||
| </div> | |||
| ) | |||
| <div className={cn(s.opBtn, 'shrink-0')} onClick={e => e.stopPropagation()}> | |||
| <ItemOperation | |||
| isPinned={isPinned} | |||
| togglePin={togglePin} | |||
| isShowDelete={!uninstallable && !isSelected} | |||
| onDelete={() => onDelete(id)} | |||
| /> | |||
| </div> | |||
| } | |||
| </div> | |||
| ) | |||
| @@ -67,6 +67,8 @@ const Main: FC<IMainProps> = ({ | |||
| * conversation info | |||
| */ | |||
| const [allConversationList, setAllConversationList] = useState<ConversationItem[]>([]) | |||
| const [isClearConversationList, { setTrue: clearConversationListTrue, setFalse: clearConversationListFalse }] = useBoolean(false) | |||
| const [isClearPinnedConversationList, { setTrue: clearPinnedConversationListTrue, setFalse: clearPinnedConversationListFalse }] = useBoolean(false) | |||
| const { | |||
| conversationList, | |||
| setConversationList, | |||
| @@ -89,18 +91,32 @@ const Main: FC<IMainProps> = ({ | |||
| const [hasPinnedMore, setHasPinnedMore] = useState<boolean>(true) | |||
| const onMoreLoaded = ({ data: conversations, has_more }: any) => { | |||
| setHasMore(has_more) | |||
| setConversationList([...conversationList, ...conversations]) | |||
| if (isClearConversationList) { | |||
| setConversationList(conversations) | |||
| clearConversationListFalse() | |||
| } | |||
| else { | |||
| setConversationList([...conversationList, ...conversations]) | |||
| } | |||
| } | |||
| const onPinnedMoreLoaded = ({ data: conversations, has_more }: any) => { | |||
| setHasPinnedMore(has_more) | |||
| setPinnedConversationList([...pinnedConversationList, ...conversations]) | |||
| if (isClearPinnedConversationList) { | |||
| setPinnedConversationList(conversations) | |||
| clearPinnedConversationListFalse() | |||
| } | |||
| else { | |||
| setPinnedConversationList([...pinnedConversationList, ...conversations]) | |||
| } | |||
| } | |||
| const [controlUpdateConversationList, setControlUpdateConversationList] = useState(0) | |||
| const noticeUpdateList = () => { | |||
| setConversationList([]) | |||
| setHasMore(true) | |||
| setPinnedConversationList([]) | |||
| clearConversationListTrue() | |||
| setHasPinnedMore(true) | |||
| clearPinnedConversationListTrue() | |||
| setControlUpdateConversationList(Date.now()) | |||
| } | |||
| const handlePin = async (id: string) => { | |||
| @@ -126,6 +142,9 @@ const Main: FC<IMainProps> = ({ | |||
| await delConversation(isInstalledApp, installedAppInfo?.id, toDeleteConversationId) | |||
| notify({ type: 'success', message: t('common.api.success') }) | |||
| hideConfirm() | |||
| if (currConversationId === toDeleteConversationId) | |||
| handleConversationIdChange('-1') | |||
| noticeUpdateList() | |||
| } | |||
| @@ -496,7 +515,9 @@ const Main: FC<IMainProps> = ({ | |||
| return ( | |||
| <Sidebar | |||
| list={conversationList} | |||
| isClearConversationList={isClearConversationList} | |||
| pinnedList={pinnedConversationList} | |||
| isClearPinnedConversationList={isClearPinnedConversationList} | |||
| onMoreLoaded={onMoreLoaded} | |||
| onPinnedMoreLoaded={onPinnedMoreLoaded} | |||
| isNoMore={!hasMore} | |||
| @@ -17,7 +17,9 @@ export type ISidebarProps = { | |||
| currentId: string | |||
| onCurrentIdChange: (id: string) => void | |||
| list: ConversationItem[] | |||
| isClearConversationList: boolean | |||
| pinnedList: ConversationItem[] | |||
| isClearPinnedConversationList: boolean | |||
| isInstalledApp: boolean | |||
| installedAppId?: string | |||
| siteInfo: SiteInfo | |||
| @@ -36,7 +38,9 @@ const Sidebar: FC<ISidebarProps> = ({ | |||
| currentId, | |||
| onCurrentIdChange, | |||
| list, | |||
| isClearConversationList, | |||
| pinnedList, | |||
| isClearPinnedConversationList, | |||
| isInstalledApp, | |||
| installedAppId, | |||
| siteInfo, | |||
| @@ -92,16 +96,17 @@ const Sidebar: FC<ISidebarProps> = ({ | |||
| <PencilSquareIcon className="mr-2 h-4 w-4" /> {t('share.chat.newChat')} | |||
| </Button> | |||
| </div> | |||
| <div className='flex-grow h-0 overflow-y-auto overflow-x-hidden'> | |||
| <div className={'flex-grow flex flex-col h-0 overflow-y-auto overflow-x-hidden'}> | |||
| {/* pinned list */} | |||
| {hasPinned && ( | |||
| <div className='mt-4 px-4'> | |||
| <div className={cn('mt-4 px-4', list.length === 0 && 'flex flex-col flex-grow')}> | |||
| <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.pinnedTitle')}</div> | |||
| <List | |||
| className={maxListHeight} | |||
| className={cn(list.length > 0 ? maxListHeight : 'flex-grow')} | |||
| currentId={currentId} | |||
| onCurrentIdChange={onCurrentIdChange} | |||
| list={pinnedList} | |||
| isClearConversationList={isClearPinnedConversationList} | |||
| isInstalledApp={isInstalledApp} | |||
| installedAppId={installedAppId} | |||
| onMoreLoaded={onPinnedMoreLoaded} | |||
| @@ -114,8 +119,8 @@ const Sidebar: FC<ISidebarProps> = ({ | |||
| </div> | |||
| )} | |||
| {/* unpinned list */} | |||
| <div className='mt-4 px-4'> | |||
| {hasPinned && ( | |||
| <div className={cn('mt-4 px-4', !hasPinned && 'flex flex-col flex-grow')}> | |||
| {(hasPinned && list.length > 0) && ( | |||
| <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.unpinnedTitle')}</div> | |||
| )} | |||
| <List | |||
| @@ -123,6 +128,7 @@ const Sidebar: FC<ISidebarProps> = ({ | |||
| currentId={currentId} | |||
| onCurrentIdChange={onCurrentIdChange} | |||
| list={list} | |||
| isClearConversationList={isClearConversationList} | |||
| isInstalledApp={isInstalledApp} | |||
| installedAppId={installedAppId} | |||
| onMoreLoaded={onMoreLoaded} | |||
| @@ -133,6 +139,7 @@ const Sidebar: FC<ISidebarProps> = ({ | |||
| onDelete={onDelete} | |||
| /> | |||
| </div> | |||
| </div> | |||
| <div className="flex flex-shrink-0 pr-4 pb-4 pl-4"> | |||
| <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div> | |||
| @@ -17,6 +17,7 @@ export type IListProps = { | |||
| currentId: string | |||
| onCurrentIdChange: (id: string) => void | |||
| list: ConversationItem[] | |||
| isClearConversationList: boolean | |||
| isInstalledApp: boolean | |||
| installedAppId?: string | |||
| onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void | |||
| @@ -32,6 +33,7 @@ const List: FC<IListProps> = ({ | |||
| currentId, | |||
| onCurrentIdChange, | |||
| list, | |||
| isClearConversationList, | |||
| isInstalledApp, | |||
| installedAppId, | |||
| onMoreLoaded, | |||
| @@ -46,7 +48,7 @@ const List: FC<IListProps> = ({ | |||
| useInfiniteScroll( | |||
| async () => { | |||
| if (!isNoMore) { | |||
| const lastId = list[list.length - 1]?.id | |||
| const lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined | |||
| const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned) | |||
| onMoreLoaded({ data: conversations, has_more }) | |||
| } | |||
| @@ -63,7 +65,7 @@ const List: FC<IListProps> = ({ | |||
| return ( | |||
| <nav | |||
| ref={listRef} | |||
| className={cn(className, 'shrink-0 space-y-1 bg-white pb-[60px] overflow-y-auto')} | |||
| className={cn(className, 'shrink-0 space-y-1 bg-white pb-[85px] overflow-y-auto')} | |||
| > | |||
| {list.map((item) => { | |||
| const isCurrent = item.id === currentId | |||
| @@ -93,18 +95,16 @@ const List: FC<IListProps> = ({ | |||
| <span>{item.name}</span> | |||
| </div> | |||
| { | |||
| !isCurrent && ( | |||
| <div className={cn(s.opBtn, 'shrink-0')} onClick={e => e.stopPropagation()}> | |||
| <ItemOperation | |||
| isPinned={isPinned} | |||
| togglePin={() => onPinChanged(item.id)} | |||
| isShowDelete | |||
| onDelete={() => onDelete(item.id)} | |||
| /> | |||
| </div> | |||
| ) | |||
| } | |||
| {item.id !== '-1' && ( | |||
| <div className={cn(s.opBtn, 'shrink-0')} onClick={e => e.stopPropagation()}> | |||
| <ItemOperation | |||
| isPinned={isPinned} | |||
| togglePin={() => onPinChanged(item.id)} | |||
| isShowDelete | |||
| onDelete={() => onDelete(item.id)} | |||
| /> | |||
| </div> | |||
| )} | |||
| </div> | |||
| ) | |||
| })} | |||