feat(ui): unify design system to Twitter/X style across screens and improve message handling
Some checks failed
Frontend CI / build-and-push-web (push) Failing after 3m9s
Frontend CI / ota-android (push) Successful in 10m29s
Frontend CI / build-android-apk (push) Successful in 43m4s

Redesign multiple screens and components from card-based responsive design to Twitter/X flat design:
- UserProfileHeader: adopt Twitter/X style with larger icons, simplified buttons, and block functionality
- AppBackButton: update to chevron-left icon with larger size, remove background styling
- Update SettingsScreen, EditProfileScreen, and UserProfileScreen with flat layout approach
- ConversationListRow: convert from bordered cards to flat list rows with updated typography

Improve message system with WebSocket notification handling:
- Add real-time system unread count updates via WebSocket
- Track lastSystemMessageAt for accurate system message timestamps
- Add notification deduplication and read-status filtering
- Combine system and conversation unread counts in background sync
- Clear system notifications from notification center on mark as read
This commit is contained in:
lafay
2026-04-25 00:39:40 +08:00
parent e0d28535f4
commit ad19bc2af7
26 changed files with 950 additions and 1121 deletions

View File

@@ -376,6 +376,18 @@ export class MessageSyncService implements IMessageSyncService {
store.setUnreadCount(totalUnread, systemUnread);
// 获取最后一条系统通知的时间
if (systemUnread > 0) {
try {
const latestSystemMsg = await messageService.getSystemMessagesCursor({ cursor: '', page_size: 1 });
if (latestSystemMsg?.list?.[0]?.created_at) {
store.setLastSystemMessageAt(latestSystemMsg.list[0].created_at);
}
} catch {
// 获取最后系统通知时间失败,不影响主流程
}
}
// 服务端汇总未读为 0 时,清掉内存中残留的红点
if (totalUnread === 0) {
const currentConversations = store.getState().conversations;

View File

@@ -16,6 +16,7 @@ import type {
WSGroupRecallMessage,
WSGroupTypingMessage,
WSGroupNoticeMessage,
WSNotificationMessage,
} from '@/services/core';
import { wsService, GroupNoticeType } from '@/services/core';
import { eventBus } from '@/core/events/EventBus';
@@ -128,6 +129,20 @@ export class WSMessageHandler implements IWSMessageHandler {
this.handleGroupNotice(message);
});
// 监听系统通知,实时更新系统未读数
wsService.on('notification', (message: WSNotificationMessage) => {
if (!message.is_read) {
this.incrementSystemUnread();
}
if (message.created_at) {
const store = useMessageStore.getState();
const currentLast = store.lastSystemMessageAt;
if (!currentLast || new Date(message.created_at) > new Date(currentLast)) {
store.setLastSystemMessageAt(message.created_at);
}
}
});
// 监听连接状态
wsService.onConnect(() => {
useMessageStore.getState().setSSEConnected(true);
@@ -484,6 +499,12 @@ export class WSMessageHandler implements IWSMessageHandler {
}
}
private incrementSystemUnread(): void {
const store = useMessageStore.getState();
const currentUnread = store.getUnreadCount();
store.setUnreadCount(currentUnread.total + 1, currentUnread.system + 1);
}
private markMessageAsRecalled(conversationId: string, messageId: string): void {
const store = useMessageStore.getState();
const messages = store.getMessages(conversationId);

View File

@@ -31,6 +31,9 @@ export interface MessageState {
totalUnreadCount: number;
systemUnreadCount: number;
// 最后一条系统通知时间
lastSystemMessageAt: string | null;
// 连接状态
isWSConnected: boolean;
@@ -75,6 +78,7 @@ export interface MessageActions {
addMessage: (conversationId: string, message: MessageResponse) => void;
updateMessage: (conversationId: string, messageId: string, updates: Partial<MessageResponse>) => void;
setUnreadCount: (total: number, system: number) => void;
setLastSystemMessageAt: (time: string | null) => void;
setSSEConnected: (connected: boolean) => void;
setCurrentConversation: (conversationId: string | null) => void;
setLoading: (loading: boolean) => void;
@@ -95,6 +99,7 @@ const initialState: MessageState = {
messagesMap: new Map(),
totalUnreadCount: 0,
systemUnreadCount: 0,
lastSystemMessageAt: null,
isWSConnected: false,
currentConversationId: null,
isLoadingConversations: false,
@@ -160,6 +165,7 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
messagesMap: state.messagesMap,
totalUnreadCount: state.totalUnreadCount,
systemUnreadCount: state.systemUnreadCount,
lastSystemMessageAt: state.lastSystemMessageAt,
isWSConnected: state.isWSConnected,
currentConversationId: state.currentConversationId,
isLoadingConversations: state.isLoadingConversations,
@@ -314,6 +320,10 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
set({ totalUnreadCount: total, systemUnreadCount: system });
},
setLastSystemMessageAt: (time: string | null) => {
set({ lastSystemMessageAt: time });
},
setSSEConnected: (connected: boolean) => {
set({ isWSConnected: connected });
},