feat: add chat navigation bar close (#4019)

* feat(聊天导航): 新增关闭、置顶和置底按钮并更新图标

在聊天导航组件中新增了关闭、置顶和置底按钮,并更新了相关图标以提升用户体验。同时,添加了点击关闭按钮时隐藏导航的功能。

* feat(消息导航): 添加手动关闭状态以避免误触

在 ChatNavigation 组件中添加 `manuallyClosedUntil` 状态,用于在用户手动关闭导航后,1分钟内不响应鼠标靠近事件。这可以防止用户在操作时误触导航栏,提升用户体验。

* refactor(ChatNavigation): 重命名函数并添加滚动处理逻辑

重命名 handleChatNavigationClick 为 handleCloseChatNavigation 以提高代码可读性,并添加 handleScrollToTop 和 handleScrollToBottom 函数以处理滚动逻辑

* fix: 修复滚动到顶部时位置不正确的问题

将 `scrollToTop` 函数中的 `top` 值从 `0` 改为 `-container.scrollHeight`,以确保滚动到顶部时位置正确

* docs(i18n): 添加新的翻译字符串以支持更多操作

在多个语言文件中添加了“回到顶部”、“回到底部”和“关闭”的翻译字符串,以支持更多用户界面操作。

* refactor: 移除未使用的变量以简化代码
```

解释:
- **类型**: `refactor`,因为这是代码重构,移除了未使用的变量,没有改变功能行为。
- **描述**: 移除了未使用的变量以简化代码,符合简洁和可维护性的原则。
This commit is contained in:
Hobee Liu 2025-04-01 16:17:57 +08:00 committed by GitHub
parent a90be7e83f
commit 8b9929cc7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 112 additions and 15 deletions

View File

@ -150,7 +150,10 @@
"history": "Chat History",
"last": "Already at the last message",
"next": "Next Message",
"prev": "Previous Message"
"prev": "Previous Message",
"top": "Back to top",
"bottom": "Back to bottom",
"close": "Close"
},
"resend": "Resend",
"save": "Save",

View File

@ -150,7 +150,10 @@
"history": "チャット履歴",
"last": "最後のメッセージです",
"next": "次のメッセージ",
"prev": "前のメッセージ"
"prev": "前のメッセージ",
"top": "トップに戻る",
"bottom": "下部に戻る",
"close": "閉じる"
},
"resend": "再送信",
"save": "保存",

View File

@ -150,7 +150,10 @@
"history": "История чата",
"last": "Уже последнее сообщение",
"next": "Следующее сообщение",
"prev": "Предыдущее сообщение"
"prev": "Предыдущее сообщение",
"top": "Вернуться наверх",
"bottom": "Вернуться вниз",
"close": "Закрыть"
},
"resend": "Переотправить",
"save": "Сохранить",

View File

@ -150,7 +150,10 @@
"history": "聊天历史",
"last": "已经是最后一条消息",
"next": "下一条消息",
"prev": "上一条消息"
"prev": "上一条消息",
"top": "回到顶部",
"bottom": "回到底部",
"close": "关闭"
},
"resend": "重新发送",
"save": "保存",

View File

@ -150,7 +150,10 @@
"history": "聊天歷史",
"last": "已經是最後一條訊息",
"next": "下一條訊息",
"prev": "上一條訊息"
"prev": "上一條訊息",
"top": "回到頂部",
"bottom": "回到底部",
"close": "關閉"
},
"resend": "重新傳送",
"save": "儲存",

View File

@ -130,7 +130,10 @@
"first": "Ήδη το πρώτο μήνυμα",
"last": "Ήδη το τελευταίο μήνυμα",
"next": "Επόμενο μήνυμα",
"prev": "Προηγούμενο μήνυμα"
"prev": "Προηγούμενο μήνυμα",
"top": "Επιστροφή στην κορυφή",
"bottom": "Επιστροφή στο κάτω μέρος",
"close": "Κλείσιμο"
},
"resend": "Ξαναστείλε",
"save": "Αποθήκευση",

View File

@ -130,7 +130,10 @@
"first": "Ya es el primer mensaje",
"last": "Ya es el último mensaje",
"next": "Siguiente mensaje",
"prev": "Mensaje anterior"
"prev": "Mensaje anterior",
"top": "Volver arriba",
"bottom": "Volver abajo",
"close": "Cerrar"
},
"resend": "Reenviar",
"save": "Guardar",

View File

@ -130,7 +130,10 @@
"first": "Déjà premier message",
"last": "Déjà dernier message",
"next": "Prochain message",
"prev": "Précédent message"
"prev": "Précédent message",
"top": "Retour en haut",
"bottom": "Retour en bas",
"close": "Fermer"
},
"resend": "Réenvoyer",
"save": "Enregistrer",

View File

@ -130,7 +130,10 @@
"first": "Esta é a primeira mensagem",
"last": "Esta é a última mensagem",
"next": "Próxima mensagem",
"prev": "Mensagem anterior"
"prev": "Mensagem anterior",
"top": "Voltar ao topo",
"bottom": "Voltar ao fundo",
"close": "Fechar"
},
"resend": "Reenviar",
"save": "Salvar",

View File

@ -1,4 +1,11 @@
import { DownOutlined, HistoryOutlined, UpOutlined } from '@ant-design/icons'
import {
ArrowDownOutlined,
ArrowUpOutlined,
CloseOutlined,
HistoryOutlined,
VerticalAlignBottomOutlined,
VerticalAlignTopOutlined
} from '@ant-design/icons'
import { useSettings } from '@renderer/hooks/useSettings'
import { RootState } from '@renderer/store'
import { selectCurrentTopicId } from '@renderer/store/messages'
@ -20,6 +27,7 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
const [isNearButtons, setIsNearButtons] = useState(false)
const [hideTimer, setHideTimer] = useState<NodeJS.Timeout | null>(null)
const [showChatHistory, setShowChatHistory] = useState(false)
const [manuallyClosedUntil, setManuallyClosedUntil] = useState<number | null>(null)
const currentTopicId = useSelector((state: RootState) => selectCurrentTopicId(state))
const lastMoveTime = useRef(0)
const { topicPosition, showTopics } = useSettings()
@ -44,6 +52,10 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
// Handle mouse entering button area
const handleMouseEnter = useCallback(() => {
if (manuallyClosedUntil && Date.now() < manuallyClosedUntil) {
return
}
setIsNearButtons(true)
setIsVisible(true)
@ -52,7 +64,7 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
clearTimeout(hideTimer)
setHideTimer(null)
}
}, [hideTimer])
}, [hideTimer, manuallyClosedUntil])
// Handle mouse leaving button area
const handleMouseLeave = useCallback(() => {
@ -97,7 +109,7 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
const scrollToTop = () => {
const container = document.getElementById(containerId)
container && container.scrollTo({ top: 0, behavior: 'smooth' })
container && container.scrollTo({ top: -container.scrollHeight, behavior: 'smooth' })
}
const scrollToBottom = () => {
@ -148,6 +160,23 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
return -1
}
// 修改 handleCloseChatNavigation 函数
const handleCloseChatNavigation = () => {
setIsVisible(false)
// 设置手动关闭状态1分钟内不响应鼠标靠近事件
setManuallyClosedUntil(Date.now() + 60000) // 60000毫秒 = 1分钟
}
const handleScrollToTop = () => {
resetHideTimer()
scrollToTop()
}
const handleScrollToBottom = () => {
resetHideTimer()
scrollToBottom()
}
const handleNextMessage = () => {
resetHideTimer()
const userMessages = findUserMessages()
@ -216,6 +245,11 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
// Throttled mouse move handler to improve performance
const handleMouseMove = (e: MouseEvent) => {
// 如果在手动关闭期间,不响应鼠标移动事件
if (manuallyClosedUntil && Date.now() < manuallyClosedUntil) {
return
}
// Throttle mouse move to every 50ms for performance
const now = Date.now()
if (now - lastMoveTime.current < 50) return
@ -262,16 +296,43 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
clearTimeout(hideTimer)
}
}
}, [containerId, hideTimer, resetHideTimer, isNearButtons, handleMouseEnter, handleMouseLeave, showRightTopics])
}, [
containerId,
hideTimer,
resetHideTimer,
isNearButtons,
handleMouseEnter,
handleMouseLeave,
showRightTopics,
manuallyClosedUntil
])
return (
<>
<NavigationContainer $isVisible={isVisible} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<ButtonGroup>
<Tooltip title={t('chat.navigation.close')} placement="left">
<NavigationButton
type="text"
icon={<CloseOutlined />}
onClick={handleCloseChatNavigation}
aria-label={t('chat.navigation.close')}
/>
</Tooltip>
<Divider />
<Tooltip title={t('chat.navigation.top')} placement="left">
<NavigationButton
type="text"
icon={<VerticalAlignTopOutlined />}
onClick={handleScrollToTop}
aria-label={t('chat.navigation.top')}
/>
</Tooltip>
<Divider />
<Tooltip title={t('chat.navigation.prev')} placement="left">
<NavigationButton
type="text"
icon={<UpOutlined />}
icon={<ArrowUpOutlined />}
onClick={handlePrevMessage}
aria-label={t('chat.navigation.prev')}
/>
@ -280,12 +341,21 @@ const ChatNavigation: FC<ChatNavigationProps> = ({ containerId }) => {
<Tooltip title={t('chat.navigation.next')} placement="left">
<NavigationButton
type="text"
icon={<DownOutlined />}
icon={<ArrowDownOutlined />}
onClick={handleNextMessage}
aria-label={t('chat.navigation.next')}
/>
</Tooltip>
<Divider />
<Tooltip title={t('chat.navigation.bottom')} placement="left">
<NavigationButton
type="text"
icon={<VerticalAlignBottomOutlined />}
onClick={handleScrollToBottom}
aria-label={t('chat.navigation.bottom')}
/>
</Tooltip>
<Divider />
<Tooltip title={t('chat.navigation.history')} placement="left">
<NavigationButton
type="text"