From e5cad1c32a216c7a89b9b00f6005e649adbe2a4d Mon Sep 17 00:00:00 2001 From: Martin Raymond Date: Thu, 4 Apr 2024 16:47:47 -0700 Subject: [PATCH] settings --- .../src/app.component.ts | 30 ++++++- .../src/app.module.ts | 17 +++- .../src/core/settings/index.ts | 2 + .../src/core/settings/settings.actions.ts | 23 +++++ .../src/core/settings/settings.effects.ts | 87 +++++++++++++++++++ .../src/core/settings/settings.model.ts | 5 ++ .../src/core/settings/settings.reducer.ts | 44 ++++++++++ .../src/core/settings/settings.selectors.ts | 19 ++++ .../src/core/settings/settings.service.ts | 58 +++++++++++++ .../src/features/home/home.component.ts | 21 +---- .../features/settings/settings.component.html | 67 ++++++++------ .../features/settings/settings.component.scss | 23 +++-- .../features/settings/settings.component.ts | 55 +++++++++--- .../src/services/api.service.ts | 5 ++ .../src/services/score-keeper.store.ts | 45 +++------- .../src/services/socket.service.ts | 45 +++++++++- 16 files changed, 442 insertions(+), 104 deletions(-) create mode 100644 apps/score-keeper-nativescript/src/core/settings/index.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.actions.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.effects.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.model.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.reducer.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.selectors.ts create mode 100644 apps/score-keeper-nativescript/src/core/settings/settings.service.ts diff --git a/apps/score-keeper-nativescript/src/app.component.ts b/apps/score-keeper-nativescript/src/app.component.ts index 10a3845..29d724f 100644 --- a/apps/score-keeper-nativescript/src/app.component.ts +++ b/apps/score-keeper-nativescript/src/app.component.ts @@ -1,8 +1,34 @@ -import { Component } from '@angular/core'; +import { Component, NgZone } from '@angular/core'; +import { SocketService } from './services/socket.service'; +import { ScoreKeeperStore } from './services/score-keeper.store'; +import { IGame, OverlayState } from '@pool-overlay/models'; @Component({ moduleId: module.id, selector: 'app-root', templateUrl: './app.component.html', }) -export class AppComponent { } +export class AppComponent { + constructor( + private _store: ScoreKeeperStore, + private ngZone: NgZone, + private _socketService: SocketService, + ) { + this._socketService.bind('GAME_EVENT', this.updateGame.bind(this)); + this._socketService.bind('OVERLAY_STATE_EVENT', this.updateOverlay.bind(this)); + this._socketService.connect(); + this._store.getGame(); + } + + public updateGame({ game }: { game: IGame }) { + this.ngZone.run(() => { + this._store.updateGame(game); + }); + } + + public updateOverlay(res: OverlayState): void { + this.ngZone.run(() => { + this._store.updateOverlay(res); + }); + } +} diff --git a/apps/score-keeper-nativescript/src/app.module.ts b/apps/score-keeper-nativescript/src/app.module.ts index 824e6f6..29b94af 100644 --- a/apps/score-keeper-nativescript/src/app.module.ts +++ b/apps/score-keeper-nativescript/src/app.module.ts @@ -1,11 +1,26 @@ import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { StoreModule } from '@ngrx/store'; +import { EffectsModule } from '@ngrx/effects'; + import { CoreModule } from './core/core.module'; import { SharedModule } from './features/shared/shared.module'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { settingsReducer, SettingsStateKey } from './core/settings/settings.reducer'; +import { SettingsEffects } from './core/settings/settings.effects'; @NgModule({ - imports: [CoreModule, SharedModule, AppRoutingModule], + imports: [ + StoreModule.forRoot({ + [SettingsStateKey]: settingsReducer, + }), + EffectsModule.forRoot([ + SettingsEffects, + ]), + CoreModule, + SharedModule, + AppRoutingModule + ], declarations: [AppComponent], bootstrap: [AppComponent], schemas: [NO_ERRORS_SCHEMA], diff --git a/apps/score-keeper-nativescript/src/core/settings/index.ts b/apps/score-keeper-nativescript/src/core/settings/index.ts new file mode 100644 index 0000000..4c7128e --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/index.ts @@ -0,0 +1,2 @@ +export * as SettingsSelectors from './settings.selectors'; +export { SettingsActions } from './settings.actions'; diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.actions.ts b/apps/score-keeper-nativescript/src/core/settings/settings.actions.ts new file mode 100644 index 0000000..94bf24a --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.actions.ts @@ -0,0 +1,23 @@ +import { createActionGroup, emptyProps, props } from '@ngrx/store'; +import { AppSettings } from './settings.model'; + +export const SettingsActions = createActionGroup({ + source: 'Settings', + events: { + 'Load Settings': emptyProps(), + 'Load Settings Success': props<{ settings: AppSettings }>(), + + 'Save Settings': props<{ settings: AppSettings }>(), + 'Save Settings Success': props<{ settings: AppSettings }>(), + + 'Set Table': props<{ table: number; }>(), + 'Set Table Success': props<{ table: number; }>(), + + 'Set Server': props<{ server: string; }>(), + 'Set Server Success': props<{ server: string; }>(), + + 'Get Max Tables': emptyProps(), + 'Get Max Tables Success': props<{ maxTables: number; }>(), + 'Get Max Tables Error': props<{ error: string; }>(), + }, +}); diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.effects.ts b/apps/score-keeper-nativescript/src/core/settings/settings.effects.ts new file mode 100644 index 0000000..1f63a83 --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.effects.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@angular/core'; +import { Actions, ROOT_EFFECTS_INIT, createEffect, ofType } from '@ngrx/effects'; +import { map, switchMap, tap, catchError, of } from 'rxjs'; + +import { SettingsKeys, SettingsService } from './settings.service'; +import { SettingsActions } from './settings.actions'; +import { APIService } from '../../services/api.service'; + +@Injectable() +export class SettingsEffects { + public init$ = createEffect(() => this.actions$.pipe( + ofType(ROOT_EFFECTS_INIT), + map(() => SettingsActions.loadSettings()), + )); + + public loadSettings$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.loadSettings), + map(() => { + const settings = this.settingsService.load(); + return SettingsActions.loadSettingsSuccess({ settings }); + }), + )); + + public saveSettings$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.saveSettings), + map(({ settings }) => { + return SettingsActions.saveSettingsSuccess({ settings }); + }), + )); + + public saveSettingsSuccess$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.saveSettingsSuccess), + tap(({ settings }) => { + this.settingsService.saveAll(settings); + }), + ), { dispatch: false }); + + public setTable$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.setTable), + map(({ table }) => SettingsActions.setTableSuccess({ table })), + )); + + public setTableSuccess$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.setTableSuccess), + tap(({ table }) => { + this.settingsService.save({ key: SettingsKeys.TABLE, value: table }); + }), + ), { dispatch: false }); + + public setServer$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.setServer), + map(({ server }) => SettingsActions.setServerSuccess({ server })), + )); + + public setServerSuccess$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.setServerSuccess), + tap(({ server }) => { + this.settingsService.save({ key: SettingsKeys.SERVER, value: server }); + }), + ), { dispatch: false }); + + public getMaxTables$ = createEffect(() => this.actions$.pipe( + ofType(ROOT_EFFECTS_INIT, SettingsActions.getMaxTables), + switchMap(() => this.apiService.getMaxTables().pipe( + map(({ count }) => SettingsActions.getMaxTablesSuccess({ maxTables: count })), + catchError((err) => of(SettingsActions.getMaxTablesError({ error: err.message }))), + )), + )); + + public getMaxTablesSuccess$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.getMaxTablesSuccess), + tap(({ maxTables }) => { + this.settingsService.save({ key: SettingsKeys.MAX_TABLES, value: maxTables }); + }), + ), { dispatch: false }); + + public getMaxTablesError$ = createEffect(() => this.actions$.pipe( + ofType(SettingsActions.getMaxTablesError), + tap((err) => console.log(err)), + ), { dispatch: false }); + + constructor( + private actions$: Actions, + private settingsService: SettingsService, + private apiService: APIService, + ) { } +} diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.model.ts b/apps/score-keeper-nativescript/src/core/settings/settings.model.ts new file mode 100644 index 0000000..d4857bf --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.model.ts @@ -0,0 +1,5 @@ +export interface AppSettings { + server: string; + table: number; + maxTables: number; +} diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.reducer.ts b/apps/score-keeper-nativescript/src/core/settings/settings.reducer.ts new file mode 100644 index 0000000..206e46e --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.reducer.ts @@ -0,0 +1,44 @@ +import { createReducer, on } from '@ngrx/store'; +import { SettingsActions } from './settings.actions'; + +export const SettingsStateKey = 'settings'; + +export interface State { + server: string; + table: number; + maxTables: number; +} + +export const initialState: State = { + server: '', + table: 1, + maxTables: 2, +}; + +export const settingsReducer = createReducer( + initialState, + on(SettingsActions.loadSettingsSuccess, (state, { settings }) => ({ + ...state, + server: settings.server, + table: settings.table, + maxTables: settings.maxTables, + })), + on(SettingsActions.saveSettings, (state, { settings }) => ({ + ...state, + server: settings.server, + table: settings.table, + maxTables: settings.maxTables, + })), + on(SettingsActions.setTable, (state, { table }) => ({ + ...state, + table, + })), + on(SettingsActions.setServer, (state, { server }) => ({ + ...state, + server, + })), + on(SettingsActions.getMaxTablesSuccess, (state, { maxTables }) => ({ + ...state, + maxTables, + })), +); diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.selectors.ts b/apps/score-keeper-nativescript/src/core/settings/settings.selectors.ts new file mode 100644 index 0000000..3fe146b --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.selectors.ts @@ -0,0 +1,19 @@ +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { SettingsStateKey, State } from './settings.reducer'; + +export const selectSettingsState = createFeatureSelector(SettingsStateKey); + +export const selectServer = createSelector( + selectSettingsState, + (state) => state.server, +); + +export const selectTable = createSelector( + selectSettingsState, + (state) => state.table, +); + +export const selectMaxTables = createSelector( + selectSettingsState, + (state) => state.maxTables, +); diff --git a/apps/score-keeper-nativescript/src/core/settings/settings.service.ts b/apps/score-keeper-nativescript/src/core/settings/settings.service.ts new file mode 100644 index 0000000..05a454e --- /dev/null +++ b/apps/score-keeper-nativescript/src/core/settings/settings.service.ts @@ -0,0 +1,58 @@ +import { Injectable } from '@angular/core'; +import { ApplicationSettings } from '@nativescript/core'; +import { AppSettings } from './settings.model'; + +export const DEFAULT_SERVER = '192.168.0.26'; +export const DEFAULT_TABLE = 1; +export const DEFAULT_MAX_TABLES = 2; + +export enum SettingsKeys { + SERVER = 'server', + TABLE = 'table', + MAX_TABLES = 'maxTables', +} + +@Injectable({ providedIn: 'root' }) +export class SettingsService { + public load(): AppSettings { + const server = ApplicationSettings.getString(SettingsKeys.SERVER, DEFAULT_SERVER); + const table = ApplicationSettings.getNumber(SettingsKeys.TABLE, DEFAULT_TABLE); + const maxTables = ApplicationSettings.getNumber(SettingsKeys.MAX_TABLES, DEFAULT_MAX_TABLES); + + return { + server, + table, + maxTables, + }; + } + + public saveAll({ server, table, maxTables }: AppSettings): void { + ApplicationSettings.setString(SettingsKeys.SERVER, server); + ApplicationSettings.setNumber(SettingsKeys.TABLE, table); + ApplicationSettings.setNumber(SettingsKeys.MAX_TABLES, maxTables); + } + + public save({ key, value }: { key: SettingsKeys, value: string | number | boolean }): void { + switch (typeof value) { + case 'string': + ApplicationSettings.setString(key, value); + break; + case 'number': + ApplicationSettings.setNumber(key, value); + break; + case 'boolean': + ApplicationSettings.setBoolean(key, value); + break; + default: + throw new Error(`unable to save setting ${key} of type ${typeof value}.`); + } + } + + public remove(key: string): void { + return ApplicationSettings.remove(key); + } + + public clear(): void { + return ApplicationSettings.clear(); + } +} diff --git a/apps/score-keeper-nativescript/src/features/home/home.component.ts b/apps/score-keeper-nativescript/src/features/home/home.component.ts index 94ce7f6..3f5c44f 100644 --- a/apps/score-keeper-nativescript/src/features/home/home.component.ts +++ b/apps/score-keeper-nativescript/src/features/home/home.component.ts @@ -1,8 +1,6 @@ -import { Component, NgZone } from '@angular/core'; -import { IGame, OverlayState } from '@pool-overlay/models'; +import { Component } from '@angular/core'; import { PlayerUpdateEvent } from '@pool-overlay/score-keeper'; import { ScoreKeeperStore } from '../../services/score-keeper.store'; -import { SocketService } from '../../services/socket.service'; @Component({ moduleId: module.id, @@ -15,27 +13,10 @@ export class HomeComponent { constructor( private _store: ScoreKeeperStore, - private _socketService: SocketService, - private ngZone: NgZone, ) { - this._socketService.bind('GAME_EVENT', this.updateGame.bind(this)); - this._socketService.bind('OVERLAY_STATE_EVENT', this.updateOverlay.bind(this)); - this._socketService.connect(); this._store.getGame(); } - public updateGame({ game }: { game: IGame }) { - this.ngZone.run(() => { - this._store.updateGame(game); - }); - } - - public updateOverlay(res: OverlayState): void { - this.ngZone.run(() => { - this._store.updateOverlay(res); - }); - } - public updatePlayer(update: PlayerUpdateEvent) { this._store.updateScore(update); } diff --git a/apps/score-keeper-nativescript/src/features/settings/settings.component.html b/apps/score-keeper-nativescript/src/features/settings/settings.component.html index a0d2f59..da0ef2c 100644 --- a/apps/score-keeper-nativescript/src/features/settings/settings.component.html +++ b/apps/score-keeper-nativescript/src/features/settings/settings.component.html @@ -1,30 +1,41 @@ - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + diff --git a/apps/score-keeper-nativescript/src/features/settings/settings.component.scss b/apps/score-keeper-nativescript/src/features/settings/settings.component.scss index e4717ba..3136dcf 100644 --- a/apps/score-keeper-nativescript/src/features/settings/settings.component.scss +++ b/apps/score-keeper-nativescript/src/features/settings/settings.component.scss @@ -20,11 +20,17 @@ .option-table-number { margin-bottom: 50; - .table-button { - background-color: #394b72; + .option-tables { + flex-direction: row; + flex-wrap: wrap; - &.active { - background-color: green; + .table-button { + width: 80; + background-color: #394b72; + + &.active { + background-color: green; + } } } } @@ -32,7 +38,14 @@ .save-button { color: white; - background-color: #992222; + + &.connect { + background-color: green; + } + + &.disconnect { + background-color: #992222; + } } } diff --git a/apps/score-keeper-nativescript/src/features/settings/settings.component.ts b/apps/score-keeper-nativescript/src/features/settings/settings.component.ts index 652fd25..a7b8861 100644 --- a/apps/score-keeper-nativescript/src/features/settings/settings.component.ts +++ b/apps/score-keeper-nativescript/src/features/settings/settings.component.ts @@ -1,7 +1,10 @@ import { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { ScoreKeeperStore } from '../../services/score-keeper.store'; import { RouterExtensions } from '@nativescript/angular'; +import { Store } from '@ngrx/store'; +import { SettingsActions, SettingsSelectors } from '../../core/settings'; +import { combineLatest, map, take } from 'rxjs'; +import { SocketService } from '../../services/socket.service'; @Component({ moduleId: module.id, @@ -10,24 +13,50 @@ import { RouterExtensions } from '@nativescript/angular'; styleUrls: ['./settings.component.scss'], }) export class SettingsComponent { - public tables: number[] = [1, 2, 3]; - public table$ = this._store.table$; - public serverAddress$ = this._store.serverAddress$; - public form: FormGroup; + public table$ = this.store.select(SettingsSelectors.selectTable); + public server$ = this.store.select(SettingsSelectors.selectServer); + public maxTables$ = this.store.select(SettingsSelectors.selectMaxTables); + public vm$ = combineLatest([ + this.table$, + this.server$, + this.maxTables$, + this.socketService.isConnected$, + ]).pipe( + map(([table, server, maxTables, isConnected]) => ({ + table, + server, + maxTables, + tables: Array.from(new Array(maxTables), (_, i) => i + 1), + isConnected, + })) + ); constructor( private router: RouterExtensions, - private _store: ScoreKeeperStore, - ) { - this.form = new FormGroup({}) - } + private store: Store, + private socketService: SocketService, + ) { } - public updateTable(table: number) { - this._store.setTable(table); + public updateTable(table: number): void { + this.store.dispatch(SettingsActions.setTable({ table })); void this.router.navigateByUrl('/home'); } - public saveServerAddress(serverAddress: string) { - console.log(serverAddress); + public updateServer(server: string): void { + this.store.dispatch(SettingsActions.setServer({ server })); + } + + public toggleServerConnection(server: string): void { + this.store.dispatch(SettingsActions.setServer({ server })); + + this.socketService.isConnected$.pipe( + take(1), + ).subscribe((isConnected) => { + if (isConnected) { + this.socketService.disconnect(); + } else { + this.socketService.connect(); + } + }); } } diff --git a/apps/score-keeper-nativescript/src/services/api.service.ts b/apps/score-keeper-nativescript/src/services/api.service.ts index 4fe0013..f776c44 100644 --- a/apps/score-keeper-nativescript/src/services/api.service.ts +++ b/apps/score-keeper-nativescript/src/services/api.service.ts @@ -15,6 +15,11 @@ export class APIService { private _http: HttpClient, ) { } + public getMaxTables(): Observable<{ count: number; }> { + const url = `${this._apiURL}/${this._apiVersion}/table/count`; + return this._http.get<{ count: number; }>(url); + } + public getGame(table: number): Observable { const url = `${this._apiURL}/${this._apiVersion}/table/${table}/game`; return this._http.get(url); diff --git a/apps/score-keeper-nativescript/src/services/score-keeper.store.ts b/apps/score-keeper-nativescript/src/services/score-keeper.store.ts index 9f6de63..7f5ba88 100644 --- a/apps/score-keeper-nativescript/src/services/score-keeper.store.ts +++ b/apps/score-keeper-nativescript/src/services/score-keeper.store.ts @@ -6,15 +6,11 @@ import { Dialogs } from '@nativescript/core'; import { Direction, IGame, OverlayState } from '@pool-overlay/models'; import { APIService } from './api.service'; import { PlayerUpdateEvent } from '@pool-overlay/score-keeper'; - -export interface Settings { - table: number; - serverAddress: string; -}; +import { Store } from '@ngrx/store'; +import { SettingsSelectors } from '../core/settings'; export interface ScoreKeeperState { game: IGame | null; - settings: Settings; overlay: OverlayState; } @@ -23,14 +19,11 @@ export interface ScoreKeeperState { }) export class ScoreKeeperStore extends ComponentStore { constructor( + private store: Store, private _apiService: APIService, ) { super({ game: null, - settings: { - table: 1, - serverAddress: '192.168.0.26', - }, overlay: { table: 1, hidden: false, @@ -62,21 +55,14 @@ export class ScoreKeeperStore extends ComponentStore { public readonly setTable = this.updater((state, table) => ({ ...state, - settings: { - ...state.settings, + overlay: { + ...state.overlay, table, }, })); - public readonly setServerAddress = this.updater((state, serverAddress) => ({ - ...state, - settings: { - ...state.settings, - serverAddress, - }, - })); - // selectors + public readonly table$ = this.store.select(SettingsSelectors.selectTable); public readonly game$ = this.select((state) => state.game); public readonly playerOneRaceTo$ = this.select( this.game$, @@ -128,24 +114,21 @@ export class ScoreKeeperStore extends ComponentStore { return wins_higher !== race_to || wins_lower !== race_to; } ); - public readonly table$ = this.select((state) => state.settings.table); - public readonly serverAddress$ = this.select((state) => state.settings.serverAddress); + public readonly overlay$ = this.select((state) => state.overlay); public readonly vm$ = this.select( + this.table$, this.game$, this.playerOneRaceTo$, this.playerTwoRaceTo$, this.showRaceTo$, - this.table$, - this.serverAddress$, this.overlay$, - (game, playerOneRaceTo, playerTwoRaceTo, showRaceTo, table, serverAddress, overlay) => ({ + (table, game, playerOneRaceTo, playerTwoRaceTo, showRaceTo, overlay) => ({ + table, game, playerOneRaceTo, playerTwoRaceTo, showRaceTo, - table, - serverAddress, overlay, }) ); @@ -153,7 +136,7 @@ export class ScoreKeeperStore extends ComponentStore { // effects public readonly getGame = this.effect((trigger$) => { return trigger$.pipe( - withLatestFrom(this.select(state => state.settings.table)), + withLatestFrom(this.table$), switchMap(([_, table]) => this._apiService.getGame(table).pipe( tapResponse( @@ -214,19 +197,19 @@ export class ScoreKeeperStore extends ComponentStore { )); public readonly updateGame = this.effect((game$) => game$.pipe( - withLatestFrom(this.select(state => state.settings.table)), + withLatestFrom(this.table$), filter(([game, table]) => game.table === table), tap(([game]) => this.setGame({ game })), )); public readonly updateOverlay = this.effect((state$) => state$.pipe( - withLatestFrom(this.select(state => state.settings.table)), + withLatestFrom(this.table$), filter(([state, table]) => state.table === table), tap(([state]) => this.setOverlay(state)), )); public readonly saveGame = this.effect((trigger$) => trigger$.pipe( - withLatestFrom(this.select(state => state.settings.table)), + withLatestFrom(this.table$), switchMap(([, table]) => this._apiService.saveGame(table).pipe( tapResponse( () => { }, diff --git a/apps/score-keeper-nativescript/src/services/socket.service.ts b/apps/score-keeper-nativescript/src/services/socket.service.ts index ad60a21..7e8f31a 100644 --- a/apps/score-keeper-nativescript/src/services/socket.service.ts +++ b/apps/score-keeper-nativescript/src/services/socket.service.ts @@ -1,7 +1,12 @@ import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { SettingsActions, SettingsSelectors } from '../core/settings'; +import { Observable, BehaviorSubject } from 'rxjs'; type EventHandlers = { [key: string]: (payload: any) => void }; +export const DEFAULT_PORT = '1268'; + @Injectable({ providedIn: 'root', }) @@ -9,10 +14,28 @@ export class SocketService { private _connTimer: ReturnType | null = null; private _conn: WebSocket | null = null; private _eventHandlers: EventHandlers = {}; + private _server: string; + + private readonly _isConnected$ = new BehaviorSubject(false); + + constructor(private store: Store) { + this.store.select(SettingsSelectors.selectServer) + .subscribe((server) => { + this._server = server; + }); + } + + public get isConnected$(): Observable { + return this._isConnected$.asObservable(); + } public connect(): void { + if (this._conn !== null) { + return; + } + try { - this._conn = new WebSocket('ws://192.168.0.26:1268/latest/overlay'); + this._conn = new WebSocket(`ws://${this._server}:${DEFAULT_PORT}/latest/overlay`); this._conn.onopen = this._onOpen.bind(this); this._conn.onmessage = this._onMessage.bind(this); this._conn.onclose = this._onClose.bind(this); @@ -28,16 +51,29 @@ export class SocketService { } } + public disconnect(): void { + if (this._conn === null) { + return; + } + + this._conn.close(1000); + this._conn = null; + this._isConnected$.next(false); + } + private _onOpen(): void { + this._isConnected$.next(true); + this.store.dispatch(SettingsActions.getMaxTables()); + if (this._connTimer) { clearInterval(this._connTimer); this._connTimer = null; } } - private _onClose(): void { - if (!this._connTimer) { - this._connTimer = setInterval(this.connect.bind(this), 5000); + private _onClose(event: CloseEvent): void { + if (!this._connTimer && event?.code !== 1000) { + this._connTimer = setInterval(this.connect.bind(this), 10000); } } @@ -53,5 +89,6 @@ export class SocketService { private _onError(ev: Event): void { console.log(ev); + this._conn = null; } }