From cfb536cd0156f61cfc226199cef0bb4794eaf41b Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Fri, 27 Feb 2026 04:34:28 +0700 Subject: [PATCH] BUGFIX: Fix issues with RFA auto-reconnecting feature (#2535) --- src/@types/global.d.ts | 4 ++++ src/RemoteFileAPI/Remote.ts | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 6f238414b..b68b0f01c 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -21,6 +21,10 @@ declare interface Document { achievements: string[]; } +declare interface WebSocket { + intentionallyClosed?: boolean; +} + declare global { /** * "loader" is not exposed in the public API. diff --git a/src/RemoteFileAPI/Remote.ts b/src/RemoteFileAPI/Remote.ts index ea6ec9696..0988cba5f 100644 --- a/src/RemoteFileAPI/Remote.ts +++ b/src/RemoteFileAPI/Remote.ts @@ -6,12 +6,12 @@ import { Settings } from "../Settings/Settings"; import { EventEmitter } from "../utils/EventEmitter"; import type { getRemoteFileApiConnectionStatus } from "./RemoteFileAPI"; +const timeOutIds = new Set(); + function showErrorMessage(address: string, detail: string) { SnackbarEvents.emit(`Error with websocket ${address}, details: ${detail}`, ToastVariant.ERROR, 5000); } -const eventCodeWhenIntentionallyStoppingConnection = 3000; - export const RemoteFileApiConnectionEvents = new EventEmitter<[ReturnType]>(); export class Remote { @@ -26,7 +26,15 @@ export class Remote { } public stopConnection(): void { - this.connection?.close(eventCodeWhenIntentionallyStoppingConnection); + // Cancel all pending retries immediately. This function is only called when we intentionally close the current + // connection before starting a new one. The new connection will retry on its own if needed. + timeOutIds.forEach((id) => window.clearTimeout(id)); + timeOutIds.clear(); + + if (this.connection) { + this.connection.intentionallyClosed = true; + } + this.connection?.close(); RemoteFileApiConnectionEvents.emit("Offline"); } @@ -69,7 +77,7 @@ export class Remote { * unexpectedly (e.g., show a warning, reconnect after a delay), so we need to check whether the close event is * unexpected. */ - if (event.code === eventCodeWhenIntentionallyStoppingConnection) { + if (event.currentTarget instanceof WebSocket && event.currentTarget.intentionallyClosed) { return; } @@ -83,7 +91,8 @@ export class Remote { if (Settings.RemoteFileApiReconnectionDelay > 0) { this.reconnecting = true; - setTimeout(() => { + const timeOutId = window.setTimeout(() => { + timeOutIds.delete(timeOutId); if (autoConnectAttempt === 1) { SnackbarEvents.emit(`Attempting to auto connect Remote API`, ToastVariant.WARNING, 2000); } @@ -93,6 +102,7 @@ export class Remote { this.startConnection(attempts); }, Settings.RemoteFileApiReconnectionDelay * 1000); + timeOutIds.add(timeOutId); RemoteFileApiConnectionEvents.emit("Reconnecting"); } else { this.reconnecting = false;