feat(pending-animation): 当消息处于后台pending时,助手头像跟话题显示脉冲动画效果 (#3867)
This commit is contained in:
parent
9b98312775
commit
6699b0902f
18
src/renderer/src/assets/styles/animation.scss
Normal file
18
src/renderer/src/assets/styles/animation.scss
Normal file
@ -0,0 +1,18 @@
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.5);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 var(--pulse-size) rgba(var(--pulse-color), 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 电磁波扩散效果
|
||||
.animation-pulse {
|
||||
--pulse-color: 59, 130, 246;
|
||||
--pulse-size: 8px;
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
@use './ant.scss';
|
||||
@use './scrollbar.scss';
|
||||
@use './container.scss';
|
||||
@use './animation.scss';
|
||||
@import '../fonts/icon-fonts/iconfont.css';
|
||||
@import '../fonts/ubuntu/ubuntu.css';
|
||||
|
||||
|
||||
@ -8,9 +8,10 @@ interface Props {
|
||||
model: Model
|
||||
size: number
|
||||
props?: AvatarProps
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ModelAvatar: FC<Props> = ({ model, size, props }) => {
|
||||
const ModelAvatar: FC<Props> = ({ model, size, props, className }) => {
|
||||
return (
|
||||
<Avatar
|
||||
src={getModelLogo(model?.id || '')}
|
||||
@ -23,7 +24,8 @@ const ModelAvatar: FC<Props> = ({ model, size, props }) => {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
{...props}>
|
||||
{...props}
|
||||
className={className}>
|
||||
{first(model?.name)}
|
||||
</Avatar>
|
||||
)
|
||||
|
||||
@ -9,10 +9,11 @@ import { getDefaultModel, getDefaultTopic } from '@renderer/services/AssistantSe
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||
import { Assistant } from '@renderer/types'
|
||||
import { uuid } from '@renderer/utils'
|
||||
import { hasTopicPendingRequests } from '@renderer/utils/queue'
|
||||
import { Dropdown } from 'antd'
|
||||
import { ItemType } from 'antd/es/menu/interface'
|
||||
import { omit } from 'lodash'
|
||||
import { FC, useCallback } from 'react'
|
||||
import { FC, useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -32,6 +33,17 @@ const AssistantItem: FC<AssistantItemProps> = ({ assistant, isActive, onSwitch,
|
||||
const { clickAssistantToShowTopic, topicPosition, showAssistantIcon } = useSettings()
|
||||
const defaultModel = getDefaultModel()
|
||||
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
useEffect(() => {
|
||||
if (isActive) {
|
||||
setIsPending(false)
|
||||
}
|
||||
const hasPending = assistant.topics.some((topic) => hasTopicPendingRequests(topic.id))
|
||||
if (hasPending) {
|
||||
setIsPending(true)
|
||||
}
|
||||
}, [isActive, assistant.topics])
|
||||
|
||||
const getMenuItems = useCallback(
|
||||
(assistant: Assistant): ItemType[] => [
|
||||
{
|
||||
@ -116,7 +128,13 @@ const AssistantItem: FC<AssistantItemProps> = ({ assistant, isActive, onSwitch,
|
||||
<Dropdown menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
|
||||
<Container onClick={handleSwitch} className={isActive ? 'active' : ''}>
|
||||
<AssistantNameRow className="name" title={fullAssistantName}>
|
||||
{showAssistantIcon && <ModelAvatar model={assistant.model || defaultModel} size={22} />}
|
||||
{showAssistantIcon && (
|
||||
<ModelAvatar
|
||||
model={assistant.model || defaultModel}
|
||||
size={22}
|
||||
className={isPending && !isActive ? 'animation-pulse' : ''}
|
||||
/>
|
||||
)}
|
||||
<AssistantName className="text-nowrap">{showAssistantIcon ? assistantName : fullAssistantName}</AssistantName>
|
||||
</AssistantNameRow>
|
||||
{isActive && (
|
||||
|
||||
@ -32,10 +32,11 @@ import {
|
||||
exportTopicToNotion,
|
||||
topicToMarkdown
|
||||
} from '@renderer/utils/export'
|
||||
import { hasTopicPendingRequests } from '@renderer/utils/queue'
|
||||
import { Dropdown, MenuProps, Tooltip } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import { findIndex } from 'lodash'
|
||||
import { FC, useCallback, useRef, useState } from 'react'
|
||||
import { FC, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -56,6 +57,28 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
||||
const [deletingTopicId, setDeletingTopicId] = useState<string | null>(null)
|
||||
const deleteTimerRef = useRef<NodeJS.Timeout>()
|
||||
|
||||
const pendingTopics = useMemo(() => {
|
||||
return new Set<string>()
|
||||
}, [])
|
||||
const isPending = useCallback(
|
||||
(topicId: string) => {
|
||||
const hasPending = hasTopicPendingRequests(topicId)
|
||||
if (topicId === activeTopic.id && !hasPending) {
|
||||
pendingTopics.delete(topicId)
|
||||
return false
|
||||
}
|
||||
if (pendingTopics.has(topicId)) {
|
||||
return true
|
||||
}
|
||||
if (hasPending) {
|
||||
pendingTopics.add(topicId)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
[activeTopic.id, pendingTopics]
|
||||
)
|
||||
|
||||
const handleDeleteClick = useCallback((topicId: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
|
||||
@ -322,6 +345,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
||||
className={isActive ? 'active' : ''}
|
||||
onClick={() => onSwitchTopic(topic)}
|
||||
style={{ borderRadius }}>
|
||||
{isPending(topic.id) && !isActive && <PendingIndicator />}
|
||||
<TopicName className="name" title={topicName}>
|
||||
{topicName}
|
||||
</TopicName>
|
||||
@ -395,6 +419,7 @@ const TopicListItem = styled.div`
|
||||
font-family: Ubuntu;
|
||||
cursor: pointer;
|
||||
border: 0.5px solid transparent;
|
||||
position: relative;
|
||||
.menu {
|
||||
opacity: 0;
|
||||
color: var(--color-text-3);
|
||||
@ -427,6 +452,19 @@ const TopicName = styled.div`
|
||||
font-size: 13px;
|
||||
`
|
||||
|
||||
const PendingIndicator = styled.div.attrs({
|
||||
className: 'animation-pulse'
|
||||
})`
|
||||
--pulse-size: 5px;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 15px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-primary);
|
||||
`
|
||||
|
||||
const TopicPromptText = styled.div`
|
||||
color: var(--color-text-2);
|
||||
font-size: 12px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user