fix: update current topic id and support EmojiAvatar for ChatFlowHistory (#5861)

* fix: update current topic id for ChatFlowHistory to work

* refactor: set current topic id early in loadTopicMessagesThunk

* refactor: extract EmojiAvatar

* fix: style
This commit is contained in:
one 2025-05-15 01:42:18 +08:00 committed by GitHub
parent 9262f92bff
commit c7a15d291e
7 changed files with 95 additions and 66 deletions

View File

@ -0,0 +1,52 @@
import React, { memo } from 'react'
import styled from 'styled-components'
interface EmojiAvatarProps {
children: string
size?: number
fontSize?: number
onClick?: React.MouseEventHandler<HTMLDivElement>
className?: string
style?: React.CSSProperties
}
const EmojiAvatar = ({
ref,
children,
size = 31,
fontSize,
onClick,
className,
style
}: EmojiAvatarProps & { ref?: React.RefObject<HTMLDivElement | null> }) => (
<StyledEmojiAvatar
ref={ref}
$size={size}
$fontSize={fontSize ?? size * 0.5}
onClick={onClick}
className={className}
style={style}>
{children}
</StyledEmojiAvatar>
)
EmojiAvatar.displayName = 'EmojiAvatar'
const StyledEmojiAvatar = styled.div<{ $size: number; $fontSize: number }>`
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
border-radius: 20%;
cursor: pointer;
width: ${(props) => props.$size}px;
height: ${(props) => props.$size}px;
font-size: ${(props) => props.$fontSize}px;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
`
export default memo(EmojiAvatar)

View File

@ -1,4 +1,5 @@
import DefaultAvatar from '@renderer/assets/images/avatar.png'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import useAvatar from '@renderer/hooks/useAvatar'
import { useSettings } from '@renderer/hooks/useSettings'
import ImageStorage from '@renderer/services/ImageStorage'
@ -154,7 +155,13 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
}
}}
placement="bottom">
{isEmoji(avatar) ? <EmojiAvatar>{avatar}</EmojiAvatar> : <UserAvatar src={avatar} />}
{isEmoji(avatar) ? (
<EmojiAvatar size={80} fontSize={40}>
{avatar}
</EmojiAvatar>
) : (
<UserAvatar src={avatar} />
)}
</Popover>
</Dropdown>
</VStack>
@ -182,23 +189,6 @@ const UserAvatar = styled(Avatar)`
}
`
const EmojiAvatar = styled.div`
cursor: pointer;
width: 80px;
height: 80px;
border-radius: 20%;
background-color: var(--color-background-soft);
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
transition: opacity 0.3s ease;
border: 0.5px solid var(--color-border);
&:hover {
opacity: 0.8;
}
`
export default class UserPopup {
static topviewId = 0
static hide() {

View File

@ -1,3 +1,4 @@
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { isMac } from '@renderer/config/constant'
import { AppLogo, UserAvatar } from '@renderer/config/env'
import { useTheme } from '@renderer/context/ThemeProvider'
@ -70,7 +71,9 @@ const Sidebar: FC = () => {
return (
<Container id="app-sidebar" style={{ backgroundColor, zIndex: minappShow ? 10000 : 'initial' }}>
{isEmoji(avatar) ? (
<EmojiAvatar onClick={onEditUser}>{avatar}</EmojiAvatar>
<EmojiAvatar onClick={onEditUser} className="sidebar-avatar" size={31} fontSize={18}>
{avatar}
</EmojiAvatar>
) : (
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
)}
@ -319,6 +322,12 @@ const Container = styled.div`
height: ${isMac ? 'calc(100vh - var(--navbar-height))' : '100vh'};
-webkit-app-region: drag !important;
margin-top: ${isMac ? 'var(--navbar-height)' : 0};
.sidebar-avatar {
margin-bottom: ${isMac ? '12px' : '12px'};
margin-top: ${isMac ? '0px' : '2px'};
-webkit-app-region: none;
}
`
const AvatarImg = styled(Avatar)`
@ -331,23 +340,6 @@ const AvatarImg = styled(Avatar)`
cursor: pointer;
`
const EmojiAvatar = styled.div`
width: 31px;
height: 31px;
background-color: var(--color-background-soft);
margin-bottom: ${isMac ? '12px' : '12px'};
margin-top: ${isMac ? '0px' : '2px'};
border-radius: 20%;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
cursor: pointer;
-webkit-app-region: none;
border: 0.5px solid var(--color-border);
font-size: 20px;
`
const MainMenusContainer = styled.div`
display: flex;
flex: 1;

View File

@ -1,14 +1,17 @@
import '@xyflow/react/dist/style.css'
import { RobotOutlined, UserOutlined } from '@ant-design/icons'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { getModelLogo } from '@renderer/config/models'
import { useTheme } from '@renderer/context/ThemeProvider'
import useAvatar from '@renderer/hooks/useAvatar'
import { useSettings } from '@renderer/hooks/useSettings'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { RootState } from '@renderer/store'
import { selectMessagesForTopic } from '@renderer/store/newMessage'
import { Model } from '@renderer/types'
import { isEmoji } from '@renderer/utils'
import { getMainTextContent } from '@renderer/utils/messageUtils/find'
import { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } from '@xyflow/react'
import { Edge, Node, NodeTypes, Position, useEdgesState, useNodesState } from '@xyflow/react'
@ -63,7 +66,11 @@ const CustomNode: FC<{ data: any }> = ({ data }) => {
// 用户头像
if (data.userAvatar) {
avatar = <Avatar src={data.userAvatar} alt={title} />
if (isEmoji(data.userAvatar)) {
avatar = <EmojiAvatar size={32}>{data.userAvatar}</EmojiAvatar>
} else {
avatar = <Avatar src={data.userAvatar} alt={title} />
}
} else {
avatar = <Avatar icon={<UserOutlined />} style={{ backgroundColor: 'var(--color-info)' }} />
}
@ -221,7 +228,7 @@ const ChatFlowHistory: FC<ChatFlowHistoryProps> = ({ conversationId }) => {
)
// 获取用户头像
const userAvatar = useSelector((state: RootState) => state.runtime.avatar)
const userAvatar = useAvatar()
// 消息过滤
const { userMessages, assistantMessages } = useMemo(() => {

View File

@ -1,4 +1,5 @@
import { DownOutlined } from '@ant-design/icons'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
import { getModelLogo } from '@renderer/config/models'
import { useTheme } from '@renderer/context/ThemeProvider'
@ -16,6 +17,7 @@ import { Avatar } from 'antd'
import { type FC, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface MessageLineProps {
messages: Message[]
}
@ -230,7 +232,15 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
) : (
<>
{isEmoji(avatar) ? (
<EmojiAvatar size={size}>{avatar}</EmojiAvatar>
<EmojiAvatar
size={size}
fontSize={size * 0.6}
style={{
cursor: 'default',
pointerEvents: 'none'
}}>
{avatar}
</EmojiAvatar>
) : (
<Avatar src={avatar} size={size} />
)}
@ -314,16 +324,4 @@ const MessageItemContent = styled.div`
max-width: 200px;
`
const EmojiAvatar = styled.div<{ size: number }>`
width: ${(props) => props.size}px;
height: ${(props) => props.size}px;
background-color: var(--color-background-soft);
border-radius: 20%;
display: flex;
align-items: center;
justify-content: center;
font-size: ${(props) => props.size * 0.6}px;
border: 0.5px solid var(--color-border);
`
export default MessageAnchorLine

View File

@ -1,3 +1,4 @@
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import UserPopup from '@renderer/components/Popups/UserPopup'
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
import { getModelLogo } from '@renderer/config/models'
@ -87,7 +88,9 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
) : (
<>
{isEmoji(avatar) ? (
<EmojiAvatar onClick={() => UserPopup.show()}>{avatar}</EmojiAvatar>
<EmojiAvatar onClick={() => UserPopup.show()} size={35} fontSize={20}>
{avatar}
</EmojiAvatar>
) : (
<Avatar
src={avatar}
@ -111,20 +114,6 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
MessageHeader.displayName = 'MessageHeader'
const EmojiAvatar = styled.div`
width: 35px;
height: 35px;
background-color: var(--color-background-soft);
border-radius: 20%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
cursor: pointer;
border: 0.5px solid var(--color-border);
font-size: 20px;
`
const Container = styled.div`
display: flex;
flex-direction: row;

View File

@ -726,6 +726,7 @@ export const loadTopicMessagesThunk =
async (dispatch: AppDispatch, getState: () => RootState) => {
const state = getState()
const topicMessagesExist = !!state.messages.messageIdsByTopic[topicId]
dispatch(newMessagesActions.setCurrentTopicId(topicId))
if (topicMessagesExist && !forceReload) {
return