refactor(PostCard, PostCard.legacy): remove legacy PostCard component and update exports
- 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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user