Home Reference Source Repository

js/base/sync/SyncService.js

'use strict';

var SyncService = ($q, $timeout, $filter, $rootScope, SyncAPIService, LoggerService,
	UserSession, SyncSelection, ModuleService, safo, UserSettings) => {


	var LOG = LoggerService('SyncService');

	// The current sync promsie
	var syncBackgroundPromise = null;


	class SyncServiceImpl{

		/**
		* Gets a summary of the sync status for all modules the user
		* is linked to.
		*/
		getSyncDetails(){

			var syncDetails = {
				inSync : true,
				download : 0,
				upload : 0,
				total : 0,
				tools : []
			};

			/**
			* Returns a promise to loop through all the modules to get their sync status
			*/
			function getLoopModulesPromise(linkedModules){
				let promise = $q.when();
				angular.forEach(linkedModules, function(linkedModule){
					promise = promise.then(function(){
						// Get the sync details for the current item in the list
						return SyncAPIService.getSyncStatus(linkedModule.id)
							.then((syncStatus) => {
								syncDetails.inSync = syncDetails.inSync && syncStatus.inSync;
								syncDetails.download += syncStatus.download;
								syncDetails.upload += syncStatus.upload;
								syncDetails.total += syncStatus.total;

								var tools = $filter('object2Array')(syncStatus.tools);
								safo(tools, 'moduleId', linkedModule.id, 'moduleName', linkedModule.name);
								syncDetails.tools = syncDetails.tools.concat(tools);
							});
					});
				});
				return promise.then(function(){
					$rootScope.$broadcast('syncStatusChanged', {'state' : syncDetails.inSync ? 'inSync' : 'outSync'});
					return syncDetails;
				});
			}

			/**
			* Get the list of all the modules currently linked to this app
			* Then loop through each of them to build up a big sync status
			*/
			return ModuleService.getLinkedModules()
				.then(getLoopModulesPromise);
		}

		/***
		* Sync a tool
		* @param toolname - name of the tool to sync
		*/
		syncDownloadTool(moduleId, toolname){
			return SyncAPIService.syncDownloadTool(moduleId, toolname);
		}


		/***
		* Performs a synchronise of the selected tools
		*/
		syncSelected(syncSelection){

			// Initialise scope variables for this page
			var toolUploads = syncSelection.getUploadArray();
			var toolDownloads = syncSelection.getDownloadArray();
			var didUpdateBase = false;// Flag if we updated base. If base was updated we need to reload the tools
			var self = this;
			/*
			* Function that will return promise for new download if there is any
			* or returns null if there are no more promises for downloads
			*/
			function getDownloadPromise(){
				let promise = $q.when();
				angular.forEach(toolDownloads, function(tool){
					promise = promise.then(function(){
						return self.syncDownloadTool(tool.moduleId, tool.key)
						.then(() => {
							LOG.debug('Completed download for tool : ' + tool.key);
							tool.downloadProgress = 100;

							// Set the flag if we downloaded for base
							if(tool.key == 'base'){
								didUpdateBase = true;
							}
						});
					});
				});
				return promise;
			}

			/*
			* Function that will return a promise for the new upload if there is any
			* or returns null if there are no more promises for uploads
			*/
			function getUploadPromise(){
				let promise = $q.when();
				angular.forEach(toolUploads, function(tool){
					promise = promise.then(function(){
						return self.syncUploadTool(tool.key)
						.then(() => {
							LOG.debug('Completed upload for tool : ' + tool.key);
							tool.downloadProgress = 100;
						});
					});
				});
				return promise;
			}

			// Get the promise to check the offline status
			function emitSyncStatusChange(){
				$rootScope.$broadcast('syncStatusChanged');
				return {
					'didUpdateBase' : didUpdateBase
				};
			}

			// Start loop with downloads
			return getDownloadPromise()
				.then(getUploadPromise)
				.then(emitSyncStatusChange);

		}


		/***
		* Sync a tool
		* @param toolname - name of the tool to sync
		*/
		syncUploadTool(moduleId, toolname){
			return SyncAPIService.syncUploadTool(moduleId, toolname);
		}

		startBackgroundSync(){
			const self = this;
			// Make sure we are not already running
			if(syncBackgroundPromise == null){

				UserSettings.getSettings().then((settings) => {
					if(settings.autoSyncDownload || settings.autoSyncUpload){
						LOG.debug('Starting background sync timer... ' + settings.autoSyncInterval);
						// Start and save the promise
						syncBackgroundPromise = $timeout(() => {
							/*
							* Because this happens somewhere later in time, we need to check if we actually still
							* want to continue with the sync.
							* This shouldn't be a problem because the UI should stop the timer promise when the user
							* decides to cancel the auto sync, but we do it anyway ;)
							*/
							if(settings.autoSyncDownload || settings.autoSyncUpload){
								self._handleTimedSync();
							}
						}, settings.autoSyncInterval || 180000); // Avoid runaway if for some reason this gets null
					}
					else{
						LOG.debug('Not going to start sync, it is not enabled');
					}
				});
			}
			else{
				LOG.debug('Cannot start background sync again, already running...');
			}
		}

		_handleTimedSync(){
			LOG.debug('Need to check for sync now');
			const self = this;
			$timeout.cancel(syncBackgroundPromise);

			UserSettings.getSettings().then((settings) => {
				self.getSyncDetails().then((syncStatus) => {

					var syncSelection = SyncSelection.newInstance();
					syncSelection.tools = syncStatus.tools;

					if(settings.autoSyncDownload){
						syncSelection.selectAllDownloads();
					}

					if(settings.autoSyncUpload){
						syncSelection.selectAllUploads();
					}

					self.syncSelected(syncSelection).then(() => {
						syncBackgroundPromise = null;
						self.startBackgroundSync(); // Restart the timer
					});

				});
			});
		}


		stopBackgroundSync(){
			LOG.debug('Stopping background sync timer...');
			if(syncBackgroundPromise != null){
				$timeout.cancel(syncBackgroundPromise);
				syncBackgroundPromise = null;
			}
		}

		/**
		* Function to notify this service that there are changes that need
		* attention
		*/
		notifyChanges(){
			$rootScope.$broadcast('syncStatusChanged', {'state' : 'outSync'});
			this._handleTimedSync(); // Handle it now if we can
		}

	}


	return new SyncServiceImpl();

};
SyncService.$inject = ['$q', '$timeout', '$filter', '$rootScope', 'SyncAPIService', 'LoggerService',
'UserSession', 'SyncSelection', 'ModuleService', 'safo', 'UserSettings'];
export default SyncService;