refactor(PostCard, PostCard.legacy): remove legacy PostCard component and update exports
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 8m37s
Frontend CI / ota-android (push) Successful in 10m53s
Frontend CI / build-android-apk (push) Successful in 51m16s

- Deleted the legacy PostCard component to streamline the codebase and improve maintainability.
- Updated exports in PostCard and index files to remove references to the legacy component.
- Adjusted PostCardProps to eliminate legacy properties, ensuring only the new API is supported.
- Enhanced the PostCard component to focus on modern implementation and features.
This commit is contained in:
lafay
2026-03-24 04:23:13 +08:00
parent 82e99d24d8
commit 357c1d4995
24 changed files with 1662 additions and 2243 deletions

View File

@@ -172,22 +172,28 @@ interface SSEEnvelope {
class SSEService {
private source: EventSource | null = null;
private isConnecting = false;
private connected = false;
private reconnectAttempts = 0;
private maxReconnectAttempts = 20;
private reconnectDelay = 3000;
private reconnectTimer: NodeJS.Timeout | null = null;
private connectionWatchdogTimer: NodeJS.Timeout | null = null;
private readonly CONNECTION_IDLE_TIMEOUT_MS = 70000;
private messageHandlers: Map<WSMessageType, MessageHandler[]> = new Map();
private connectionHandlers: ConnectionHandler[] = [];
private disconnectionHandlers: ConnectionHandler[] = [];
private appStateSubscription: any = null;
private lastAppState: AppStateStatus = 'active';
private lastEventId = '';
private lastActivityAt = 0;
private shouldRun = false;
private toSSEUrl(): string {
return `${SSE_URL}?last_event_id=${encodeURIComponent(this.lastEventId)}`;
}
async connect(): Promise<boolean> {
this.shouldRun = true;
if (this.isConnecting || this.isConnected()) return true;
this.isConnecting = true;
try {
@@ -201,18 +207,31 @@ class SSEService {
headers: {
Authorization: `Bearer ${token}`,
},
// Use one reconnect strategy only (manual), avoid library auto-reconnect fighting with us.
pollingInterval: 0,
// Be explicit to prevent platform-level short idle timeout behavior.
timeout: this.CONNECTION_IDLE_TIMEOUT_MS,
});
this.source.addEventListener('open', () => {
this.isConnecting = false;
this.connected = true;
this.reconnectAttempts = 0;
this.markActivity();
this.startConnectionWatchdog();
this.connectionHandlers.forEach(h => h());
});
this.source.addEventListener('error', () => {
this.isConnecting = false;
this.disconnectionHandlers.forEach(h => h());
this.scheduleReconnect();
this.handleDisconnected();
});
this.source.addEventListener('done' as any, () => {
this.handleDisconnected();
});
this.source.addEventListener('close' as any, () => {
this.handleDisconnected();
});
const events = ['chat_message', 'message_read', 'typing', 'system_notification', 'group_notice', 'message_recall', 'heartbeat'];
@@ -229,6 +248,7 @@ class SSEService {
}
private handleIncoming(eventName: string, evt: any): void {
this.markActivity();
const rawData = typeof evt?.data === 'string' ? evt.data : '{}';
const lastEventId = evt?.lastEventId;
if (lastEventId) {
@@ -375,10 +395,14 @@ class SSEService {
}
disconnect(): void {
this.shouldRun = false;
this.connected = false;
this.isConnecting = false;
if (this.source) {
this.source.close();
this.source = null;
}
this.stopConnectionWatchdog();
this.stopReconnect();
}
@@ -399,7 +423,7 @@ class SSEService {
}
isConnected(): boolean {
return this.source != null;
return this.connected;
}
on<T extends WSMessageType>(type: T, handler: MessageHandler<Extract<WSMessage, { type: T }>>): () => void {
@@ -453,6 +477,45 @@ class SSEService {
}
this.disconnect();
}
private markActivity(): void {
this.lastActivityAt = Date.now();
}
private startConnectionWatchdog(): void {
this.stopConnectionWatchdog();
this.connectionWatchdogTimer = setInterval(() => {
// During active app usage, if no SSE activity for too long, reconnect proactively.
if (!this.connected || this.lastActivityAt <= 0) return;
if (Date.now() - this.lastActivityAt > this.CONNECTION_IDLE_TIMEOUT_MS) {
this.handleDisconnected();
}
}, 10000);
}
private stopConnectionWatchdog(): void {
if (this.connectionWatchdogTimer) {
clearInterval(this.connectionWatchdogTimer);
this.connectionWatchdogTimer = null;
}
}
private handleDisconnected(): void {
const wasActive = this.connected || this.source != null || this.isConnecting;
this.isConnecting = false;
this.connected = false;
if (this.source) {
this.source.close();
this.source = null;
}
this.stopConnectionWatchdog();
if (wasActive) {
this.disconnectionHandlers.forEach(h => h());
}
if (this.shouldRun) {
this.scheduleReconnect();
}
}
}
export const sseService = new SSEService();