feat: support rename conversation (#1056)

This commit is contained in:
Joel
2023-08-30 17:32:32 +08:00
committed by GitHub
parent c67f345d0e
commit a834ba8759
118 changed files with 459 additions and 16 deletions

View File

@@ -65,6 +65,7 @@ function useConversation() {
setCurrInputs,
currConversationInfo,
setNewConversationInfo,
existConversationInfo,
setExistConversationInfo,
}
}

View File

@@ -101,6 +101,7 @@ const Main: FC<IMainProps> = ({
resetNewConversationInputs,
setCurrInputs,
setNewConversationInfo,
existConversationInfo,
setExistConversationInfo,
} = useConversation()
const [hasMore, setHasMore] = useState<boolean>(true)
@@ -186,6 +187,23 @@ const Main: FC<IMainProps> = ({
const conversationName = currConversationInfo?.name || t('share.chat.newChatDefaultName') as string
const conversationIntroduction = currConversationInfo?.introduction || ''
const [controlChatUpdateAllConversation, setControlChatUpdateAllConversation] = useState(0)
useEffect(() => {
(async () => {
if (controlChatUpdateAllConversation && !isNewConversation) {
const { data: allConversations } = await fetchAllConversations() as { data: ConversationItem[]; has_more: boolean }
const item = allConversations.find(item => item.id === currConversationId)
setAllConversationList(allConversations)
if (item) {
setExistConversationInfo({
...existConversationInfo,
name: item?.name || '',
} as any)
}
}
})()
}, [controlChatUpdateAllConversation])
const handleConversationSwitch = () => {
if (!inited)
@@ -546,8 +564,16 @@ const Main: FC<IMainProps> = ({
return (
<Sidebar
list={conversationList}
onListChanged={(list) => {
setConversationList(list)
setControlChatUpdateAllConversation(Date.now())
}}
isClearConversationList={isClearConversationList}
pinnedList={pinnedConversationList}
onPinnedListChanged={(list) => {
setPinnedConversationList(list)
setControlChatUpdateAllConversation(Date.now())
}}
isClearPinnedConversationList={isClearPinnedConversationList}
onMoreLoaded={onMoreLoaded}
onPinnedMoreLoaded={onPinnedMoreLoaded}

View File

@@ -18,8 +18,10 @@ export type ISidebarProps = {
currentId: string
onCurrentIdChange: (id: string) => void
list: ConversationItem[]
onListChanged: (newList: ConversationItem[]) => void
isClearConversationList: boolean
pinnedList: ConversationItem[]
onPinnedListChanged: (newList: ConversationItem[]) => void
isClearPinnedConversationList: boolean
isInstalledApp: boolean
installedAppId?: string
@@ -40,8 +42,10 @@ const Sidebar: FC<ISidebarProps> = ({
currentId,
onCurrentIdChange,
list,
onListChanged,
isClearConversationList,
pinnedList,
onPinnedListChanged,
isClearPinnedConversationList,
isInstalledApp,
installedAppId,
@@ -115,6 +119,7 @@ const Sidebar: FC<ISidebarProps> = ({
currentId={currentId}
onCurrentIdChange={onCurrentIdChange}
list={pinnedList}
onListChanged={onPinnedListChanged}
isClearConversationList={isClearPinnedConversationList}
isInstalledApp={isInstalledApp}
installedAppId={installedAppId}
@@ -138,6 +143,7 @@ const Sidebar: FC<ISidebarProps> = ({
currentId={currentId}
onCurrentIdChange={onCurrentIdChange}
list={list}
onListChanged={onListChanged}
isClearConversationList={isClearConversationList}
isInstalledApp={isInstalledApp}
installedAppId={installedAppId}

View File

@@ -1,23 +1,27 @@
'use client'
import type { FC } from 'react'
import React, { useRef } from 'react'
import React, { useRef, useState } from 'react'
import {
ChatBubbleOvalLeftEllipsisIcon,
} from '@heroicons/react/24/outline'
import { useInfiniteScroll } from 'ahooks'
import { useBoolean, useInfiniteScroll } from 'ahooks'
import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import RenameModal from '../rename-modal'
import s from './style.module.css'
import type { ConversationItem } from '@/models/share'
import { fetchConversations } from '@/service/share'
import { fetchConversations as fetchUniversalConversations } from '@/service/universal-chat'
import { fetchConversations, renameConversation } from '@/service/share'
import { fetchConversations as fetchUniversalConversations, renameConversation as renameUniversalConversation } from '@/service/universal-chat'
import ItemOperation from '@/app/components/explore/item-operation'
import Toast from '@/app/components/base/toast'
export type IListProps = {
className: string
currentId: string
onCurrentIdChange: (id: string) => void
list: ConversationItem[]
onListChanged?: (newList: ConversationItem[]) => void
isClearConversationList: boolean
isInstalledApp: boolean
isUniversalChat?: boolean
@@ -35,6 +39,7 @@ const List: FC<IListProps> = ({
currentId,
onCurrentIdChange,
list,
onListChanged,
isClearConversationList,
isInstalledApp,
isUniversalChat,
@@ -46,6 +51,8 @@ const List: FC<IListProps> = ({
controlUpdate,
onDelete,
}) => {
const { t } = useTranslation()
const listRef = useRef<HTMLDivElement>(null)
useInfiniteScroll(
@@ -72,6 +79,50 @@ const List: FC<IListProps> = ({
reloadDeps: [isNoMore, controlUpdate],
},
)
const [isShowRename, { setTrue: setShowRename, setFalse: setHideRename }] = useBoolean(false)
const [isSaving, { setTrue: setIsSaving, setFalse: setNotSaving }] = useBoolean(false)
const [currentConversation, setCurrentConversation] = useState<ConversationItem | null>(null)
const showRename = (item: ConversationItem) => {
setCurrentConversation(item)
setShowRename()
}
const handleRename = async (newName: string) => {
if (!newName.trim() || !currentConversation) {
Toast.notify({
type: 'error',
message: t('common.chat.conversationNameCanNotEmpty'),
})
return
}
setIsSaving()
const currId = currentConversation.id
try {
if (isUniversalChat)
await renameUniversalConversation(currId, newName)
else
await renameConversation(isInstalledApp, installedAppId, currId, newName)
Toast.notify({
type: 'success',
message: t('common.actionMsg.modifiedSuccessfully'),
})
onListChanged?.(list.map((item) => {
if (item.id === currId) {
return {
...item,
name: newName,
}
}
return item
}))
setHideRename()
}
finally {
setNotSaving()
}
}
return (
<nav
ref={listRef}
@@ -110,6 +161,8 @@ const List: FC<IListProps> = ({
<ItemOperation
isPinned={isPinned}
togglePin={() => onPinChanged(item.id)}
isShowRenameConversation
onRenameConversation={() => showRename(item)}
isShowDelete
onDelete={() => onDelete(item.id)}
/>
@@ -118,6 +171,15 @@ const List: FC<IListProps> = ({
</div>
)
})}
{isShowRename && (
<RenameModal
isShow={isShowRename}
onClose={setHideRename}
saveLoading={isSaving}
name={currentConversation?.name || ''}
onSave={handleRename}
/>
)}
</nav>
)
}

View File

@@ -0,0 +1,47 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
export type IRenameModalProps = {
isShow: boolean
saveLoading: boolean
name: string
onClose: () => void
onSave: (name: string) => void
}
const RenameModal: FC<IRenameModalProps> = ({
isShow,
saveLoading,
name,
onClose,
onSave,
}) => {
const { t } = useTranslation()
const [tempName, setTempName] = useState(name)
return (
<Modal
title={t('common.chat.renameConversation')}
isShow={isShow}
onClose={onClose}
wrapperClassName='!z-50'
>
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('common.chat.conversationName')}</div>
<input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
value={tempName}
onChange={e => setTempName(e.target.value)}
placeholder={t('common.chat.conversationNamePlaceholder') || ''}
/>
<div className='mt-10 flex justify-end'>
<Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
<Button type='primary' className='flex-shrink-0' onClick={() => onSave(tempName)} loading={saveLoading}>{t('common.operation.save')}</Button>
</div>
</Modal>
)
}
export default React.memo(RenameModal)