forked from EVOgeek/Vmeda.Online
		
	MOBILE-2114 scorm: Prefetch after sending data
This commit is contained in:
		
							parent
							
								
									48b5233a84
								
							
						
					
					
						commit
						cba4f2271a
					
				| @ -61,7 +61,8 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom | ||||
|     protected lastAttempt: number; // Last attempt.
 | ||||
|     protected lastIsOffline: boolean; // Whether the last attempt is offline.
 | ||||
|     protected hasPlayed = false; // Whether the user has opened the player page.
 | ||||
|     protected syncDueToPlayerLeft = false; // Whether a sync was due to the user leaving the player.
 | ||||
|     protected dataSentObserver; // To detect data sent to server.
 | ||||
|     protected dataSent = false; // Whether some data was sent to server while playing the SCORM.
 | ||||
| 
 | ||||
|     constructor(injector: Injector, protected scormProvider: AddonModScormProvider, @Optional() protected content: Content, | ||||
|             protected scormHelper: AddonModScormHelperProvider, protected scormOffline: AddonModScormOfflineProvider, | ||||
| @ -330,13 +331,12 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom | ||||
|      * @return {boolean}        If suceed or not. | ||||
|      */ | ||||
|     protected hasSyncSucceed(result: any): boolean { | ||||
|         if (result.updated || this.syncDueToPlayerLeft) { | ||||
|             // Check completion status if something was sent or the user just left the player.
 | ||||
|             // If the user plays the SCORM in online we don't know if he sent data or not, so always check completion.
 | ||||
|         if (result.updated || this.dataSent) { | ||||
|             // Check completion status if something was sent.
 | ||||
|             this.checkCompletion(); | ||||
|         } | ||||
| 
 | ||||
|         this.syncDueToPlayerLeft = false; | ||||
|         this.dataSent = false; | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| @ -349,11 +349,13 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom | ||||
| 
 | ||||
|         if (this.hasPlayed) { | ||||
|             this.hasPlayed = false; | ||||
|             this.syncDueToPlayerLeft = true; | ||||
|             this.scormOptions.newAttempt = false; // Uncheck new attempt.
 | ||||
| 
 | ||||
|             // Add a delay to make sure the player has started the last writing calls so we can detect conflicts.
 | ||||
|             setTimeout(() => { | ||||
|                 this.dataSentObserver && this.dataSentObserver.off(); // Stop listening for changes.
 | ||||
|                 this.dataSentObserver = undefined; | ||||
| 
 | ||||
|                 // Refresh data.
 | ||||
|                 this.showLoadingAndRefresh(true, false); | ||||
|             }, 500); | ||||
| @ -368,6 +370,15 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom | ||||
| 
 | ||||
|         if (this.navCtrl.getActive().component.name == 'AddonModScormPlayerPage') { | ||||
|             this.hasPlayed = true; | ||||
| 
 | ||||
|             // Detect if anything was sent to server.
 | ||||
|             this.dataSentObserver && this.dataSentObserver.off(); | ||||
| 
 | ||||
|             this.dataSentObserver = this.eventsProvider.on(AddonModScormProvider.DATA_SENT_EVENT, (data) => { | ||||
|                 if (data.scormId === this.scorm.id) { | ||||
|                     this.dataSent = true; | ||||
|                 } | ||||
|             }, this.siteId); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -533,6 +544,17 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     protected sync(): Promise<any> { | ||||
|         return this.scormSync.syncScorm(this.scorm); | ||||
|         return this.scormSync.syncScorm(this.scorm).then((result) => { | ||||
|             if (!result.updated && this.dataSent) { | ||||
|                 // The user sent data to server, but not in the sync process. Check if we need to fetch data.
 | ||||
|                 return this.scormSync.prefetchAfterUpdate(this.module, this.courseId).catch(() => { | ||||
|                     // Ignore errors.
 | ||||
|                 }).then(() => { | ||||
|                     return result; | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             return result; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -23,6 +23,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text'; | ||||
| import { CoreTimeUtilsProvider } from '@providers/utils/time'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| import { CoreCourseProvider } from '@core/course/providers/course'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; | ||||
| import { CoreSyncBaseProvider } from '@classes/base-sync'; | ||||
| import { AddonModScormProvider, AddonModScormAttemptCountResult } from './scorm'; | ||||
| import { AddonModScormOfflineProvider } from './scorm-offline'; | ||||
| @ -63,9 +64,10 @@ export class AddonModScormSyncProvider extends CoreSyncBaseProvider { | ||||
| 
 | ||||
|     constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, | ||||
|             syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, | ||||
|             courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider, | ||||
|             private eventsProvider: CoreEventsProvider, timeUtils: CoreTimeUtilsProvider, | ||||
|             private scormProvider: AddonModScormProvider, private scormOfflineProvider: AddonModScormOfflineProvider, | ||||
|             private prefetchHandler: AddonModScormPrefetchHandler, private utils: CoreUtilsProvider) { | ||||
|             private prefetchHandler: AddonModScormPrefetchHandler, private utils: CoreUtilsProvider, | ||||
|             private prefetchDelegate: CoreCourseModulePrefetchDelegate, private courseProvider: CoreCourseProvider) { | ||||
| 
 | ||||
|         super('AddonModScormSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, | ||||
|                 timeUtils); | ||||
| @ -190,11 +192,11 @@ export class AddonModScormSyncProvider extends CoreSyncBaseProvider { | ||||
|         let promise; | ||||
| 
 | ||||
|         if (updated) { | ||||
|             // Update the WS data.
 | ||||
|             promise = this.scormProvider.invalidateAllScormData(scorm.id, siteId).catch(() => { | ||||
|             // Update downloaded data.
 | ||||
|             promise = this.courseProvider.getModuleBasicInfoByInstance(scorm.id, 'scorm', siteId).then((module) => { | ||||
|                 return this.prefetchAfterUpdate(module, scorm.course); | ||||
|             }).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }).then(() => { | ||||
|                 return this.prefetchHandler.fetchWSData(scorm, siteId); | ||||
|             }); | ||||
|         } else { | ||||
|             promise = Promise.resolve(); | ||||
| @ -357,6 +359,31 @@ export class AddonModScormSyncProvider extends CoreSyncBaseProvider { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Prefetch data after an update. It won't prefetch the data if the package file was updated. | ||||
|      * | ||||
|      * @param {any} module Module. | ||||
|      * @param {number} courseId Course ID. | ||||
|      * @param {string} [siteId] Site ID. If not defined, current site. | ||||
|      * @return {Promise<any>} Promise resolved when done. | ||||
|      */ | ||||
|     prefetchAfterUpdate(module: any, courseId: number, siteId?: string): Promise<any> { | ||||
|         // Get the module updates to check if the package was updated or not.
 | ||||
|         return this.prefetchDelegate.getModuleUpdates(module, courseId, true, siteId).then((result) => { | ||||
| 
 | ||||
|             if (result && result.updates) { | ||||
|                 // Only prefetch if the package file hasn't changed.
 | ||||
|                 const fileChanged = !!result.updates.find((entry) => { | ||||
|                     return entry.name == 'packagefiles'; | ||||
|                 }); | ||||
| 
 | ||||
|                 if (!fileChanged) { | ||||
|                     return this.prefetchHandler.download(module, courseId); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Save a snapshot from a synchronization. | ||||
|      * | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreEventsProvider } from '@providers/events'; | ||||
| import { CoreFilepoolProvider } from '@providers/filepool'; | ||||
| import { CoreLoggerProvider } from '@providers/logger'; | ||||
| import { CoreSitesProvider } from '@providers/sites'; | ||||
| @ -86,6 +87,7 @@ export class AddonModScormProvider { | ||||
|     static LAUNCH_PREV_SCO_EVENT = 'addon_mod_scorm_launch_prev_sco'; | ||||
|     static UPDATE_TOC_EVENT = 'addon_mod_scorm_update_toc'; | ||||
|     static GO_OFFLINE_EVENT = 'addon_mod_scorm_go_offline'; | ||||
|     static DATA_SENT_EVENT = 'addon_mod_scorm_data_sent'; | ||||
| 
 | ||||
|     // Protected constants.
 | ||||
|     protected VALID_STATUSES = ['notattempted', 'passed', 'completed', 'failed', 'incomplete', 'browsed', 'suspend']; | ||||
| @ -110,7 +112,8 @@ export class AddonModScormProvider { | ||||
|     constructor(logger: CoreLoggerProvider, private translate: TranslateService, private sitesProvider: CoreSitesProvider, | ||||
|             private wsProvider: CoreWSProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider, | ||||
|             private filepoolProvider: CoreFilepoolProvider, private scormOfflineProvider: AddonModScormOfflineProvider, | ||||
|             private timeUtils: CoreTimeUtilsProvider, private syncProvider: CoreSyncProvider) { | ||||
|             private timeUtils: CoreTimeUtilsProvider, private syncProvider: CoreSyncProvider, | ||||
|             private eventsProvider: CoreEventsProvider) { | ||||
|         this.logger = logger.getInstance('AddonModScormProvider'); | ||||
|     } | ||||
| 
 | ||||
| @ -1483,6 +1486,12 @@ export class AddonModScormProvider { | ||||
|             return this.saveTracksOnline(scorm.id, scoId, attempt, tracks, siteId).then(() => { | ||||
|                 // Tracks have been saved, update cached user data.
 | ||||
|                 this.updateUserDataAfterSave(scorm.id, attempt, tracks, siteId); | ||||
| 
 | ||||
|                 this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { | ||||
|                     scormId: scorm.id, | ||||
|                     scoId: scoId, | ||||
|                     attempt: attempt | ||||
|                 }, this.sitesProvider.getCurrentSiteId()); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @ -1546,6 +1555,12 @@ export class AddonModScormProvider { | ||||
|             if (success) { | ||||
|                 // Tracks have been saved, update cached user data.
 | ||||
|                 this.updateUserDataAfterSave(scorm.id, attempt, tracks); | ||||
| 
 | ||||
|                 this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { | ||||
|                     scormId: scorm.id, | ||||
|                     scoId: scoId, | ||||
|                     attempt: attempt | ||||
|                 }, this.sitesProvider.getCurrentSiteId()); | ||||
|             } | ||||
| 
 | ||||
|             return success; | ||||
|  | ||||
| @ -826,7 +826,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a module status and download time. It will only return the download time if the module is downloaded and not outdated. | ||||
|      * Get a module status and download time. It will only return the download time if the module is downloaded or outdated. | ||||
|      * | ||||
|      * @param {any} module Module. | ||||
|      * @param {number} courseId Course ID the module belongs to. | ||||
| @ -841,8 +841,8 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { | ||||
|             const packageId = this.filepoolProvider.getPackageId(handler.component, module.id), | ||||
|                 status = this.statusCache.getValue(packageId, 'status'); | ||||
| 
 | ||||
|             if (typeof status != 'undefined' && status != CoreConstants.DOWNLOADED) { | ||||
|                 // Status is different than downloaded, just return the status.
 | ||||
|             if (typeof status != 'undefined' && status != CoreConstants.DOWNLOADED && status != CoreConstants.OUTDATED) { | ||||
|                 // Module isn't downloaded, just return the status.
 | ||||
|                 return Promise.resolve({ | ||||
|                     status: status | ||||
|                 }); | ||||
| @ -872,6 +872,75 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get updates for a certain module. | ||||
|      * It will only return the updates if the module can use check updates and it's downloaded or outdated. | ||||
|      * | ||||
|      * @param {any} module Module to check. | ||||
|      * @param {number} courseId Course the module belongs to. | ||||
|      * @param {boolean} [ignoreCache] True if it should ignore cached data (it will always fail in offline or server down). | ||||
|      * @param {string} [siteId] Site ID. If not defined, current site. | ||||
|      * @return {Promise<any>} Promise resolved with the updates. | ||||
|      */ | ||||
|     getModuleUpdates(module: any, courseId: number, ignoreCache?: boolean, siteId?: string): Promise<any> { | ||||
| 
 | ||||
|         return this.sitesProvider.getSite(siteId).then((site) => { | ||||
|             // Get the status and download time of the module.
 | ||||
|             return this.getModuleStatusAndDownloadTime(module, courseId).then((data) => { | ||||
|                 if (data.status != CoreConstants.DOWNLOADED && data.status != CoreConstants.OUTDATED) { | ||||
|                     // Not downloaded, no updates.
 | ||||
|                     return {}; | ||||
|                 } | ||||
| 
 | ||||
|                 // Module is downloaded. Check if it can check updates.
 | ||||
|                 return this.canModuleUseCheckUpdates(module, courseId).then((canUse) => { | ||||
|                     if (!canUse) { | ||||
|                         // Can't use check updates, no updates.
 | ||||
|                         return {}; | ||||
|                     } | ||||
| 
 | ||||
|                     const params = { | ||||
|                             courseid: courseId, | ||||
|                             tocheck: [ | ||||
|                                 { | ||||
|                                     contextlevel: 'module', | ||||
|                                     id: module.id, | ||||
|                                     since: data.downloadTime || 0 | ||||
|                                 } | ||||
|                             ] | ||||
|                         }, | ||||
|                         preSets: CoreSiteWSPreSets = { | ||||
|                             cacheKey: this.getModuleUpdatesCacheKey(courseId, module.id), | ||||
|                         }; | ||||
| 
 | ||||
|                     if (ignoreCache) { | ||||
|                         preSets.getFromCache = false; | ||||
|                         preSets.emergencyCache = false; | ||||
|                     } | ||||
| 
 | ||||
|                     return site.read('core_course_check_updates', params, preSets).then((response) => { | ||||
|                         if (!response || !response.instances || !response.instances[0]) { | ||||
|                             return Promise.reject(null); | ||||
|                         } | ||||
| 
 | ||||
|                         return response.instances[0]; | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get cache key for module updates WS calls. | ||||
|      * | ||||
|      * @param {number} courseId Course ID. | ||||
|      * @param {number} moduleId Module ID. | ||||
|      * @return {string} Cache key. | ||||
|      */ | ||||
|     protected getModuleUpdatesCacheKey(courseId: number, moduleId: number): string { | ||||
|         return this.getCourseUpdatesCacheKey(courseId) + ':' + moduleId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get a prefetch handler. | ||||
|      * | ||||
| @ -933,6 +1002,20 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Invalidate check updates WS call for a certain module. | ||||
|      * | ||||
|      * @param {number} courseId Course ID. | ||||
|      * @param {number} moduleId Module ID. | ||||
|      * @param {string} [siteId] Site ID. If not defined, current site. | ||||
|      * @return {Promise<any>} Promise resolved when data is invalidated. | ||||
|      */ | ||||
|     invalidateModuleUpdates(courseId: number, moduleId: number, siteId?: string): Promise<any> { | ||||
|         return this.sitesProvider.getSite(siteId).then((site) => { | ||||
|             return site.invalidateWsCacheForKey(this.getModuleUpdatesCacheKey(courseId, moduleId)); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if a list of modules is being downloaded. | ||||
|      * | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user