diff --git a/src/api/socketio.ts b/src/api/socketio.ts index ccd2a611..bcc4af06 100644 --- a/src/api/socketio.ts +++ b/src/api/socketio.ts @@ -111,7 +111,7 @@ export class SocketIOAPI { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject('timeout'); - }, 5000); + }, 15000); }); const waitPromise = new Promise((resolve, reject) => { this.socket.emit(event, ...args, (err:any, ...data:any[]) => { @@ -144,7 +144,7 @@ export class SocketIOAPI { console.log('SocketIOAPI: connectionRejected.', err.message); }); this.socket.on('error', (err:any) => { - throw new Error(err); + console.error('SocketIOAPI error:', err); }); if (this.scheme==='v2') { @@ -230,7 +230,10 @@ export class SocketIOAPI { this.socket.on('connectionAccepted', (_:any, publicId:any) => { handler(publicId); }); - EventBus.on('socketioConnectedEvent', (arg:{publicId:string}) => { + if (this._busConnAcceptedDisposable) { + this._busConnAcceptedDisposable.dispose(); + } + this._busConnAcceptedDisposable = EventBus.on('socketioConnectedEvent', (arg:{publicId:string}) => { handler(arg.publicId); }); break; @@ -292,7 +295,7 @@ export class SocketIOAPI { const timeoutPromise: Promise = new Promise((_, reject) => { setTimeout(() => { reject('timeout'); - }, 5000); + }, 15000); }); switch(this.scheme) { @@ -305,7 +308,7 @@ export class SocketIOAPI { return project; }); const rejectPromise = new Promise((_, reject) => { - this.socket.on('connectionRejected', (err:any) => { + this.socket.once('connectionRejected', (err:any) => { this.scheme = 'v2'; reject(err.message); }); diff --git a/src/collaboration/clientManager.ts b/src/collaboration/clientManager.ts index c108ca96..1a7a174f 100644 --- a/src/collaboration/clientManager.ts +++ b/src/collaboration/clientManager.ts @@ -58,15 +58,16 @@ export class ClientManager { private readonly status: vscode.StatusBarItem; private readonly onlineUsers: {[K:string]:ExtendedUpdateUserSchema} = {}; private connectedFlag: boolean = true; + private _handlers: any; private readonly chatViewer: ChatViewProvider; constructor( private readonly vfs: VirtualFileSystem, private readonly context: vscode.ExtensionContext, - private readonly publicId: string, - private readonly socket: SocketIOAPI, + private publicId: string, + private socket: SocketIOAPI, ) { - this.socket.updateEventHandlers({ + this._handlers = { onClientUpdated: (user:UpdateUserSchema) => { if (user.id !== this.publicId) { this.setStatusActive(user.id); } this.updatePosition(user.id, user.doc_id, user.row, user.column, user); @@ -80,7 +81,8 @@ export class ClientManager { onConnectionAccepted: (publicId:string) => { this.connectedFlag = true; } - }); + }; + this.socket.updateEventHandlers(this._handlers); this.socket.getConnectedUsers().then(users => { users.forEach(user => { const onlineUser = { @@ -105,6 +107,14 @@ export class ClientManager { this.updateStatus(); } + reconnect() { + this.socket = this.vfs.socket; + this.publicId = this.vfs.publicId || ''; + this.socket.updateEventHandlers(this._handlers); + this.connectedFlag = true; + this.onlineUsers = {}; + } + private async jumpToUser(id?: string) { if (id === undefined) { const onlineUsers = Object.values(this.onlineUsers); @@ -248,7 +258,7 @@ export class ClientManager { this.status.tooltip = `${ELEGANT_NAME}: ${vscode.l10n.t('Not connected')}`; // Kick out all users indication since the connection is lost Object.keys(this.onlineUsers).forEach(clientId => { - this.removePosition(clientId); + try { this.removePosition(clientId); } catch (e) { /* vfs may be reconnecting */ } }); break; case true: diff --git a/src/core/remoteFileSystemProvider.ts b/src/core/remoteFileSystemProvider.ts index 7c0a8eec..18e3c0b8 100644 --- a/src/core/remoteFileSystemProvider.ts +++ b/src/core/remoteFileSystemProvider.ts @@ -204,23 +204,17 @@ export class VirtualFileSystem extends vscode.Disposable { this.root = project; const activeCondition = (vscode.workspace.workspaceFolders===undefined) || (vscode.workspace.workspaceFolders?.[0].uri.scheme!==ROOT_NAME) || (vscode.workspace.workspaceFolders?.[0].uri===this.origin); // Register: [collaboration] ClientManager on Statusbar - if (activeCondition) { - if (this.clientManagerItem?.triggers) { - this.clientManagerItem.triggers.forEach((trigger) => trigger.dispose()); - delete this.clientManagerItem; - } + if (activeCondition && !this.clientManagerItem) { const clientManager = new ClientManager(this, this.context, this.publicId||'', this.socket); this.clientManagerItem = { manager: clientManager, triggers: clientManager.triggers, }; + } else if (activeCondition && this.clientManagerItem) { + this.clientManagerItem.manager.reconnect(); } // Register: [scm] SCMCollectionProvider in explorer - if (activeCondition) { - if (this.scmCollectionItem?.triggers) { - this.scmCollectionItem.triggers.forEach((trigger) => trigger.dispose()); - delete this.scmCollectionItem; - } + if (activeCondition && !this.scmCollectionItem) { const scmCollection = new SCMCollectionProvider(this, this.context); this.scmCollectionItem = { collection: scmCollection, @@ -282,7 +276,7 @@ export class VirtualFileSystem extends vscode.Disposable { parentFolder: FolderEntity, fileEntity: FileEntity, fileType:FileType, path:string } | undefined { if (!this.root) { - throw vscode.FileSystemError.FileNotFound(); + return undefined; } root = root || this.root.rootFolder[0]; path = path || '/';