Adds modal to indicate if the server connection is dropped in the quiz

parent e81f04aa
......@@ -71,6 +71,7 @@
"marked": "git+https://github.com/trayhem/marked.git",
"messageformat": "^2.0.5",
"ngx-qrcode2": "^0.1.0",
"ngx-toastr": "^9.1.2",
"ngx-translate-messageformat-compiler": "^4.4.0",
"rxjs": "^6.3.3",
"ts-loader": "^5.3.3",
......
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
@import "~intro.js/introjs.css";
@import "~intro.js/themes/introjs-nassim.css";
@import '~ngx-toastr/toastr-bs4-alert';
......@@ -4,16 +4,17 @@ import { AddModeComponent } from './add-mode/add-mode.component';
import { AddUserComponent } from './add-user/add-user.component';
import { AvailableQuizzesComponent } from './available-quizzes/available-quizzes.component';
import { QuizSaveComponent } from './quiz-save/quiz-save.component';
import { ServerUnavailableModalComponent } from './server-unavailable-modal/server-unavailable-modal.component';
@NgModule({
imports: [
SharedModule,
],
declarations: [
AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent,
AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent, ServerUnavailableModalComponent,
],
entryComponents: [AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent],
exports: [AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent],
entryComponents: [AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent, ServerUnavailableModalComponent],
exports: [AvailableQuizzesComponent, AddModeComponent, AddUserComponent, QuizSaveComponent, ServerUnavailableModalComponent],
})
export class ModalsModule {
}
<div class="modal-header">
<h5 class="modal-title">{{'component.server-unavailable-modal.title' | translate}}</h5>
</div>
<div class="modal-body">
<p>{{'component.server-unavailable-modal.connection-dropped' | translate}}</p>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary"
(click)="reloadPage()">{{'component.server-unavailable-modal.reload-page' | translate}}
</button>
</div>
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ServerUnavailableModalComponent } from './server-unavailable-modal.component';
describe('ServerUnavailableModalComponent', () => {
let component: ServerUnavailableModalComponent;
let fixture: ComponentFixture<ServerUnavailableModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ServerUnavailableModalComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ServerUnavailableModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
@Component({
selector: 'app-server-unavailable-modal',
templateUrl: './server-unavailable-modal.component.html',
styleUrls: ['./server-unavailable-modal.component.scss'],
})
export class ServerUnavailableModalComponent {
constructor() { }
public reloadPage(): void {
location.reload(true);
}
}
import { Component, Inject, PLATFORM_ID } from '@angular/core';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { AutoUnsubscribe } from '../../../../lib/AutoUnsubscribe';
import { StorageKey } from '../../../../lib/enums/enums';
import { MessageProtocol } from '../../../../lib/enums/Message';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { MemberApiService } from '../../../service/api/member/member-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../service/connection/connection.service';
......@@ -39,6 +41,7 @@ export class ConfidenceRateComponent {
private headerLabelService: HeaderLabelService,
private footerBarService: FooterBarService,
private memberApiService: MemberApiService,
private ngbModal: NgbModal,
) {
headerLabelService.headerLabel = 'component.liveResults.confidence_grade';
......@@ -52,6 +55,13 @@ export class ConfidenceRateComponent {
this.initData();
}));
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public initData(): void {
......
......@@ -2,6 +2,7 @@ import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnDestroy, PLATFORM_ID, SecurityContext } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ILeaderBoardItem } from 'arsnova-click-v2-types/dist/common';
import { Subscription } from 'rxjs';
import { AutoUnsubscribe } from '../../../../lib/AutoUnsubscribe';
......@@ -10,6 +11,7 @@ import { MessageProtocol } from '../../../../lib/enums/Message';
import { QuestionType } from '../../../../lib/enums/QuestionType';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { parseGithubFlavoredMarkdown } from '../../../../lib/markdown/markdown';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { LeaderboardApiService } from '../../../service/api/leaderboard/leaderboard-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../service/connection/connection.service';
......@@ -62,18 +64,18 @@ export class LeaderboardComponent implements OnDestroy {
// noinspection JSMismatchedCollectionQueryUpdate
private readonly _subscriptions: Array<Subscription> = [];
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
private sanitizer: DomSanitizer,
private footerBarService: FooterBarService,
private route: ActivatedRoute,
private headerLabelService: HeaderLabelService,
private router: Router,
private connectionService: ConnectionService,
public attendeeService: AttendeeService,
private i18nService: I18nService,
private leaderboardApiService: LeaderboardApiService,
public quizService: QuizService,
constructor(@Inject(PLATFORM_ID) private platformId: Object,
public attendeeService: AttendeeService,
public quizService: QuizService,
private sanitizer: DomSanitizer,
private footerBarService: FooterBarService,
private route: ActivatedRoute,
private headerLabelService: HeaderLabelService,
private router: Router,
private connectionService: ConnectionService,
private i18nService: I18nService,
private leaderboardApiService: LeaderboardApiService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = LeaderboardComponent.TYPE;
......@@ -89,6 +91,13 @@ export class LeaderboardComponent implements OnDestroy {
this.attendeeService.restoreMembers();
this.addFooterElements();
}));
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public ngOnDestroy(): void {
......
......@@ -11,6 +11,7 @@ import { MessageProtocol } from '../../../../lib/enums/Message';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { IMemberSerialized } from '../../../../lib/interfaces/entities/Member/IMemberSerialized';
import { parseGithubFlavoredMarkdown } from '../../../../lib/markdown/markdown';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { MemberApiService } from '../../../service/api/member/member-api.service';
import { QuizApiService } from '../../../service/api/quiz/quiz-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
......@@ -75,6 +76,7 @@ export class QuizLobbyComponent implements OnDestroy {
private trackingService: TrackingService,
private memberApiService: MemberApiService,
private quizApiService: QuizApiService,
private ngbModal: NgbModal,
) {
console.log('lobby quiz initializing');
......@@ -95,6 +97,13 @@ export class QuizLobbyComponent implements OnDestroy {
this.attendeeService.restoreMembers();
}
}));
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
this.footerBarService.TYPE_REFERENCE = QuizLobbyComponent.TYPE;
}
......
import { Component, OnDestroy, OnInit } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { AutoUnsubscribe } from '../../../../../lib/AutoUnsubscribe';
import { AbstractQuestionEntity } from '../../../../../lib/entities/question/AbstractQuestionEntity';
import { MessageProtocol, StatusProtocol } from '../../../../../lib/enums/Message';
import { IMessage } from '../../../../../lib/interfaces/communication/IMessage';
import { IMemberSerialized } from '../../../../../lib/interfaces/entities/Member/IMemberSerialized';
import { ServerUnavailableModalComponent } from '../../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { AttendeeService } from '../../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../../service/connection/connection.service';
import { FooterBarService } from '../../../../service/footer-bar/footer-bar.service';
......@@ -61,12 +63,20 @@ export class QuestionDetailsComponent implements OnInit, OnDestroy {
private attendeeService: AttendeeService,
private connectionService: ConnectionService,
private footerBarService: FooterBarService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = QuestionDetailsComponent.TYPE;
footerBarService.replaceFooterElements([
this.footerBarService.footerElemBack, this.footerBarService.footerElemFullscreen,
]);
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public sanitizeHTML(value: string): SafeHtml {
......@@ -78,8 +88,10 @@ export class QuestionDetailsComponent implements OnInit, OnDestroy {
}
public async ngOnInit(): Promise<void> {
await this.connectionService.initConnection();
this.handleMessages();
this.connectionService.initConnection().then(() => {
this.connectionService.connectToChannel(this.quizService.quiz.name);
this.handleMessages();
});
this._subscriptions.push(this.questionTextService.eventEmitter.subscribe((value: string | Array<string>) => {
if (Array.isArray(value)) {
......
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { AutoUnsubscribe } from '../../../../lib/AutoUnsubscribe';
import { Countdown } from '../../../../lib/countdown/countdown';
......@@ -10,6 +11,7 @@ import { QuestionType } from '../../../../lib/enums/QuestionType';
import { QuizState } from '../../../../lib/enums/QuizState';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { IMemberSerialized } from '../../../../lib/interfaces/entities/Member/IMemberSerialized';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { QuizApiService } from '../../../service/api/quiz/quiz-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../service/connection/connection.service';
......@@ -49,6 +51,7 @@ export class QuizResultsComponent implements OnInit, OnDestroy {
private footerBarService: FooterBarService,
private questionTextService: QuestionTextService,
private quizApiService: QuizApiService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = QuizResultsComponent.TYPE;
......@@ -67,6 +70,13 @@ export class QuizResultsComponent implements OnInit, OnDestroy {
});
this.addFooterElements();
}));
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public showLeaderBoardButton(index: number): boolean {
......
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { Component, Inject, OnDestroy, PLATFORM_ID } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { ConnectionService } from '../../../service/connection/connection.service';
import { FooterBarService } from '../../../service/footer-bar/footer-bar.service';
import { QuizService } from '../../../service/quiz/quiz.service';
import { ThemesService } from '../../../service/themes/themes.service';
......@@ -19,6 +22,8 @@ export class QuizThemeComponent implements OnDestroy {
private footerBarService: FooterBarService,
private quizService: QuizService,
private themesService: ThemesService,
private connectionService: ConnectionService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = QuizThemeComponent.TYPE;
......@@ -27,6 +32,14 @@ export class QuizThemeComponent implements OnDestroy {
]);
if (isPlatformBrowser(this.platformId)) {
this.previewThemeBackup = document.getElementsByTagName('html').item(0).dataset['theme'];
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
}
......
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { MessageProtocol } from '../../../../lib/enums/Message';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { IMemberSerialized } from '../../../../lib/interfaces/entities/Member/IMemberSerialized';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { MemberApiService } from '../../../service/api/member/member-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
import { ConnectionService } from '../../../service/connection/connection.service';
......@@ -36,12 +38,21 @@ export class ReadingConfirmationComponent implements OnInit, OnDestroy {
private headerLabelService: HeaderLabelService,
private footerBarService: FooterBarService,
private memberApiService: MemberApiService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = ReadingConfirmationComponent.TYPE;
headerLabelService.headerLabel = 'component.liveResults.reading_confirmation';
this.questionIndex = quizService.quiz.currentQuestionIndex;
this.footerBarService.replaceFooterElements([]);
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public sanitizeHTML(value: string): SafeHtml {
......
......@@ -2,6 +2,7 @@ import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnDestroy, PLATFORM_ID } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { AutoUnsubscribe } from '../../../../lib/AutoUnsubscribe';
import { Countdown } from '../../../../lib/countdown/countdown';
......@@ -10,6 +11,7 @@ import { StorageKey } from '../../../../lib/enums/enums';
import { MessageProtocol, StatusProtocol } from '../../../../lib/enums/Message';
import { QuestionType } from '../../../../lib/enums/QuestionType';
import { IMessage } from '../../../../lib/interfaces/communication/IMessage';
import { ServerUnavailableModalComponent } from '../../../modals/server-unavailable-modal/server-unavailable-modal.component';
import { MemberApiService } from '../../../service/api/member/member-api.service';
import { QuizApiService } from '../../../service/api/quiz/quiz-api.service';
import { AttendeeService } from '../../../service/attendee/attendee.service';
......@@ -71,6 +73,7 @@ export class VotingComponent implements OnDestroy {
private router: Router,
private quizApiService: QuizApiService,
private memberApiService: MemberApiService,
private ngbModal: NgbModal,
) {
this.footerBarService.TYPE_REFERENCE = VotingComponent.TYPE;
......@@ -88,6 +91,13 @@ export class VotingComponent implements OnDestroy {
this.initData();
this.attendeeService.restoreMembers();
}));
this._subscriptions.push(this.connectionService.serverStatusEmitter.subscribe(isConnected => {
if (isConnected) {
return;
}
this.ngbModal.open(ServerUnavailableModalComponent);
}));
}
public sanitizeHTML(value: string): SafeHtml {
......
import { HttpClient } from '@angular/common/http';
import { NgModule, PLATFORM_ID } from '@angular/core';
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule, Routes } from '@angular/router';
import { ServiceWorkerModule } from '@angular/service-worker';
import { JWT_OPTIONS, JwtModule } from '@auth0/angular-jwt';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateCompiler, TranslateLoader, TranslateModule, TranslatePipe } from '@ngx-translate/core';
import { Angulartics2Module } from 'angulartics2';
import { ToastrModule } from 'ngx-toastr';
import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler';
import { environment } from '../environments/environment';
import { jwtOptionsFactory } from '../lib/jwt.factory';
......@@ -100,6 +102,8 @@ export const appRoutes: Routes = [
],
imports: [
BrowserModule.withServerTransition({ appId: 'frontend' }),
BrowserAnimationsModule,
ToastrModule.forRoot(),
BrowserTransferStateModule,
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }),
ModalsModule,
......
......@@ -420,7 +420,9 @@ export class HomeComponent implements OnInit, OnDestroy {
const currentQuiz = new QuizEntity(await this.storageService.read(DbTable.Quiz, quizname).toPromise());
this.canAddQuiz = false;
this.canEditQuiz = true;
this.canStartQuiz = !this.settingsService.serverSettings.createQuizPasswordRequired && currentQuiz.isValid();
this.canStartQuiz = this.connectionService.serverAvailable && //
(this.settingsService.serverSettings && !this.settingsService.serverSettings.createQuizPasswordRequired) && //
currentQuiz.isValid();
this.passwordRequired = this.canStartQuiz && this.settingsService.serverSettings.createQuizPasswordRequired;
}
......@@ -450,7 +452,7 @@ export class HomeComponent implements OnInit, OnDestroy {
this.canStartQuiz = false;
} else if (value.step === MessageProtocol.Available && value.payload.state === QuizState.Active) {
this.canAddQuiz = false;
this.canJoinQuiz = true;
this.canJoinQuiz = this.connectionService.serverAvailable;
this.passwordRequired = false;
this.canStartQuiz = false;
this._provideNickSelection = value.payload.provideNickSelection;
......
......@@ -3,7 +3,7 @@ import { AfterViewInit, Component, Inject, OnInit, PLATFORM_ID } from '@angular/
import { ActivatedRoute, NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart, Router } from '@angular/router';
import { SwUpdate } from '@angular/service-worker';
import { TranslateService } from '@ngx-translate/core';
import { DeprecatedDb, DeprecatedKeys, StorageKey } from '../../../lib/enums/enums';
import { DeprecatedDb, DeprecatedKeys } from '../../../lib/enums/enums';
import { INamedType } from '../../../lib/interfaces/interfaces';
// tslint:disable-next-line:max-line-length
import { QuizManagerDetailsOverviewComponent } from '../../quiz/quiz-manager/quiz-manager-details/quiz-manager-details-overview/quiz-manager-details-overview.component';
......@@ -12,7 +12,6 @@ import { ConnectionService } from '../../service/connection/connection.service';
import { FooterBarService } from '../../service/footer-bar/footer-bar.service';
import { HeaderLabelService } from '../../service/header-label/header-label.service';
import { I18nService } from '../../service/i18n/i18n.service';
import { NotificationService } from '../../service/notification/notification.service';
import { StorageService } from '../../service/storage/storage.service';
import { ThemesService } from '../../service/themes/themes.service';
import { TrackingService } from '../../service/tracking/tracking.service';
......@@ -63,7 +62,6 @@ export class RootComponent implements OnInit, AfterViewInit {
private storageService: StorageService,
private userService: UserService,
private swUpdate: SwUpdate,
private notificationService: NotificationService,
) {}
public ngOnInit(): void {
......@@ -73,34 +71,8 @@ export class RootComponent implements OnInit, AfterViewInit {
sessionStorage.removeItem(deprecatedKey);
});
Object.values(DeprecatedDb).forEach(deprecatedDb => {
indexedDB.deleteDatabase(deprecatedDb).addEventListener('success', result => {});
indexedDB.deleteDatabase(deprecatedDb).addEventListener('success', () => {});
});
/* Reload the page if the fetch of production chunks failed
* https://stackoverflow.com/a/49805926
*/
// Keep the original error handler
const oldHandler = this.router.errorHandler;
// Replace route error handler
this.router.errorHandler = (err: any) => {
// Check if there is an error loading the chunk
console.error('error while loading route', err);
if (err.originalStack && err.originalStack.indexOf('Error: Loading chunk') >= 0) {
// Check if is the first time the error happend
console.error('loading chunk failed');
if (sessionStorage.getItem(StorageKey.ChunkError) !== err.originalStack) {
// Save the last error to avoid an infinite reload loop if the chunk really does not exists after reload
sessionStorage.setItem(StorageKey.ChunkError, err.originalStack);
console.error('no previous reload found, so forcing reload now');
location.reload(true);
} else {
// The chunk really does not exists after reload
console.error('We really don\'t find the chunk...');
}
}
// Run original handler
oldHandler(err);
};
}
this.userService.loadConfig();
......
......@@ -38,6 +38,12 @@ export class ConnectionService {
this._websocketAvailable = value;
}
private _serverStatusEmitter = new EventEmitter<boolean>();
get serverStatusEmitter(): EventEmitter<boolean> {
return this._serverStatusEmitter;
}
private _rtt = 0;
get rtt(): number {
......@@ -67,7 +73,7 @@ export class ConnectionService {
}
private _connectedChannel: string;
private lastTimeout = 500;
private lastTimeout = 1;
private _isWebSocketAuthorized = false;
constructor(
......@@ -214,6 +220,7 @@ export class ConnectionService {
this._socket.onopen = () => {
this._websocketAvailable = true;
this._serverAvailable = true;
this._serverStatusEmitter.emit(true);
this.toggleFooterElemState(true);
if (this._connectedChannel) {
const name = this._connectedChannel;
......@@ -235,13 +242,14 @@ export class ConnectionService {
this._socket.onclose = () => {
this._websocketAvailable = false;
this._serverAvailable = false;
this._serverStatusEmitter.emit(false);
this.toggleFooterElemState(false);
const timeout = this.lastTimeout * 2;
const timeout = this.lastTimeout >= 30 ? 30 : Math.round((this.lastTimeout * 2));
this.lastTimeout = timeout;
console.log(`Socket connection dropped, waiting ${timeout}ms for reconnect`);
console.log(`Socket connection dropped, waiting ${timeout}s for reconnect`);
setTimeout(() => {
this.initWebsocket();
}, timeout);
}, timeout * 1000);
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment