mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 22:10:21 +08:00
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:
parent
928530c6f6
commit
7332763d9f
52
src/renderer/src/components/Avatar/EmojiAvatar.tsx
Normal file
52
src/renderer/src/components/Avatar/EmojiAvatar.tsx
Normal 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)
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import DefaultAvatar from '@renderer/assets/images/avatar.png'
|
import DefaultAvatar from '@renderer/assets/images/avatar.png'
|
||||||
|
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import ImageStorage from '@renderer/services/ImageStorage'
|
import ImageStorage from '@renderer/services/ImageStorage'
|
||||||
@ -154,7 +155,13 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
{isEmoji(avatar) ? <EmojiAvatar>{avatar}</EmojiAvatar> : <UserAvatar src={avatar} />}
|
{isEmoji(avatar) ? (
|
||||||
|
<EmojiAvatar size={80} fontSize={40}>
|
||||||
|
{avatar}
|
||||||
|
</EmojiAvatar>
|
||||||
|
) : (
|
||||||
|
<UserAvatar src={avatar} />
|
||||||
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</VStack>
|
</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 {
|
export default class UserPopup {
|
||||||
static topviewId = 0
|
static topviewId = 0
|
||||||
static hide() {
|
static hide() {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { AppLogo, UserAvatar } from '@renderer/config/env'
|
import { AppLogo, UserAvatar } from '@renderer/config/env'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
@ -70,7 +71,9 @@ const Sidebar: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Container id="app-sidebar" style={{ backgroundColor, zIndex: minappShow ? 10000 : 'initial' }}>
|
<Container id="app-sidebar" style={{ backgroundColor, zIndex: minappShow ? 10000 : 'initial' }}>
|
||||||
{isEmoji(avatar) ? (
|
{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} />
|
<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'};
|
height: ${isMac ? 'calc(100vh - var(--navbar-height))' : '100vh'};
|
||||||
-webkit-app-region: drag !important;
|
-webkit-app-region: drag !important;
|
||||||
margin-top: ${isMac ? 'var(--navbar-height)' : 0};
|
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)`
|
const AvatarImg = styled(Avatar)`
|
||||||
@ -331,23 +340,6 @@ const AvatarImg = styled(Avatar)`
|
|||||||
cursor: pointer;
|
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`
|
const MainMenusContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
import '@xyflow/react/dist/style.css'
|
import '@xyflow/react/dist/style.css'
|
||||||
|
|
||||||
import { RobotOutlined, UserOutlined } from '@ant-design/icons'
|
import { RobotOutlined, UserOutlined } from '@ant-design/icons'
|
||||||
|
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
|
||||||
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
||||||
import { getModelLogo } from '@renderer/config/models'
|
import { getModelLogo } from '@renderer/config/models'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { RootState } from '@renderer/store'
|
import { RootState } from '@renderer/store'
|
||||||
import { selectMessagesForTopic } from '@renderer/store/newMessage'
|
import { selectMessagesForTopic } from '@renderer/store/newMessage'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
|
import { isEmoji } from '@renderer/utils'
|
||||||
import { getMainTextContent } from '@renderer/utils/messageUtils/find'
|
import { getMainTextContent } from '@renderer/utils/messageUtils/find'
|
||||||
import { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } from '@xyflow/react'
|
import { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } from '@xyflow/react'
|
||||||
import { Edge, Node, NodeTypes, Position, useEdgesState, useNodesState } 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) {
|
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 {
|
} else {
|
||||||
avatar = <Avatar icon={<UserOutlined />} style={{ backgroundColor: 'var(--color-info)' }} />
|
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(() => {
|
const { userMessages, assistantMessages } = useMemo(() => {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { DownOutlined } from '@ant-design/icons'
|
import { DownOutlined } from '@ant-design/icons'
|
||||||
|
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
|
||||||
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
|
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
|
||||||
import { getModelLogo } from '@renderer/config/models'
|
import { getModelLogo } from '@renderer/config/models'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
@ -16,6 +17,7 @@ import { Avatar } from 'antd'
|
|||||||
import { type FC, useCallback, useEffect, useRef, useState } from 'react'
|
import { type FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
interface MessageLineProps {
|
interface MessageLineProps {
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
}
|
}
|
||||||
@ -230,7 +232,15 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{isEmoji(avatar) ? (
|
{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} />
|
<Avatar src={avatar} size={size} />
|
||||||
)}
|
)}
|
||||||
@ -314,16 +324,4 @@ const MessageItemContent = styled.div`
|
|||||||
max-width: 200px;
|
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
|
export default MessageAnchorLine
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
|
||||||
import UserPopup from '@renderer/components/Popups/UserPopup'
|
import UserPopup from '@renderer/components/Popups/UserPopup'
|
||||||
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
|
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
|
||||||
import { getModelLogo } from '@renderer/config/models'
|
import { getModelLogo } from '@renderer/config/models'
|
||||||
@ -87,7 +88,9 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{isEmoji(avatar) ? (
|
{isEmoji(avatar) ? (
|
||||||
<EmojiAvatar onClick={() => UserPopup.show()}>{avatar}</EmojiAvatar>
|
<EmojiAvatar onClick={() => UserPopup.show()} size={35} fontSize={20}>
|
||||||
|
{avatar}
|
||||||
|
</EmojiAvatar>
|
||||||
) : (
|
) : (
|
||||||
<Avatar
|
<Avatar
|
||||||
src={avatar}
|
src={avatar}
|
||||||
@ -111,20 +114,6 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message }) => {
|
|||||||
|
|
||||||
MessageHeader.displayName = 'MessageHeader'
|
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`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -726,6 +726,7 @@ export const loadTopicMessagesThunk =
|
|||||||
async (dispatch: AppDispatch, getState: () => RootState) => {
|
async (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const state = getState()
|
const state = getState()
|
||||||
const topicMessagesExist = !!state.messages.messageIdsByTopic[topicId]
|
const topicMessagesExist = !!state.messages.messageIdsByTopic[topicId]
|
||||||
|
dispatch(newMessagesActions.setCurrentTopicId(topicId))
|
||||||
|
|
||||||
if (topicMessagesExist && !forceReload) {
|
if (topicMessagesExist && !forceReload) {
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user