MOBILE-2261 init: Implement init delegate

main
Dani Palou 2017-11-07 13:01:32 +01:00
parent a4eb66062a
commit 8196c79954
4 changed files with 189 additions and 49 deletions

View File

@ -1,6 +1,6 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core'; import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { IonicApp, IonicErrorHandler, IonicModule, Platform } from 'ionic-angular';
import { HttpClient, HttpClientModule } from '@angular/common/http'; import { HttpClient, HttpClientModule } from '@angular/common/http';
import { SplashScreen } from '@ionic-native/splash-screen'; import { SplashScreen } from '@ionic-native/splash-screen';
@ -23,6 +23,7 @@ import { CoreTimeUtilsProvider } from '../providers/utils/time';
import { CoreUrlUtilsProvider } from '../providers/utils/url'; import { CoreUrlUtilsProvider } from '../providers/utils/url';
import { CoreUtilsProvider } from '../providers/utils/utils'; import { CoreUtilsProvider } from '../providers/utils/utils';
import { CoreMimetypeUtilsProvider } from '../providers/utils/mimetype'; import { CoreMimetypeUtilsProvider } from '../providers/utils/mimetype';
import { CoreInitDelegate } from '../providers/init';
// For translate loader. AoT requires an exported function for factories. // For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient) {
@ -66,7 +67,22 @@ export function createTranslateLoader(http: HttpClient) {
CoreTimeUtilsProvider, CoreTimeUtilsProvider,
CoreUrlUtilsProvider, CoreUrlUtilsProvider,
CoreUtilsProvider, CoreUtilsProvider,
CoreMimetypeUtilsProvider CoreMimetypeUtilsProvider,
CoreInitDelegate
] ]
}) })
export class AppModule {} export class AppModule {
constructor(platform: Platform, initDelegate: CoreInitDelegate) {
// Create a handler for platform ready and register it in the init delegate.
let handler = {
name: 'CorePlatformReady',
priority: initDelegate.MAX_RECOMMENDED_PRIORITY + 400,
blocking: true,
load: platform.ready
};
initDelegate.registerProcess(handler);
// Execute the init processes.
initDelegate.executeInitProcesses();
}
}

View File

@ -91,22 +91,6 @@ export class CoreAppProvider {
return this.db; return this.db;
}; };
/**
* Core init process for the app.
*
* @description
* This should be the first init process of all, no other process should run until we are certain that the cordova plugins
* are loaded, which is what platform.ready tells us.
*
* Reserved for core use, do not call directly.
*
* @protected
* @return Promise resolved when ready.
*/
initProcess() : Promise<any> {
return this.platform.ready();
};
/** /**
* Checks if the app is running in a desktop environment (not browser). * Checks if the app is running in a desktop environment (not browser).
* *
@ -165,19 +149,6 @@ export class CoreAppProvider {
return limited.indexOf(type) > -1; return limited.indexOf(type) > -1;
}; };
/**
* Instantly returns if the app is ready.
*
* To be notified when the app is ready, refer to {@link $mmApp#ready}.
*
* @return {Boolean} True when it is, false when not.
* @todo
*/
isReady() {
// var promise = $injector.get('$mmInitDelegate').ready();
// return promise.$$state.status === 1;
};
/** /**
* Open the keyboard. * Open the keyboard.
*/ */
@ -188,23 +159,6 @@ export class CoreAppProvider {
} }
}; };
/**
* Resolves when the app is ready.
*
* Usage:
*
* $mmApp.ready().then(function() {
* // What you want to do.
* });
*
* @return {Promise} Resolved when the app is initialised. Never rejected.
* @todo
*/
ready() {
// Injects to prevent circular dependencies.
// return $injector.get('$mmInitDelegate').ready();
};
/** /**
* Start an SSO authentication process. * Start an SSO authentication process.
* Please notice that this function should be called when the app receives the new token from the browser, * Please notice that this function should be called when the app receives the new token from the browser,

View File

@ -0,0 +1,156 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Platform } from 'ionic-angular';
import { CoreLoggerProvider } from './logger';
import { CoreUtilsProvider } from './utils/utils';
export interface CoreInitHandler {
name: string; // Name of the handler.
load(): Promise<any>; // Function to execute during the init process.
priority?: number; // The highest priority is executed first. You should use values lower than MAX_RECOMMENDED_PRIORITY.
blocking?: boolean; // Set this to true when this process should be resolved before any following one.
};
/*
* Provider for initialisation mechanisms.
*/
@Injectable()
export class CoreInitDelegate {
DEFAULT_PRIORITY = 100; // Default priority for init processes.
MAX_RECOMMENDED_PRIORITY = 600;
initProcesses = {};
logger;
readiness;
constructor(logger: CoreLoggerProvider, platform: Platform, private utils: CoreUtilsProvider) {
this.logger = logger.getInstance('CoreInitDelegate');
}
/**
* Executes the registered init processes.
*
* Reserved for core use, do not call directly.
*/
executeInitProcesses() : void {
let ordered = [];
if (typeof this.readiness == 'undefined') {
this.initReadiness();
}
// Re-ordering by priority.
for (let name in this.initProcesses) {
ordered.push(this.initProcesses[name]);
}
ordered.sort((a, b) => {
return b.priority - a.priority;
});
ordered = ordered.map((data: CoreInitHandler) => {
return {
context: this,
func: this.prepareProcess,
params: [data],
blocking: !!data.blocking
};
});
// Execute all the processes in order to solve dependencies.
this.utils.executeOrderedPromises(ordered).finally(this.readiness.resolve);
}
/**
* Init the readiness promise.
*/
protected initReadiness() : void {
this.readiness = this.utils.promiseDefer();
this.readiness.promise.then(() => this.readiness.resolved = true);
}
/**
* Instantly returns if the app is ready.
*
* To be notified when the app is ready, refer to {@link $mmApp#ready}.
*
* @return {boolean} Whether it's ready.
*/
isReady(): boolean {
return this.readiness.resolved;
};
/**
* Convenience function to return a function that executes the process.
*
* @param {CoreInitHandler} data The data of the process.
* @return {Promise<any>} Promise of the process.
*/
protected prepareProcess(data: CoreInitHandler) : Promise<any> {
let promise;
this.logger.debug(`Executing init process '${data.name}'`);
try {
promise = data.load();
} catch (e) {
this.logger.error('Error while calling the init process \'' + data.name + '\'. ' + e);
return;
}
return promise;
}
/**
* Notifies when the app is ready. This returns a promise that is resolved when the app is initialised.
*
* @return {Promise<any>} Resolved when the app is initialised. Never rejected.
*/
ready() : Promise<any> {
if (typeof this.readiness === 'undefined') {
// Prevent race conditions if this is called before executeInitProcesses.
this.initReadiness();
}
return this.readiness.promise;
}
/**
* Registers an initialisation process.
*
* @description
* Init processes can be used to add initialisation logic to the app. Anything that should block the user interface while
* some processes are done should be an init process. It is recommended to use a priority lower than MAX_RECOMMENDED_PRIORITY
* to make sure that your process does not happen before some essential other core processes.
*
* An init process should never change state or prompt user interaction.
*
* This delegate cannot be used in remote addons.
*
* @param {CoreInitHandler} instance The instance of the handler.
*/
registerProcess(handler: CoreInitHandler) : void {
if (typeof handler.priority == 'undefined') {
handler.priority = this.DEFAULT_PRIORITY;
}
if (typeof this.initProcesses[handler.name] != 'undefined') {
this.logger.log(`Process '${handler.name}' already registered.`);
return;
}
this.logger.log(`Registering process '${handler.name}'.`);
this.initProcesses[handler.name] = handler;
}
}

View File

@ -916,6 +916,20 @@ export class CoreUtilsProvider {
return mapped; return mapped;
} }
/**
* Similar to AngularJS $q.defer(). It will return an object containing the promise, and the resolve and reject functions.
*
* @return {any} Object containing the promise, and the resolve and reject functions.
*/
promiseDefer() : any {
let deferred: any = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
/** /**
* Given a promise, returns true if it's rejected or false if it's resolved. * Given a promise, returns true if it's rejected or false if it's resolved.
* *