Files
banban/banban-mini/src/pages/chat/index.tsx
T

211 lines
8.6 KiB
TypeScript

import { getParticipantArtwork } from '@/utils/artwork'
import { formatConversationPreview } from '@/utils/chat-preview'
import PageTitle from '@/components/page-title'
import { getPageStyle } from '@/utils/page-style'
import { setActiveTabRoute } from '@/utils/tab-bar'
import { View, Text, ScrollView, Image } from '@tarojs/components'
import { useState } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { getToken } from '@/services/auth'
import { getConversations, ChatConversation } from '../../services/chat'
import { useSystemBanner } from '@/components/system-banner/use-system-banner'
import { usePageShare } from '@/utils/page-share'
import './index.scss'
export default function Chat() {
usePageShare()
const systemBanner = useSystemBanner()
const [conversations, setConversations] = useState<ChatConversation[]>([])
const [loading, setLoading] = useState(true)
const [isGuest, setIsGuest] = useState(false)
useDidShow(() => {
setActiveTabRoute('pages/chat/index')
void loadConversations()
})
const loadConversations = async () => {
if (!getToken()) {
setConversations([])
setIsGuest(true)
setLoading(false)
return
}
setIsGuest(false)
setLoading(true)
try {
const data = await getConversations()
setConversations(data)
} catch (error: any) {
console.error('[chat] load failed:', error)
Taro.showToast({
title: error?.message || '加载失败,请稍后重试',
icon: 'none',
})
} finally {
setLoading(false)
}
}
const handleConversationClick = (conversation: ChatConversation) => {
Taro.navigateTo({
url:
`/pages/chat/detail/index?id=${conversation.id}` +
`&source=${conversation.source}` +
`&name=${encodeURIComponent(conversation.name)}` +
`&peerKind=${conversation.peerKind}` +
`&roleKey=${encodeURIComponent(conversation.roleKey || '')}` +
`&conversationTypeName=${encodeURIComponent(conversation.conversationTypeName || '')}` +
`&peerId=${encodeURIComponent(conversation.peerId || '')}` +
`&childId=${conversation.childId || ''}` +
`&childName=${encodeURIComponent(conversation.childName || '')}` +
`&parentUserId=${conversation.parentUserId || ''}` +
`&deviceId=${encodeURIComponent(conversation.deviceId || '')}` +
`&channelLabel=${encodeURIComponent(conversation.channelLabel || '')}` +
`&canSend=${conversation.canSend ? '1' : '0'}`,
})
}
const handleGuestLogin = () => {
Taro.setStorageSync('postLoginRedirect', '/pages/chat/index')
Taro.navigateTo({ url: '/pages/login/index' })
}
const handleGuestPreviewTap = (title: string, content: string) => {
Taro.showModal({
title,
content,
showCancel: false,
confirmText: '我知道了',
})
}
return (
<View className='chat-page' style={getPageStyle(true)}>
<View className='page-header'>
<PageTitle title='玩伴' />
</View>
{isGuest ? (
<View className='guest-chat-shell'>
<View className='guest-card'>
<Text className='guest-card-icon'>💬</Text>
<Text className='guest-card-title'>亲子互动会话</Text>
<Text className='guest-card-desc'>这里会汇总宝贝、家长和 AI 玩伴之间的消息。绑定设备后,真实会话会按宝贝和设备自动归档。</Text>
</View>
<View className='guest-demo-card'>
<View className='guest-demo-header'>
<Text className='guest-demo-title'>会话示例</Text>
<View
className='guest-demo-tag'
onClick={() => handleGuestPreviewTap(
'会话预览',
'这里展示玩伴页的会话能力。绑定设备后,会显示宝贝、家长和 AI 玩伴的真实消息记录。'
)}
>
<Text className='guest-demo-tag-text'>预览</Text>
</View>
</View>
<View
className='guest-demo-item'
onClick={() => handleGuestPreviewTap(
'AI 玩伴',
'绑定设备后,可以在这里查看宝贝与 AI 玩伴的真实会话记录,包括聊天内容、时间和对应宝贝。'
)}
>
<View className='guest-demo-avatar'>
<Image className='conversation-avatar-art' src={getParticipantArtwork('ai')} mode='aspectFit' />
</View>
<View className='guest-demo-content'>
<Text className='guest-demo-name'>AI 玩伴</Text>
<Text className='guest-demo-message'>陪宝贝聊天、讲故事,也能留下可回看记录。</Text>
</View>
<View
className='guest-demo-action'
onClick={() => handleGuestPreviewTap(
'AI 玩伴',
'绑定设备后,可以在这里查看宝贝与 AI 玩伴的真实会话记录,包括聊天内容、时间和对应宝贝。'
)}
>
<Text className='guest-demo-action-text'>查看</Text>
</View>
</View>
<View
className='guest-demo-item'
onClick={() => handleGuestPreviewTap(
'家长留言',
'绑定设备后,家长可以查看消息记录,也可以给宝贝发送语音留言,设备端会收到对应提醒。'
)}
>
<View className='guest-demo-avatar parent'>
<Image className='conversation-avatar-art' src={getParticipantArtwork('parent')} mode='aspectFit' />
</View>
<View className='guest-demo-content'>
<Text className='guest-demo-name'>家长留言</Text>
<Text className='guest-demo-message'>家长可查看设备消息,并给宝贝发送语音留言。</Text>
</View>
<View
className='guest-demo-action'
onClick={() => handleGuestPreviewTap(
'家长留言',
'绑定设备后,家长可以查看消息记录,也可以给宝贝发送语音留言,设备端会收到对应提醒。'
)}
>
<Text className='guest-demo-action-text'>查看</Text>
</View>
</View>
</View>
<View className='guest-guide-card'>
<Text className='guest-guide-title'>开始使用</Text>
<Text className='guest-guide-desc'>浏览功能不需要登录;当你需要绑定设备、查看真实会话记录时,再进入登录和绑定流程。</Text>
<View className='guest-card-btn secondary' onClick={handleGuestLogin}>
<Text className='guest-card-btn-text'>去登录 / 绑定设备</Text>
</View>
</View>
</View>
) : loading ? (
<View className='loading'>
<Text>加载中...</Text>
</View>
) : conversations.length === 0 ? (
<View className='empty-card'>
<Text className='empty-title'>当前宝贝暂无会话</Text>
</View>
) : (
<ScrollView className='conversation-list' scrollY enhanced showScrollbar={false}>
{conversations.map((conversation) => (
<View
key={conversation.key}
className='conversation-item'
onClick={() => handleConversationClick(conversation)}
>
<View className='conversation-avatar'>
<Image className='conversation-avatar-art' src={getParticipantArtwork(conversation.peerKind, conversation.roleKey)} mode='aspectFit' />
</View>
<View className='conversation-content'>
<View className='conversation-header'>
<View className='conversation-name-wrap'>
<Text className='conversation-name'>{conversation.name}</Text>
<Text className={`conversation-tag ${conversation.peerKind}`}>{conversation.typeLabel}</Text>
</View>
<Text className='conversation-time'>{conversation.time}</Text>
</View>
<View className='conversation-footer'>
<View className='last-message'>
{formatConversationPreview(conversation.lastMessage)}
</View>
</View>
{conversation.childName && <Text className='conversation-meta'>{conversation.childName}</Text>}
</View>
</View>
))}
</ScrollView>
)}
{systemBanner}
</View>
)
}