From 9c1be97635113615da3b124bb9b65e1507974019 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Tue, 5 Mar 2019 13:31:11 +0100 Subject: [PATCH] MOBILE-2485 data: Ratings --- .../index/addon-mod-data-index.html | 2 +- src/addon/mod/data/components/index/index.ts | 43 +++++++++++- src/addon/mod/data/pages/entry/entry.html | 3 + .../mod/data/pages/entry/entry.module.ts | 4 +- src/addon/mod/data/pages/entry/entry.ts | 11 ++++ src/addon/mod/data/providers/data.ts | 8 ++- src/addon/mod/data/providers/helper.ts | 2 +- .../mod/data/providers/prefetch-handler.ts | 10 ++- src/addon/mod/data/providers/sync.ts | 66 +++++++++++++++++-- 9 files changed, 137 insertions(+), 12 deletions(-) diff --git a/src/addon/mod/data/components/index/addon-mod-data-index.html b/src/addon/mod/data/components/index/addon-mod-data-index.html index 62d8c38a0..acc54a271 100644 --- a/src/addon/mod/data/components/index/addon-mod-data-index.html +++ b/src/addon/mod/data/components/index/addon-mod-data-index.html @@ -22,7 +22,7 @@ -
+
{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}
diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts index 9da9180fe..68bdccef6 100644 --- a/src/addon/mod/data/components/index/index.ts +++ b/src/addon/mod/data/components/index/index.ts @@ -19,6 +19,9 @@ import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; import { CoreCommentsProvider } from '@core/comments/providers/comments'; +import { CoreRatingProvider } from '@core/rating/providers/rating'; +import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; +import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; import { AddonModDataProvider } from '../../providers/data'; import { AddonModDataHelperProvider } from '../../providers/helper'; import { AddonModDataOfflineProvider } from '../../providers/offline'; @@ -74,11 +77,16 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp protected hasComments = false; protected fieldsArray: any; + hasOfflineRatings: boolean; + protected ratingOfflineObserver: any; + protected ratingSyncObserver: any; + constructor(injector: Injector, private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider, private dataOffline: AddonModDataOfflineProvider, @Optional() content: Content, private dataSync: AddonModDataSyncProvider, private timeUtils: CoreTimeUtilsProvider, private groupsProvider: CoreGroupsProvider, private commentsProvider: CoreCommentsProvider, - private modalCtrl: ModalController, private utils: CoreUtilsProvider, protected navCtrl: NavController) { + private modalCtrl: ModalController, private utils: CoreUtilsProvider, protected navCtrl: NavController, + private ratingOffline: CoreRatingOfflineProvider) { super(injector, content); // Refresh entries on change. @@ -89,7 +97,22 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp return this.loadContent(true); } }, this.siteId); + + // Listen for offline ratings saved and synced. + this.ratingOfflineObserver = this.eventsProvider.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => { + if (this.data && data.component == 'mod_data' && data.ratingArea == 'entry' && data.contextLevel == 'module' + && data.instanceId == this.data.coursemodule) { + this.hasOfflineRatings = true; + } + }); + this.ratingSyncObserver = this.eventsProvider.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => { + if (this.data && data.component == 'mod_data' && data.ratingArea == 'entry' && data.contextLevel == 'module' + && data.instanceId == this.data.coursemodule) { + this.hasOfflineRatings = false; + } + }); } + /** * Component being initialized. */ @@ -487,6 +510,10 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp } }); } + }).then(() => { + return this.ratingOffline.hasRatings('mod_data', 'entry', 'module', this.data.coursemodule).then((hasRatings) => { + this.hasOfflineRatings = hasRatings; + }); }); } @@ -496,7 +523,17 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp * @return {Promise} Promise resolved when done. */ protected sync(): Promise { - return this.dataSync.syncDatabase(this.data.id); + const promises = [ + this.dataSync.syncDatabase(this.data.id), + this.dataSync.syncRatings(this.data.coursemodule) + ]; + + return Promise.all(promises).then((results) => { + return results.reduce((a, b) => ({ + updated: a.updated || b.updated, + warnings: (a.warnings || []).concat(b.warnings || []), + }), {updated: false}); + }); } /** @@ -515,5 +552,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp ngOnDestroy(): void { super.ngOnDestroy(); this.entryChangedObserver && this.entryChangedObserver.off(); + this.ratingOfflineObserver && this.ratingOfflineObserver.off(); + this.ratingSyncObserver && this.ratingSyncObserver.off(); } } diff --git a/src/addon/mod/data/pages/entry/entry.html b/src/addon/mod/data/pages/entry/entry.html index a49acdf57..3cc182e48 100644 --- a/src/addon/mod/data/pages/entry/entry.html +++ b/src/addon/mod/data/pages/entry/entry.html @@ -30,6 +30,9 @@
+ + + diff --git a/src/addon/mod/data/pages/entry/entry.module.ts b/src/addon/mod/data/pages/entry/entry.module.ts index cebf202e2..eb77745c6 100644 --- a/src/addon/mod/data/pages/entry/entry.module.ts +++ b/src/addon/mod/data/pages/entry/entry.module.ts @@ -19,6 +19,7 @@ import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreComponentsModule } from '@components/components.module'; import { CoreCommentsComponentsModule } from '@core/comments/components/components.module'; import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; +import { CoreRatingComponentsModule } from '@core/rating/components/components.module'; import { AddonModDataComponentsModule } from '../../components/components.module'; import { AddonModDataEntryPage } from './entry'; @@ -33,7 +34,8 @@ import { AddonModDataEntryPage } from './entry'; CoreCompileHtmlComponentModule, CoreCommentsComponentsModule, IonicPageModule.forChild(AddonModDataEntryPage), - TranslateModule.forChild() + TranslateModule.forChild(), + CoreRatingComponentsModule ], }) export class AddonModDataEntryPageModule {} diff --git a/src/addon/mod/data/pages/entry/entry.ts b/src/addon/mod/data/pages/entry/entry.ts index 16a35f39e..d1b26a8f1 100644 --- a/src/addon/mod/data/pages/entry/entry.ts +++ b/src/addon/mod/data/pages/entry/entry.ts @@ -20,6 +20,7 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreGroupsProvider } from '@providers/groups'; import { CoreEventsProvider } from '@providers/events'; import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreRatingInfo } from '@core/rating/providers/rating'; import { AddonModDataProvider } from '../../providers/data'; import { AddonModDataHelperProvider } from '../../providers/helper'; import { AddonModDataOfflineProvider } from '../../providers/offline'; @@ -66,6 +67,7 @@ export class AddonModDataEntryPage implements OnDestroy { cssClass = ''; extraImports = [AddonModDataComponentsModule]; jsData; + ratingInfo: CoreRatingInfo; constructor(params: NavParams, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider, protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate, @@ -162,7 +164,9 @@ export class AddonModDataEntryPage implements OnDestroy { return this.dataHelper.getEntry(this.data, this.entryId, this.offlineActions); }); }).then((entry) => { + this.ratingInfo = entry.ratinginfo; entry = entry.entry; + this.cssTemplate = this.dataHelper.prefixCSS(this.data.csstemplate, '.' + this.cssClass); // Index contents by fieldid. @@ -311,6 +315,13 @@ export class AddonModDataEntryPage implements OnDestroy { }); } + /** + * Function called when rating is updated online. + */ + ratingUpdated(): void { + this.dataProvider.invalidateEntryData(this.data.id, this.entryId); + } + /** * Component being destroyed. */ diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts index 8ab7f43e2..a50cf5156 100644 --- a/src/addon/mod/data/providers/data.ts +++ b/src/addon/mod/data/providers/data.ts @@ -652,10 +652,11 @@ export class AddonModDataProvider { * * @param {number} dataId Data ID for caching purposes. * @param {number} entryId Entry ID. + * @param {boolean} [ignoreCache=false] True if it should ignore cached data (it'll always fail in offline or server down). * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the database entry is retrieved. */ - getEntry(dataId: number, entryId: number, siteId?: string): Promise { + getEntry(dataId: number, entryId: number, ignoreCache: boolean = false, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { const params = { entryid: entryId, @@ -665,6 +666,11 @@ export class AddonModDataProvider { cacheKey: this.getEntryCacheKey(dataId, entryId) }; + if (ignoreCache) { + preSets['getFromCache'] = false; + preSets['emergencyCache'] = false; + } + return site.read('mod_data_get_entry', params, preSets); }); } diff --git a/src/addon/mod/data/providers/helper.ts b/src/addon/mod/data/providers/helper.ts index a94879196..29d6435c4 100644 --- a/src/addon/mod/data/providers/helper.ts +++ b/src/addon/mod/data/providers/helper.ts @@ -354,7 +354,7 @@ export class AddonModDataHelperProvider { getEntry(data: any, entryId: number, offlineActions?: any, siteId?: string): Promise { if (entryId > 0) { // It's an online entry, get it from WS. - return this.dataProvider.getEntry(data.id, entryId, siteId); + return this.dataProvider.getEntry(data.id, entryId, false, siteId); } // It's an offline entry, search it in the offline actions. diff --git a/src/addon/mod/data/providers/prefetch-handler.ts b/src/addon/mod/data/providers/prefetch-handler.ts index b39b6b204..85c54d10e 100644 --- a/src/addon/mod/data/providers/prefetch-handler.ts +++ b/src/addon/mod/data/providers/prefetch-handler.ts @@ -24,6 +24,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreCommentsProvider } from '@core/comments/providers/comments'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; +import { CoreRatingProvider } from '@core/rating/providers/rating'; import { AddonModDataProvider } from './data'; import { AddonModDataHelperProvider } from './helper'; @@ -41,7 +42,8 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected dataProvider: AddonModDataProvider, protected timeUtils: CoreTimeUtilsProvider, protected dataHelper: AddonModDataHelperProvider, - protected groupsProvider: CoreGroupsProvider, protected commentsProvider: CoreCommentsProvider) { + protected groupsProvider: CoreGroupsProvider, protected commentsProvider: CoreCommentsProvider, + private ratingProvider: CoreRatingProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -282,7 +284,11 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl }); info.entries.forEach((entry) => { - promises.push(this.dataProvider.getEntry(database.id, entry.id, siteId)); + promises.push(this.dataProvider.getEntry(database.id, entry.id, true, siteId).then((entry) => { + return this.ratingProvider.prefetchRatings('module', module.id, database.scale, courseId, entry.ratinginfo, + siteId); + })); + if (database.comments) { promises.push(this.commentsProvider.getComments('module', database.coursemodule, 'mod_data', entry.id, 'database_entry', 0, siteId)); diff --git a/src/addon/mod/data/providers/sync.ts b/src/addon/mod/data/providers/sync.ts index 7cf764845..aeb16b190 100644 --- a/src/addon/mod/data/providers/sync.ts +++ b/src/addon/mod/data/providers/sync.ts @@ -28,6 +28,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreSyncProvider } from '@providers/sync'; +import { CoreRatingSyncProvider } from '@core/rating/providers/sync'; /** * Service to sync databases. @@ -43,7 +44,8 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { private eventsProvider: CoreEventsProvider, private dataProvider: AddonModDataProvider, protected translate: TranslateService, private utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider, timeUtils: CoreTimeUtilsProvider, - private dataHelper: AddonModDataHelperProvider, private logHelper: CoreCourseLogHelperProvider) { + private dataHelper: AddonModDataHelperProvider, private logHelper: CoreCourseLogHelperProvider, + private ratingSync: CoreRatingSyncProvider) { super('AddonModDataSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); @@ -77,8 +79,12 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ protected syncAllDatabasesFunc(siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + const promises = []; + // Get all data answers pending to be sent in the site. - return this.dataOffline.getAllEntries(siteId).then((offlineActions) => { + promises.push(this.dataOffline.getAllEntries(siteId).then((offlineActions) => { const promises = {}; // Do not sync same database twice. @@ -101,7 +107,11 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { // Promises will be an object so, convert to an array first; return Promise.all(this.utils.objectToArray(promises)); - }); + })); + + promises.push(this.syncRatings(undefined, siteId)); + + return Promise.all(promises); } /** @@ -232,7 +242,7 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { entryId = entryActions[0].entryid; if (entryId > 0) { - timePromise = this.dataProvider.getEntry(data.id, entryId, siteId).then((entry) => { + timePromise = this.dataProvider.getEntry(data.id, entryId, false, siteId).then((entry) => { return entry.entry.timemodified; }).catch(() => { return -1; @@ -343,4 +353,52 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { }); } + /** + * Synchronize offline ratings. + * + * @param {number} [cmId] Course module to be synced. If not defined, sync all databases. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved if sync is successful, rejected otherwise. + */ + syncRatings(cmId?: number, siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + return this.ratingSync.syncRatings('mod_data', 'entry', 'module', cmId, 0, siteId).then((results) => { + let updated = false; + const warnings = []; + const promises = []; + + results.forEach((result) => { + promises.push(this.dataProvider.getDatabase(result.itemSet.courseId, result.itemSet.instanceId, siteId) + .then((data) => { + const promises = []; + + if (result.updated.length) { + updated = true; + + // Invalidate entry of updated ratings. + result.updated.forEach((itemId) => { + promises.push(this.dataProvider.invalidateEntryData(data.id, itemId, siteId)); + }); + } + + if (result.warnings.length) { + result.warnings.forEach((warning) => { + warnings.push(this.translate.instant('core.warningofflinedatadeleted', { + component: this.componentTranslate, + name: data.name, + error: warning + })); + }); + } + + return this.utils.allPromises(promises); + })); + }); + + return Promise.all(promises).then(() => { + return { updated, warnings }; + }); + }); + } }