Show loading icon when chat is in streaming (#10319)

* support chat stream loading rendering

* support chat stream loading rendering

* update loading icon to dots

* fix format

---------

Co-authored-by: suyao <sy20010504@gmail.com>
This commit is contained in:
Johnny.H 2025-09-24 23:27:07 +08:00 committed by GitHub
parent 09e9b95e08
commit 2bafc53b25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 19 additions and 6 deletions

View File

@ -1,4 +1,4 @@
import { LoadingIcon } from '@renderer/components/Icons' import { Spinner } from '@heroui/react'
import { MessageBlockStatus, MessageBlockType, type PlaceholderMessageBlock } from '@renderer/types/newMessage' import { MessageBlockStatus, MessageBlockType, type PlaceholderMessageBlock } from '@renderer/types/newMessage'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -10,7 +10,7 @@ const PlaceholderBlock: React.FC<PlaceholderBlockProps> = ({ block }) => {
if (block.status === MessageBlockStatus.PROCESSING && block.type === MessageBlockType.UNKNOWN) { if (block.status === MessageBlockStatus.PROCESSING && block.type === MessageBlockType.UNKNOWN) {
return ( return (
<MessageContentLoading> <MessageContentLoading>
<LoadingIcon /> <Spinner color="current" variant="dots" />
</MessageContentLoading> </MessageContentLoading>
) )
} }

View File

@ -3,7 +3,7 @@ import type { RootState } from '@renderer/store'
import { messageBlocksSelectors } from '@renderer/store/messageBlock' import { messageBlocksSelectors } from '@renderer/store/messageBlock'
import type { ImageMessageBlock, Message, MessageBlock } from '@renderer/types/newMessage' import type { ImageMessageBlock, Message, MessageBlock } from '@renderer/types/newMessage'
import { MessageBlockStatus, MessageBlockType } from '@renderer/types/newMessage' import { MessageBlockStatus, MessageBlockType } from '@renderer/types/newMessage'
import { isMainTextBlock, isVideoBlock } from '@renderer/utils/messageUtils/is' import { isMainTextBlock, isMessageProcessing, isVideoBlock } from '@renderer/utils/messageUtils/is'
import { AnimatePresence, motion, type Variants } from 'motion/react' import { AnimatePresence, motion, type Variants } from 'motion/react'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
@ -107,6 +107,9 @@ const MessageBlockRenderer: React.FC<Props> = ({ blocks, message }) => {
const renderedBlocks = blocks.map((blockId) => blockEntities[blockId]).filter(Boolean) const renderedBlocks = blocks.map((blockId) => blockEntities[blockId]).filter(Boolean)
const groupedBlocks = useMemo(() => groupSimilarBlocks(renderedBlocks), [renderedBlocks]) const groupedBlocks = useMemo(() => groupSimilarBlocks(renderedBlocks), [renderedBlocks])
// Check if message is still processing
const isProcessing = isMessageProcessing(message)
return ( return (
<AnimatePresence mode="sync"> <AnimatePresence mode="sync">
{groupedBlocks.map((block) => { {groupedBlocks.map((block) => {
@ -151,9 +154,6 @@ const MessageBlockRenderer: React.FC<Props> = ({ blocks, message }) => {
switch (block.type) { switch (block.type) {
case MessageBlockType.UNKNOWN: case MessageBlockType.UNKNOWN:
if (block.status === MessageBlockStatus.PROCESSING) {
blockComponent = <PlaceholderBlock key={block.id} block={block} />
}
break break
case MessageBlockType.MAIN_TEXT: case MessageBlockType.MAIN_TEXT:
case MessageBlockType.CODE: { case MessageBlockType.CODE: {
@ -213,6 +213,19 @@ const MessageBlockRenderer: React.FC<Props> = ({ blocks, message }) => {
</AnimatedBlockWrapper> </AnimatedBlockWrapper>
) )
})} })}
{isProcessing && (
<AnimatedBlockWrapper key="message-loading-placeholder" enableAnimation={true}>
<PlaceholderBlock
block={{
id: `loading-${message.id}`,
messageId: message.id,
type: MessageBlockType.UNKNOWN,
status: MessageBlockStatus.PROCESSING,
createdAt: new Date().toISOString()
}}
/>
</AnimatedBlockWrapper>
)}
</AnimatePresence> </AnimatePresence>
) )
} }