Merge branch 'develop' into feature/ui_upgrade_zy
This commit is contained in:
@@ -37,7 +37,6 @@ const ChatContent: FC<ChatContentProps> = ({
|
||||
const prevDataLengthRef = useRef(data.length);
|
||||
const isScrolledToBottomRef = useRef(true);
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null)
|
||||
const [playingIndex, setPlayingIndex] = useState<number | null>(null)
|
||||
const [expandedReasoning, setExpandedReasoning] = useState<Set<number>>(new Set())
|
||||
const [manualToggledReasoning, setManualToggledReasoning] = useState<Set<number>>(new Set())
|
||||
|
||||
@@ -54,10 +53,11 @@ const ChatContent: FC<ChatContentProps> = ({
|
||||
if (manualToggledReasoning.has(index)) return expandedReasoning.has(index)
|
||||
return !data[index]?.content
|
||||
}
|
||||
const [playingIndex, setPlayingIndex] = useState<string | null>(null)
|
||||
|
||||
const handlePlay = (index: number, audio_url: string, audio_status?: string) => {
|
||||
if (audio_status !== 'completed' && !audio_status) return
|
||||
if (playingIndex === index) {
|
||||
const handlePlay = (audio_url: string, audio_status?: string) => {
|
||||
if (audio_status !== 'completed' && typeof audio_status === 'string') return
|
||||
if (playingIndex === audio_url) {
|
||||
audioRef.current?.pause()
|
||||
setPlayingIndex(null)
|
||||
return
|
||||
@@ -68,7 +68,7 @@ const ChatContent: FC<ChatContentProps> = ({
|
||||
const audio = new Audio(audio_url)
|
||||
audioRef.current = audio
|
||||
audio.play()
|
||||
setPlayingIndex(index)
|
||||
setPlayingIndex(audio_url)
|
||||
audio.onended = () => setPlayingIndex(null)
|
||||
}
|
||||
|
||||
@@ -95,12 +95,16 @@ const ChatContent: FC<ChatContentProps> = ({
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
// Auto-scroll to bottom when data changes to show latest messages
|
||||
// When data array length remains unchanged, if data is updated and user manually scrolled up, don't auto-scroll to bottom
|
||||
// When data array length changes, auto-scroll to bottom
|
||||
// If already scrolled to bottom, will auto-scroll to bottom
|
||||
useEffect(() => {
|
||||
if (playingIndex && !data.some(item => item.meta_data?.audio_url === playingIndex)) {
|
||||
audioRef.current?.pause()
|
||||
setPlayingIndex(null)
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (scrollContainerRef.current) {
|
||||
// Auto-scroll if data length changed OR user is currently at bottom
|
||||
@@ -236,16 +240,16 @@ const ChatContent: FC<ChatContentProps> = ({
|
||||
{item.meta_data?.audio_url && <>
|
||||
<Divider className="rb:my-3!" />
|
||||
<Space size={12} className="rb:pb-2 rb:pl-1">
|
||||
{playingIndex !== index && item.meta_data?.audio_status === 'pending'
|
||||
{playingIndex !== item.meta_data?.audio_url && item.meta_data?.audio_status === 'pending'
|
||||
? <Spin />
|
||||
: playingIndex !== index
|
||||
: playingIndex !== item.meta_data?.audio_url
|
||||
? <SoundOutlined className={clsx("rb:cursor-pointer rb:size-5.5", {
|
||||
'rb:text-[#FF5D34]': item.meta_data?.audio_status === 'error',
|
||||
'rb:hover:text-[#155EEF]!': !item.meta_data?.audio_status || !['pending', 'error'].includes(item.meta_data?.audio_status)
|
||||
})} onClick={() => handlePlay(index, item.meta_data?.audio_url!, item.meta_data?.audio_status)} />
|
||||
})} onClick={() => handlePlay(item.meta_data?.audio_url!, item.meta_data?.audio_status)} />
|
||||
: <div
|
||||
className="rb:size-5.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/audio_ing.gif')]"
|
||||
onClick={() => handlePlay(index, item.meta_data?.audio_url!, item.meta_data?.audio_status)}
|
||||
onClick={() => handlePlay(item.meta_data?.audio_url!, item.meta_data?.audio_status)}
|
||||
/>
|
||||
}
|
||||
</Space>
|
||||
|
||||
@@ -24,16 +24,17 @@ interface BodyWrapperProps {
|
||||
/** Whether to show loading state */
|
||||
loading?: boolean
|
||||
/** Whether the content is empty */
|
||||
empty: boolean
|
||||
empty: boolean;
|
||||
className?: string;
|
||||
}
|
||||
const BodyWrapper: FC<BodyWrapperProps> = ({ children, loading = false, empty }) => {
|
||||
const BodyWrapper: FC<BodyWrapperProps> = ({ children, loading = false, empty, className = 'rb:max-h-[calc(100%-48px)]!' }) => {
|
||||
// Show loading spinner while data is being fetched
|
||||
if (loading) {
|
||||
return <PageLoading />
|
||||
return <PageLoading className={className} />
|
||||
}
|
||||
// Show empty state when no data is available
|
||||
if (!loading && empty) {
|
||||
return <PageEmpty />
|
||||
return <PageEmpty className={className} />
|
||||
}
|
||||
// Render actual content when data is loaded and available
|
||||
return children
|
||||
|
||||
@@ -128,6 +128,7 @@ const Menu: FC<{
|
||||
|
||||
/** Filter menus based on user role and source */
|
||||
useEffect(() => {
|
||||
if (!user) return
|
||||
let menuList: MenuItem[] = []
|
||||
|
||||
if (user.role === 'member' && source === 'space') {
|
||||
@@ -136,7 +137,7 @@ const Menu: FC<{
|
||||
menuList = allMenus[source] || []
|
||||
}
|
||||
|
||||
const noAuthList = ['user', 'pricing'].filter(vo => !user.permissions?.includes(vo) && !user.permissions?.includes('all'))
|
||||
const noAuthList = ['user', 'pricing'].filter(vo => (Array.isArray(user.permissions) && !user.permissions?.includes(vo) && !user.permissions?.includes('all')) || !Array.isArray(user.permissions))
|
||||
|
||||
if (noAuthList && !noAuthList?.includes('all')) {
|
||||
const filterMenus = (list: MenuItem[]): MenuItem[] =>{
|
||||
|
||||
Reference in New Issue
Block a user