refactor: initialize config via APP_INITIALIZER

Closes #181
This commit is contained in:
Jovan Krunić
2022-02-09 20:43:08 +01:00
parent 411e0970b8
commit bde0df219c
9 changed files with 37 additions and 72 deletions

View File

@@ -17,8 +17,6 @@ import {Router} from '@angular/router';
import {App, URLOpenListenerEvent} from '@capacitor/app';
import {SplashScreen} from '@capacitor/splash-screen';
import {Platform, ToastController} from '@ionic/angular';
import {NGXLogger} from 'ngx-logger';
import {ConfigProvider} from './modules/config/config.provider';
import {SettingsProvider} from './modules/settings/settings.provider';
import {PAIAAuthService} from './modules/auth/paia/paia-auth.service';
import {DefaultAuthService} from './modules/auth/default-auth.service';
@@ -52,8 +50,6 @@ export class AppComponent implements AfterContentInit {
*
* @param platform TODO
* @param settingsProvider TODO
* @param configProvider TODO
* @param logger An angular logger
* @param router The angular router
* @param zone The angular zone
* @param defaultAuth Auth Service
@@ -65,8 +61,6 @@ export class AppComponent implements AfterContentInit {
constructor(
private readonly platform: Platform,
private readonly settingsProvider: SettingsProvider,
private readonly configProvider: ConfigProvider,
private readonly logger: NGXLogger,
private readonly router: Router,
private readonly zone: NgZone,
private readonly defaultAuth: DefaultAuthService,
@@ -102,19 +96,6 @@ export class AppComponent implements AfterContentInit {
await this.paiaAuth.init();
await SplashScreen.hide();
// initialise the configProvider
try {
await this.configProvider.init();
} catch (error) {
if (
typeof error.name !== 'undefined' &&
error.name === 'ConfigInitError'
) {
// TODO: Issue #43 handle initialisation error and inform user
}
this.logger.error(error);
}
// set order of categories in settings
this.settingsProvider.setCategoriesOrder([
'profile',

View File

@@ -65,18 +65,21 @@ import {AuthModule} from './modules/auth/auth.module';
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
import {BackgroundModule} from './modules/background/background.module';
import {LibraryModule} from './modules/library/library.module';
import {StorageProvider} from './modules/storage/storage.provider';
registerLocaleData(localeDe);
/**
* Initializes settings from Config before other components
* Initializes data needed on startup
*
* @param storageProvider provider of the saved data (using framework's storage)
* @param logger TODO
* @param settingsProvider provider of settings (e.g. language that has been set)
* @param configProvider TODO
* @param translateService TODO
*/
export function initSettingsFactory(
export function initializerFactory(
storageProvider: StorageProvider,
logger: NGXLogger,
settingsProvider: SettingsProvider,
configProvider: ConfigProvider,
@@ -84,10 +87,12 @@ export function initSettingsFactory(
) {
return async () => {
initLogger(logger);
await storageProvider.init();
await configProvider.init();
await settingsProvider.init();
try {
// set language from settings
if (configProvider.firstSession) {
// set language from browser
await settingsProvider.setSettingValue(
'profile',
'language',
@@ -184,13 +189,14 @@ export function createTranslateLoader(http: HttpClient) {
provide: APP_INITIALIZER,
multi: true,
deps: [
StorageProvider,
NGXLogger,
SettingsProvider,
ConfigProvider,
TranslateService,
ScheduleSyncService,
],
useFactory: initSettingsFactory,
useFactory: initializerFactory,
},
],
})

View File

@@ -23,7 +23,7 @@ import {ConfigProvider} from '../../config/config.provider';
styleUrls: ['about-page.scss'],
})
export class AboutPageComponent implements OnInit {
content: Promise<SCAboutPage>;
content: SCAboutPage;
constructor(
private readonly route: ActivatedRoute,
@@ -33,12 +33,11 @@ export class AboutPageComponent implements OnInit {
async ngOnInit() {
const route = this.route.snapshot.url.map(it => it.path).join('/');
this.content = new Promise(resolve => {
this.configProvider
.getValue('aboutPages')
.then(value =>
resolve((value as SCAppConfiguration['aboutPages'])[route] ?? {}),
);
});
this.content =
(
this.configProvider.getValue(
'aboutPages',
) as SCAppConfiguration['aboutPages']
)[route] ?? {};
}
}

View File

@@ -19,7 +19,7 @@
<ion-menu-button></ion-menu-button>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title *ngIf="content | async as content; else titleLoading">{{
<ion-title *ngIf="content; else titleLoading">{{
'title' | translateSimple: content
}}</ion-title>
<ng-template #titleLoading>
@@ -29,7 +29,7 @@
</ng-template>
</ion-toolbar>
</ion-header>
<ion-content *ngIf="content | async as content">
<ion-content *ngIf="content">
<about-page-content
*ngFor="let element of content.content"
[content]="element"

View File

@@ -107,7 +107,6 @@ describe('ConfigProvider', () => {
expect(storageProviderSpy.has).toHaveBeenCalled();
expect(storageProviderSpy.get).toHaveBeenCalledTimes(0);
expect(configProvider.client.handshake).toHaveBeenCalled();
expect(configProvider.initialised).toBe(true);
expect(await configProvider.getValue('name')).toEqual(
sampleIndexResponse.app.name,
);
@@ -129,7 +128,6 @@ describe('ConfigProvider', () => {
expect(error).toEqual(new ConfigFetchError());
expect(storageProviderSpy.has).toHaveBeenCalled();
expect(storageProviderSpy.get).toHaveBeenCalled();
expect(configProvider.initialised).toBe(true);
expect(await configProvider.getValue('name')).toEqual(
sampleIndexResponse.app.name,
);

View File

@@ -50,21 +50,16 @@ export class ConfigProvider {
*/
config: SCIndexResponse;
/**
* First session indicator
*/
firstSession = true;
/**
* Initialised status flag of config provider
*/
initialised = false;
/**
* Version of the @openstapps/core package that app is using
*/
scVersion = packageJson.dependencies['@openstapps/core'];
/**
* First session indicator (config not found in storage)
*/
firstSession = true;
/**
* Constructor, initialise api client
*
@@ -100,17 +95,7 @@ export class ConfigProvider {
*
* @param attribute requested attribute from app configuration
*/
public async getValue(attribute: keyof SCAppConfiguration) {
if (!this.initialised) {
try {
await this.init();
} catch (error) {
// don't throw ConfigFetchError if saved config is available
if (error.name === 'ConfigFetchError' && !this.initialised) {
throw error;
}
}
}
public getValue(attribute: keyof SCAppConfiguration) {
if (typeof this.config.app[attribute] !== 'undefined') {
return this.config.app[attribute];
}
@@ -138,12 +123,10 @@ export class ConfigProvider {
async init(): Promise<void> {
let loadError;
let fetchError;
this.initialised = false;
// load saved configuration
try {
this.config = await this.loadLocal();
this.firstSession = false;
this.initialised = true;
this.logger.log(`initialised configuration from storage`);
if (this.config.backend.SCVersion !== this.scVersion) {
loadError = new WrongConfigVersionInStorage(
@@ -159,7 +142,6 @@ export class ConfigProvider {
try {
const fetchedConfig: SCIndexResponse = await this.fetch();
await this.set(fetchedConfig);
this.initialised = true;
this.logger.log(`initialised configuration from remote`);
} catch (error) {
fetchError = error;
@@ -169,7 +151,7 @@ export class ConfigProvider {
throw new ConfigInitError();
}
if (typeof loadError !== 'undefined') {
throw loadError;
this.logger.warn(loadError);
}
if (typeof fetchError !== 'undefined') {
throw fetchError;
@@ -182,7 +164,6 @@ export class ConfigProvider {
* @throws SavedConfigNotAvailable if no configuration could be loaded
*/
async loadLocal(): Promise<SCIndexResponse> {
await this.storageProvider.init();
// get local configuration
if (await this.storageProvider.has(STORAGE_KEY_CONFIG)) {
return this.storageProvider.get<SCIndexResponse>(STORAGE_KEY_CONFIG);

View File

@@ -13,7 +13,6 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {CommonModule} from '@angular/common';
import {APP_INITIALIZER, NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {RouterModule, Routes} from '@angular/router';
import {LeafletModule} from '@asymmetrik/ngx-leaflet';
@@ -34,6 +33,7 @@ import {MapPageComponent} from './page/map-page.component';
import {MapListModalComponent} from './page/modals/map-list-modal.component';
import {MapSingleModalComponent} from './page/modals/map-single-modal.component';
import {MapItemComponent} from './item/map-item.component';
import {NgModule} from '@angular/core';
/**
* Initializes the default area to show in advance (before components are initialized)
@@ -86,12 +86,6 @@ const mapRoutes: Routes = [
DataProvider,
DataFacetsProvider,
StAppsWebHttpClient,
{
provide: APP_INITIALIZER,
multi: true,
deps: [ConfigProvider, MapProvider],
useFactory: initMapConfigFactory,
},
],
})
export class MapModule {}

View File

@@ -26,6 +26,7 @@ import {divIcon, geoJSON, icon, LatLng, Map, marker, Marker} from 'leaflet';
import {DataProvider} from '../data/data.provider';
import {MapPosition, PositionService} from './position.service';
import {hasValidLocation} from '../data/types/place/place-types';
import {ConfigProvider} from '../config/config.provider';
/**
* Provides methods for presenting the map
@@ -107,7 +108,12 @@ export class MapProvider {
constructor(
private dataProvider: DataProvider,
private positionService: PositionService,
) {}
private configProvider: ConfigProvider,
) {
this.defaultPolygon = this.configProvider.getValue(
'campusPolygon',
) as Polygon;
}
/**
* Provide the specific place by its UID
@@ -128,7 +134,7 @@ export class MapProvider {
/**
* Provide places (buildings and canteens) const result = await this.dataProvider.search(query);
*
* @param contextFilter Additional contextual filter (e.g. from the context menu)
* @param queryText Query (text) of the search query

View File

@@ -333,9 +333,9 @@ export class SettingsProvider {
*/
public async init(): Promise<void> {
try {
const settings: SCSetting[] = (await this.configProvider.getValue(
const settings: SCSetting[] = this.configProvider.getValue(
'settings',
)) as SCSetting[];
) as SCSetting[];
for (const setting of settings) this.addSetting(setting);
for (const category of Object.keys(this.settingsCache)) {