Reference Source

src/components/presenters/authloading-presenter.js

import AsyncLock from 'async-lock';

import BasePresenter from './presenter';
import { AuthLoadingM, HomeM } from '../models/export-models';

const lock = new AsyncLock({timeout: 10000});

/**
 * Class for the auth loading presenter to check if the authentication state is valid. 
 * Uses the persist storage as the model.
 */
class AuthLoadingPresenter extends BasePresenter {
	/**
	 * Creates an instance of AuthLoadingPresenter. Subscribes to models to await data 
	 *
	 * @constructor
	 */
	constructor() {
		super();
		this._dataLoaded = false;
		this._dataCount = 0;
		this._callback = null;

		// This is just used to see how many models we have
		// We could declare the import using 'as Models' but doing Object.keys(Models).length is O(n) so this is faster
		// Only add models that we expect data from
		this._numModels = [HomeM].length;
		// AlertM.subscribe(this);

		/*
		 * The true in the subscribe forces the model to toggle it's database listeners.
		 * This is helpful when leaving the app and reentering on Android.
		 * Has not been tested with more than one subscribed model that we expect data from.
		 */
		HomeM.subscribe(this, true); 

		// This model is really only used to logout, so we don't expect an onUpdated call from it
		// We use callbacks since logout is an async call
		AuthLoadingM.subscribe(this); // We don't expect data from it so don't need to add it to '_numModels'
	}

	/**
	 * Asynchronously check the authentication state of the user to see if they are logged in.
	 *
	 * @param {Function} onSuccess - A success callback
	 * @param {Function} onFailure - A failure callback
	 */
	async checkAuthState(onSuccess, onFailure) {
		await AuthLoadingM.checkAuthenticationState((userID) => {
			if (this._dataLoaded) { // In-case this is reached after data is received
				this.onRetrievalSuccess(userID, onSuccess, onFailure);
			} else { // If data is received after authentication completes
				this.setCallback(() => {
					this.onRetrievalSuccess(userID, onSuccess, onFailure);
				});
			}
		})
	}

	/**
	 * Sets the callback to be called when data is received.
	 *
	 * @param {Function} callback - A function to call when data is received
	 */
	setCallback(callback) {
		this._callback = callback;
	}

	/**
	 * Check if the userToken is valid and if it is, then call the onSuccess callback, otherwise call the onFailure callback.
	 *
	 * @param {string} userToken - A user token
	 * @param {Function} onSuccess - A success callback
	 * @param {Function} onFailure - A failure callback
	 */
	onRetrievalSuccess(userToken, onSuccess, onFailure) {
		// console.log(userToken);
		userToken ? onSuccess() : onFailure();
		AuthLoadingM.unsubscribeAuthListener();
	}

	/**
	 * Called when a response is received from the Home and the Alert models after it has received data.
	 * Acquires a lock to make sure that both don't enter at the same time.
	 *
	 * @param {Object} data - The data received from the model
	 */
	onUpdated = (data) => {
		// Acquire the lock on some key
		lock.acquire('key', (done) => {
			this._dataCount++; // Increment on the amount of data we receive (The reason for the lock)
			// console.log(this._dataCount);

			// Check if the amount of data received is the same as the number of models we expect data from
			if (this._dataCount === this._numModels) {
				this.onDestroy(); // Unsubscribe from the models
				this._dataLoaded = true; // In-case authentication occurs after data is received
				if (this._callback != null) {
					this._callback();
				}
			}

			done(); // Need to say we are finished with the lock
		}, (err, ret) => {
			// Lock is released
			// console.log(err);
		});
	}

	/**
	 * Unsubscribe from models
	 */
	onDestroy = () => {
		// AlertM.unsubscribe(this);
		AuthLoadingM.unsubscribe(this);
		HomeM.unsubscribe(this);
	}

	/**
	 * Try to logout. Function is called on every entry to the authloading view so only execute logout if logout is requested.
	 *
	 * @param {Boolean} shouldLogout - true: if a logout is requested; false: otherwise
	 * @param {Function} onSuccess - A function to call on a successful logout
	 * @param {Function} onFailure - A function to call on failure to logout
	 */
	tryLogout = (shouldLogout, onSuccess, onFailure) => {
		// console.log(shouldLogout);
		if (shouldLogout) {
			AuthLoadingM.logout(onSuccess, onFailure);
		}
	}

}

export default AuthLoadingPresenter;