feat: conversation app support pin and delete conversation (#467)
parent
accc5faae3
commit
ec261aea54
@ -0,0 +1,115 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useRef } from 'react'
|
||||
import {
|
||||
ChatBubbleOvalLeftEllipsisIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
import { useInfiniteScroll } from 'ahooks'
|
||||
import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
|
||||
import cn from 'classnames'
|
||||
import s from './style.module.css'
|
||||
import type { ConversationItem } from '@/models/share'
|
||||
import { fetchConversations } from '@/service/share'
|
||||
import ItemOperation from '@/app/components/explore/item-operation'
|
||||
|
||||
export type IListProps = {
|
||||
className: string
|
||||
currentId: string
|
||||
onCurrentIdChange: (id: string) => void
|
||||
list: ConversationItem[]
|
||||
isInstalledApp: boolean
|
||||
installedAppId?: string
|
||||
onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
|
||||
isNoMore: boolean
|
||||
isPinned: boolean
|
||||
onPinChanged: (id: string) => void
|
||||
controlUpdate: number
|
||||
onDelete: (id: string) => void
|
||||
}
|
||||
|
||||
const List: FC<IListProps> = ({
|
||||
className,
|
||||
currentId,
|
||||
onCurrentIdChange,
|
||||
list,
|
||||
isInstalledApp,
|
||||
installedAppId,
|
||||
onMoreLoaded,
|
||||
isNoMore,
|
||||
isPinned,
|
||||
onPinChanged,
|
||||
controlUpdate,
|
||||
onDelete,
|
||||
}) => {
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useInfiniteScroll(
|
||||
async () => {
|
||||
if (!isNoMore) {
|
||||
const lastId = list[list.length - 1]?.id
|
||||
const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned)
|
||||
onMoreLoaded({ data: conversations, has_more })
|
||||
}
|
||||
return { list: [] }
|
||||
},
|
||||
{
|
||||
target: listRef,
|
||||
isNoMore: () => {
|
||||
return isNoMore
|
||||
},
|
||||
reloadDeps: [isNoMore, controlUpdate],
|
||||
},
|
||||
)
|
||||
return (
|
||||
<nav
|
||||
ref={listRef}
|
||||
className={cn(className, 'shrink-0 space-y-1 bg-white pb-[60px] overflow-y-auto')}
|
||||
>
|
||||
{list.map((item) => {
|
||||
const isCurrent = item.id === currentId
|
||||
const ItemIcon
|
||||
= isCurrent ? ChatBubbleOvalLeftEllipsisSolidIcon : ChatBubbleOvalLeftEllipsisIcon
|
||||
return (
|
||||
<div
|
||||
onClick={() => onCurrentIdChange(item.id)}
|
||||
key={item.id}
|
||||
className={cn(s.item,
|
||||
isCurrent
|
||||
? 'bg-primary-50 text-primary-600'
|
||||
: 'text-gray-700 hover:bg-gray-200 hover:text-gray-700',
|
||||
'group flex justify-between items-center rounded-md px-2 py-2 text-sm font-medium cursor-pointer',
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center w-0 grow'>
|
||||
<ItemIcon
|
||||
className={cn(
|
||||
isCurrent
|
||||
? 'text-primary-600'
|
||||
: 'text-gray-400 group-hover:text-gray-500',
|
||||
'mr-3 h-5 w-5 flex-shrink-0',
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(List)
|
||||
@ -0,0 +1,7 @@
|
||||
.opBtn {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.item:hover .opBtn {
|
||||
visibility: visible;
|
||||
}
|
||||
@ -1,45 +1,51 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "Welcome to use",
|
||||
appUnavailable: "App is unavailable",
|
||||
appUnkonwError: "App is unavailable"
|
||||
welcome: 'Welcome to use',
|
||||
appUnavailable: 'App is unavailable',
|
||||
appUnkonwError: 'App is unavailable',
|
||||
},
|
||||
chat: {
|
||||
newChat: "New chat",
|
||||
newChatDefaultName: "New conversation",
|
||||
powerBy: "Powered by",
|
||||
prompt: "Prompt",
|
||||
privatePromptConfigTitle: "Conversation settings",
|
||||
publicPromptConfigTitle: "Initial Prompt",
|
||||
configStatusDes: "Before start, you can modify conversation settings",
|
||||
newChat: 'New chat',
|
||||
pinnedTitle: 'Pinned',
|
||||
unpinnedTitle: 'Chats',
|
||||
newChatDefaultName: 'New conversation',
|
||||
powerBy: 'Powered by',
|
||||
prompt: 'Prompt',
|
||||
privatePromptConfigTitle: 'Conversation settings',
|
||||
publicPromptConfigTitle: 'Initial Prompt',
|
||||
configStatusDes: 'Before start, you can modify conversation settings',
|
||||
configDisabled:
|
||||
"Previous session settings have been used for this session.",
|
||||
startChat: "Start Chat",
|
||||
'Previous session settings have been used for this session.',
|
||||
startChat: 'Start Chat',
|
||||
privacyPolicyLeft:
|
||||
"Please read the ",
|
||||
'Please read the ',
|
||||
privacyPolicyMiddle:
|
||||
"privacy policy",
|
||||
'privacy policy',
|
||||
privacyPolicyRight:
|
||||
" provided by the app developer.",
|
||||
' provided by the app developer.',
|
||||
deleteConversation: {
|
||||
title: 'Delete conversation',
|
||||
content: 'Are you sure you want to delete this conversation?',
|
||||
},
|
||||
},
|
||||
generation: {
|
||||
tabs: {
|
||||
create: "Create",
|
||||
saved: "Saved",
|
||||
create: 'Create',
|
||||
saved: 'Saved',
|
||||
},
|
||||
savedNoData: {
|
||||
title: "You haven't saved a result yet!",
|
||||
title: 'You haven\'t saved a result yet!',
|
||||
description: 'Start generating content, and find your saved results here.',
|
||||
startCreateContent: 'Start create content'
|
||||
startCreateContent: 'Start create content',
|
||||
},
|
||||
title: "AI Completion",
|
||||
queryTitle: "Query content",
|
||||
queryPlaceholder: "Write your query content...",
|
||||
run: "RUN",
|
||||
copy: "Copy",
|
||||
resultTitle: "AI Completion",
|
||||
noData: "AI will give you what you want here.",
|
||||
title: 'AI Completion',
|
||||
queryTitle: 'Query content',
|
||||
queryPlaceholder: 'Write your query content...',
|
||||
run: 'RUN',
|
||||
copy: 'Copy',
|
||||
resultTitle: 'AI Completion',
|
||||
noData: 'AI will give you what you want here.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default translation;
|
||||
export default translation
|
||||
|
||||
@ -1,41 +1,47 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "欢迎使用",
|
||||
appUnavailable: "应用不可用",
|
||||
appUnkonwError: "应用不可用",
|
||||
welcome: '欢迎使用',
|
||||
appUnavailable: '应用不可用',
|
||||
appUnkonwError: '应用不可用',
|
||||
},
|
||||
chat: {
|
||||
newChat: "新对话",
|
||||
newChatDefaultName: "新的对话",
|
||||
powerBy: "Powered by",
|
||||
prompt: "提示词",
|
||||
privatePromptConfigTitle: "对话设置",
|
||||
publicPromptConfigTitle: "对话前提示词",
|
||||
configStatusDes: "开始前,您可以修改对话设置",
|
||||
configDisabled: "此次会话已使用上次会话表单",
|
||||
startChat: "开始对话",
|
||||
privacyPolicyLeft: "请阅读由该应用开发者提供的",
|
||||
privacyPolicyMiddle: "隐私政策",
|
||||
privacyPolicyRight: "。",
|
||||
newChat: '新对话',
|
||||
pinnedTitle: '已置顶',
|
||||
unpinnedTitle: '对话列表',
|
||||
newChatDefaultName: '新的对话',
|
||||
powerBy: 'Powered by',
|
||||
prompt: '提示词',
|
||||
privatePromptConfigTitle: '对话设置',
|
||||
publicPromptConfigTitle: '对话前提示词',
|
||||
configStatusDes: '开始前,您可以修改对话设置',
|
||||
configDisabled: '此次会话已使用上次会话表单',
|
||||
startChat: '开始对话',
|
||||
privacyPolicyLeft: '请阅读由该应用开发者提供的',
|
||||
privacyPolicyMiddle: '隐私政策',
|
||||
privacyPolicyRight: '。',
|
||||
deleteConversation: {
|
||||
title: '删除对话',
|
||||
content: '您确定要删除此对话吗?',
|
||||
},
|
||||
},
|
||||
generation: {
|
||||
tabs: {
|
||||
create: "创建",
|
||||
saved: "已保存",
|
||||
create: '创建',
|
||||
saved: '已保存',
|
||||
},
|
||||
savedNoData: {
|
||||
title: "您还没有保存结果!",
|
||||
title: '您还没有保存结果!',
|
||||
description: '开始生成内容,您可以在这里找到保存的结果。',
|
||||
startCreateContent: '开始生成内容'
|
||||
startCreateContent: '开始生成内容',
|
||||
},
|
||||
title: "AI 智能书写",
|
||||
queryTitle: "查询内容",
|
||||
queryPlaceholder: "请输入文本内容",
|
||||
run: "运行",
|
||||
copy: "拷贝",
|
||||
resultTitle: "AI 书写",
|
||||
noData: "AI 会在这里给你惊喜。",
|
||||
title: 'AI 智能书写',
|
||||
queryTitle: '查询内容',
|
||||
queryPlaceholder: '请输入文本内容',
|
||||
run: '运行',
|
||||
copy: '拷贝',
|
||||
resultTitle: 'AI 书写',
|
||||
noData: 'AI 会在这里给你惊喜。',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default translation;
|
||||
export default translation
|
||||
|
||||
Loading…
Reference in New Issue