Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Global Settings #15

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions apps/score-keeper-nativescript/src/app.component.ts
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
17 changes: 16 additions & 1 deletion apps/score-keeper-nativescript/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -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],
Expand Down
2 changes: 2 additions & 0 deletions apps/score-keeper-nativescript/src/core/settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as SettingsSelectors from './settings.selectors';
export { SettingsActions } from './settings.actions';
Original file line number Diff line number Diff line change
@@ -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; }>(),
},
});
Original file line number Diff line number Diff line change
@@ -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,
) { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface AppSettings {
server: string;
table: number;
maxTables: number;
}
Original file line number Diff line number Diff line change
@@ -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,
})),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { SettingsStateKey, State } from './settings.reducer';

export const selectSettingsState = createFeatureSelector<State>(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,
);
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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);
}
Expand Down
Loading
Loading