Skip to content
This repository has been archived by the owner on Dec 6, 2022. It is now read-only.

MES-3574 reload app config when app resumes #757

Merged
merged 6 commits into from
Sep 16, 2019
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
19 changes: 10 additions & 9 deletions src/app/__tests__/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ describe('App', () => {
{ provide: TranslateService, useValue: translateServiceMock },
],
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(App);
component = fixture.componentInstance;
});
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(App);
component = fixture.componentInstance;
statusBar = TestBed.get(StatusBar);
store$ = TestBed.get(Store);

statusBar = TestBed.get(StatusBar);

store$ = TestBed.get(Store);
spyOn(store$, 'dispatch');
spyOn(store$, 'dispatch');
spyOn(component, 'configureAccessibility');
spyOn(component, 'configurePlatformSubscriptions');
});
}));

describe('Class', () => {
Expand Down
60 changes: 44 additions & 16 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { StatusBar } from '@ionic-native/status-bar';
import { Store } from '@ngrx/store';

import { StoreModel } from '../shared/models/store.model';
import { LoadAppInfo } from '../modules/app-info/app-info.actions';
import { LoadAppInfo, AppSuspended, AppResumed } from '../modules/app-info/app-info.actions';
import { TranslateService } from 'ng2-translate/ng2-translate';
import { LOGIN_PAGE } from '../pages/page-names.constants';
import { Subscription } from 'rxjs/Subscription';
import { map } from 'rxjs/operators';
import { merge } from 'rxjs/observable/merge';

declare let window: any;

Expand All @@ -18,21 +21,36 @@ export class App {
textZoom: number = 100;
increasedContrast: Boolean = false;

private platformSubscription: Subscription;
private subscription: Subscription;

constructor(
private store$: Store<StoreModel>,
private statusBar: StatusBar,
private platform: Platform,
private translate: TranslateService,
) {
platform.ready()
this.platform.ready()
.then(() => {
this.configureLocale();
this.configureStatusBar();
this.configureAccessibility();
this.loadAppInfo();
if (this.platform.is('ios')) {
this.configureAccessibility();
this.configurePlatformSubscriptions();
}
});
}

ionViewWillUnload() {
if (this.platformSubscription) {
this.platformSubscription.unsubscribe();
}
if (this.subscription) {
this.subscription.unsubscribe();
}
}

configureLocale() {
this.translate.setDefaultLang('en');
}
Expand All @@ -47,19 +65,29 @@ export class App {
this.store$.dispatch(new LoadAppInfo());
}

configureAccessibility() {
if (this.platform.is('ios') && window && window.MobileAccessibility) {
window.MobileAccessibility.updateTextZoom();
window.MobileAccessibility.getTextZoom(this.getTextZoomCallback);
window.MobileAccessibility.isDarkerSystemColorsEnabled(
(increasedContrast: boolean) => this.increasedContrast = increasedContrast);
}
if (typeof this.platform.resume.subscribe === 'function') {
this.platform.resume.subscribe(() => {
window.MobileAccessibility.usePreferredTextZoom(true);
window.MobileAccessibility.getTextZoom(this.getTextZoomCallback);
});
}
configurePlatformSubscriptions() {
const merged$ = merge(
this.platform.resume.pipe(map(this.onAppResumed)),
this.platform.pause.pipe(map(this.onAppSuspended)),
);
this.platformSubscription = merged$.subscribe();
}

onAppResumed = () => {
this.store$.dispatch(new AppResumed());
window.MobileAccessibility.usePreferredTextZoom(true);
window.MobileAccessibility.getTextZoom(this.getTextZoomCallback);
}

onAppSuspended = () => {
this.store$.dispatch(new AppSuspended());
}

configureAccessibility = () => {
window.MobileAccessibility.updateTextZoom();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering about the removal of the window && window.MobileAccessibility line (51).
Is this because window is guaranteed to exists and this was an unnecessary check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. And we can assume MobileAccessibility will exist too unless the plugin fails.

Copy link
Contributor Author

@buzzub buzzub Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we shouldn't really need to use window.MobileAccessibility as this should work:

constructor(mobileAccessibility: MobileAccessibility) {
  this.platform.ready()
    .then(() => mobileAccessibility.getTextZoom())
    .then(e => this.zoomLevel = e);
}

see more at danielsogl/awesome-cordova-plugins#2138

...but it doesn't. The signature is wrong - it doesn't return a promise, it returns a callback ... but you can't pass it a function. Went down a rabbit hole trying to reference the plugin correctly. In the end, decided to leave the code as is because it works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this react in the browser , we know the plugin won't be there and that line will error

window.MobileAccessibility.getTextZoom(this.getTextZoomCallback);
window.MobileAccessibility.isDarkerSystemColorsEnabled(
(increasedContrast: boolean) => this.increasedContrast = increasedContrast);
}

getTextZoomCallback = (zoomLevel: number) => {
Expand Down
4 changes: 4 additions & 0 deletions src/app/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ img {
height: 100% !important;
width: 100% !important;
}

body {
-webkit-text-size-adjust: 100% !important; // prevents the iOS text size config from adjusting webview zoom level
}
33 changes: 32 additions & 1 deletion src/modules/app-info/app-info.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export const LOAD_APP_INFO = '[AppComponent] Load App Info';
export const LOAD_APP_INFO_SUCCESS = '[AppInfoEffects] Load App Info Success';
export const LOAD_APP_INFO_FAILURE = '[AppInfoEffects] Load App Info Failure';
export const LOAD_EMPLOYEE_ID = '[LoginComponent] Load employee ID';
export const LOAD_CONFIG_SUCCESS = '[AppInfoEffects] Load Config Success';
export const SET_DATE_CONFIG_LOADED = '[AppInfoEffects] Set Date Config Loaded';
export const APP_SUSPENDED = '[AppInfoEffects] App Suspended';
export const APP_RESUMED = '[AppInfoEffects] App Resumed';
export const RESTART_APP = '[AppInfoEffects] Restart App';

export class LoadAppInfo implements Action {
readonly type = LOAD_APP_INFO;
Expand All @@ -24,8 +29,34 @@ export class LoadEmployeeId implements Action {
constructor(public employeeId: string) {}
}

export class LoadConfigSuccess implements Action {
readonly type = LOAD_CONFIG_SUCCESS;
}

export class SetDateConfigLoaded implements Action {
readonly type = SET_DATE_CONFIG_LOADED;
constructor(public refreshDate: string) {}
}

export class AppSuspended implements Action {
readonly type = APP_SUSPENDED;
}

export class AppResumed implements Action {
readonly type = APP_RESUMED;
}

export class RestartApp implements Action {
readonly type = RESTART_APP;
}

export type Types =
LoadAppInfo |
LoadAppInfoSuccess |
LoadAppInfoFailure |
LoadEmployeeId;
LoadEmployeeId |
LoadConfigSuccess |
SetDateConfigLoaded |
AppSuspended |
AppResumed |
RestartApp;
41 changes: 39 additions & 2 deletions src/modules/app-info/app-info.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,26 @@ import { Injectable } from '@angular/core';
import { Effect, Actions, ofType } from '@ngrx/effects';

import { of } from 'rxjs/observable/of';
import { map, switchMap, catchError } from 'rxjs/operators';

import { map, switchMap, catchError, concatMap, withLatestFrom, filter } from 'rxjs/operators';
import { StoreModel } from '../../shared/models/store.model';
import { Store, select } from '@ngrx/store';
import * as appInfoActions from './app-info.actions';
import { AppInfoProvider } from '../../providers/app-info/app-info';
import { HttpErrorResponse } from '@angular/common/http';
import { DateTimeProvider } from '../../providers/date-time/date-time';
import { getAppInfoState } from './app-info.reducer';
import { getDateConfigLoaded } from './app-info.selector';
import { LOGIN_PAGE } from '../../pages/page-names.constants';
import { NavigationProvider } from '../../providers/navigation/navigation';

@Injectable()
export class AppInfoEffects {
constructor(
private actions$: Actions,
private appInfoProvider: AppInfoProvider,
private dateTimeProvider: DateTimeProvider,
private store$: Store<StoreModel>,
private navigationProvider: NavigationProvider,
) {}

@Effect()
Expand All @@ -27,4 +36,32 @@ export class AppInfoEffects {
);
}),
);

@Effect()
loadConfigSuccessEffect$ = this.actions$.pipe(
ofType(appInfoActions.LOAD_CONFIG_SUCCESS),
switchMap(() => {
console.log('Config loaded successfully');
Copy link
Contributor

@rossfauldsbjss rossfauldsbjss Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need console.logs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Ammar wanted this. I can't remember exactly why... but it definitely made sense at the time.

return of(new appInfoActions.SetDateConfigLoaded(this.dateTimeProvider.now().format('YYYY-MM-DD')));
}),
);

@Effect()
appResumedEffect$ = this.actions$.pipe(
ofType(appInfoActions.APP_RESUMED),
concatMap(action => of(action).pipe(
withLatestFrom(
this.store$.pipe(
select(getAppInfoState),
select(getDateConfigLoaded),
),
),
)),
filter(([action, dateConfigLoaded]) => dateConfigLoaded !== this.dateTimeProvider.now().format('YYYY-MM-DD')),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 😍

switchMap(([action, dateConfigLoaded]) => {
console.log('App resumed after being suspended. Config was not loaded today... app will refresh');
this.navigationProvider.getNav().setRoot(LOGIN_PAGE);
return of(new appInfoActions.RestartApp());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this action used for anything or is it just for info

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's just for info. Need to return an observable, so figured I'd return a descriptive action. Could easily just return empty of() but the benefit of this is I'll be able to write a test.

There's also an AppSuspended action. It get's dispatched, but nothing acts on it (yet).

}),
);
}
1 change: 1 addition & 0 deletions src/modules/app-info/app-info.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export type AppInfoModel = {
versionNumber: string,
employeeId: string | null,
error?: any,
dateConfigLoaded?: string,
};
5 changes: 5 additions & 0 deletions src/modules/app-info/app-info.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export function appInfoReducer(state = initialState, action: appInfoActions.Type
...state,
employeeId: action.employeeId,
};
case appInfoActions.SET_DATE_CONFIG_LOADED:
return {
...state,
dateConfigLoaded: action.refreshDate,
};
default:
return state;
}
Expand Down
2 changes: 2 additions & 0 deletions src/modules/app-info/app-info.selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ import { AppInfoModel } from './app-info.model';
export const getVersionNumber = (appInfo: AppInfoModel) => appInfo.versionNumber;

export const getEmployeeId = (appInfo: AppInfoModel) => appInfo.employeeId;

export const getDateConfigLoaded = (appInfo: AppInfoModel) => appInfo.dateConfigLoaded;
2 changes: 2 additions & 0 deletions src/pages/login/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { LogsProvider } from '../../providers/logs/logs';
import { LogType } from '../../shared/models/log.model';
import { DASHBOARD_PAGE } from '../page-names.constants';
import { LogHelper } from '../../providers/logs/logsHelper';
import { LoadConfigSuccess } from '../../modules/app-info/app-info.actions';

@IonicPage()
@Component({
Expand Down Expand Up @@ -104,6 +105,7 @@ export class LoginPage extends BasePageComponent {
await this.initialisePersistentStorage();

await this.appConfigProvider.loadRemoteConfig();
this.store$.dispatch(new LoadConfigSuccess());

this.analytics.initialiseAnalytics();

Expand Down