From 1e3829899a0c03dbc93657bb1d58ff001ceed80e Mon Sep 17 00:00:00 2001 From: 2977094657 <2977094657@qq.com> Date: Sun, 15 Mar 2026 17:12:25 +0800 Subject: [PATCH 1/2] fix(chat): add exited group toggle filter --- src/pages/ChatPage.scss | 27 +++++++++++++++++--- src/pages/ChatPage.tsx | 55 +++++++++++++++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/pages/ChatPage.scss b/src/pages/ChatPage.scss index e00feb5..3c47e24 100644 --- a/src/pages/ChatPage.scss +++ b/src/pages/ChatPage.scss @@ -304,11 +304,12 @@ } } - .refresh-btn { + .refresh-btn, + .session-filter-btn { width: 32px; height: 32px; padding: 0; - border: none; + border: 1px solid transparent; background: var(--bg-tertiary); border-radius: 8px; color: var(--text-secondary); @@ -324,6 +325,12 @@ color: var(--text-primary); } + &.active { + background: var(--primary-light); + border-color: var(--primary-light); + color: var(--primary); + } + &:disabled { opacity: 0.4; cursor: not-allowed; @@ -812,7 +819,7 @@ .icon-btn { width: 32px; height: 32px; - border: none; + border: 1px solid transparent; background: transparent; border-radius: 6px; cursor: pointer; @@ -835,6 +842,20 @@ } } + .session-filter-btn { + background: var(--bg-primary); + + &:hover { + background: var(--bg-hover); + } + + &.active { + background: var(--primary-light); + border-color: var(--primary-light); + color: var(--primary); + } + } + .search-row { flex: 1; display: flex; diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index 50e1534..b6ec3db 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react' -import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, ChevronLeft, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check, CheckSquare, Download, BarChart3, Edit2, Trash2, BellOff, Users, FolderClosed, UserCheck, Crown, Aperture } from 'lucide-react' +import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, ChevronLeft, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check, CheckSquare, Download, BarChart3, Edit2, Trash2, BellOff, Users, FolderClosed, UserCheck, Crown, Aperture, LogOut } from 'lucide-react' import { useNavigate } from 'react-router-dom' import { createPortal } from 'react-dom' import { useChatStore } from '../stores/chatStore' @@ -34,6 +34,16 @@ const SYSTEM_MESSAGE_TYPES = [ 266287972401, // 拍一拍 ] +const EXITED_GROUP_SUMMARY_EXACT = '你已退出该群聊' + +function isExitedGroupSession(session: Pick): boolean { + return ( + session.username.endsWith('@chatroom') && + session.lastMsgType === 10000 && + session.summary.trim() === EXITED_GROUP_SUMMARY_EXACT + ) +} + interface XmlField { key: string; value: string; @@ -512,6 +522,7 @@ function ChatPage(props: ChatPageProps) { const [groupMembersLoadingHint, setGroupMembersLoadingHint] = useState('') const [isRefreshingGroupMembers, setIsRefreshingGroupMembers] = useState(false) const [groupMemberSearchKeyword, setGroupMemberSearchKeyword] = useState('') + const [hideExitedGroups, setHideExitedGroups] = useState(true) const [copiedField, setCopiedField] = useState(null) const [highlightedMessageKeys, setHighlightedMessageKeys] = useState([]) const [isRefreshingSessions, setIsRefreshingSessions] = useState(false) @@ -581,6 +592,25 @@ function ChatPage(props: ChatPageProps) { const highlightedMessageSet = useMemo(() => new Set(highlightedMessageKeys), [highlightedMessageKeys]) + const exitedGroupCount = useMemo(() => { + if (!Array.isArray(sessions)) return 0 + return sessions.filter(isExitedGroupSession).length + }, [sessions]) + const sessionsAfterExitedGroupFilter = useMemo(() => { + if (!Array.isArray(sessions)) return [] + if (!hideExitedGroups) return sessions + return sessions.filter((session) => !isExitedGroupSession(session)) + }, [sessions, hideExitedGroups]) + const exitedGroupFilterTitle = useMemo(() => { + if (hideExitedGroups) { + return exitedGroupCount > 0 + ? `已隐藏 ${exitedGroupCount} 个已退出群聊,点击显示` + : '已开启:隐藏已退出群聊' + } + return exitedGroupCount > 0 + ? `当前显示全部群聊(含 ${exitedGroupCount} 个已退出群聊),点击隐藏` + : '当前显示全部群聊,点击隐藏已退出群聊' + }, [exitedGroupCount, hideExitedGroups]) const messageKeySetRef = useRef>(new Set()) const lastMessageTimeRef = useRef(0) const sessionMapRef = useRef>(new Map()) @@ -2963,16 +2993,16 @@ function ChatPage(props: ChatPageProps) { // 普通视图:隐藏 isFolded 的群,保留 placeholder_foldgroup 入口 useEffect(() => { - if (!Array.isArray(sessions)) { + if (!Array.isArray(sessionsAfterExitedGroupFilter)) { setFilteredSessions([]) return } // 检查是否有折叠的群聊 - const foldedGroups = sessions.filter(s => s.isFolded && !s.username.toLowerCase().includes('placeholder_foldgroup')) + const foldedGroups = sessionsAfterExitedGroupFilter.filter(s => s.isFolded && !s.username.toLowerCase().includes('placeholder_foldgroup')) const hasFoldedGroups = foldedGroups.length > 0 - let visible = sessions.filter(s => { + let visible = sessionsAfterExitedGroupFilter.filter(s => { if (s.isFolded && !s.username.toLowerCase().includes('placeholder_foldgroup')) return false return true }) @@ -3022,12 +3052,12 @@ function ChatPage(props: ChatPageProps) { s.username.toLowerCase().includes(lower) || s.summary.toLowerCase().includes(lower) )) - }, [sessions, searchKeyword, setFilteredSessions]) + }, [sessionsAfterExitedGroupFilter, searchKeyword, setFilteredSessions]) // 折叠群列表(独立计算,供折叠 panel 使用) const foldedSessions = useMemo(() => { - if (!Array.isArray(sessions)) return [] - const folded = sessions.filter(s => s.isFolded) + if (!Array.isArray(sessionsAfterExitedGroupFilter)) return [] + const folded = sessionsAfterExitedGroupFilter.filter(s => s.isFolded) if (!searchKeyword.trim() || !foldedView) return folded const lower = searchKeyword.toLowerCase() return folded.filter(s => @@ -3035,7 +3065,7 @@ function ChatPage(props: ChatPageProps) { s.username.toLowerCase().includes(lower) || s.summary.toLowerCase().includes(lower) ) - }, [sessions, searchKeyword, foldedView]) + }, [sessionsAfterExitedGroupFilter, searchKeyword, foldedView]) const hasSessionRecords = Array.isArray(sessions) && sessions.length > 0 const shouldShowSessionsSkeleton = isLoadingSessions && !hasSessionRecords @@ -3878,6 +3908,15 @@ function ChatPage(props: ChatPageProps) { {/* 普通 header */}
+
Date: Sun, 15 Mar 2026 17:14:41 +0800 Subject: [PATCH 2/2] fix(chat): replace exited-group filter icon --- src/pages/ChatPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index b6ec3db..d4a9d0a 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react' -import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, ChevronLeft, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check, CheckSquare, Download, BarChart3, Edit2, Trash2, BellOff, Users, FolderClosed, UserCheck, Crown, Aperture, LogOut } from 'lucide-react' +import { Search, MessageSquare, MessageSquareOff, AlertCircle, Loader2, RefreshCw, X, ChevronDown, ChevronLeft, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check, CheckSquare, Download, BarChart3, Edit2, Trash2, BellOff, Users, FolderClosed, UserCheck, Crown, Aperture } from 'lucide-react' import { useNavigate } from 'react-router-dom' import { createPortal } from 'react-dom' import { useChatStore } from '../stores/chatStore' @@ -3915,7 +3915,7 @@ function ChatPage(props: ChatPageProps) { aria-label={hideExitedGroups ? '显示已退出群聊' : '隐藏已退出群聊'} aria-pressed={hideExitedGroups} > - +