From f284ab7c733294a8f76798ebbd4950b61fe987a6 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 25 May 2020 10:22:09 +0200 Subject: [PATCH] MOBILE-3401 courses: Refresh only right data on MY_COURSES_UPDATED --- .../components/myoverview/myoverview.ts | 36 +++++++--- .../recentlyaccessedcourses.ts | 70 ++++++++++++++++++- .../starredcourses/starredcourses.ts | 47 ++++++++++++- src/core/course/providers/course.ts | 6 +- .../course-progress/course-progress.ts | 18 +++-- .../components/my-courses/my-courses.ts | 11 ++- .../pages/course-preview/course-preview.ts | 7 +- src/core/courses/providers/courses.ts | 20 ++++++ 8 files changed, 190 insertions(+), 25 deletions(-) diff --git a/src/addon/block/myoverview/components/myoverview/myoverview.ts b/src/addon/block/myoverview/components/myoverview/myoverview.ts index 0f3cce93b..3ed75d517 100644 --- a/src/addon/block/myoverview/components/myoverview/myoverview.ts +++ b/src/addon/block/myoverview/components/myoverview/myoverview.ts @@ -17,7 +17,7 @@ import { Searchbar } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; @@ -112,8 +112,12 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem }, this.sitesProvider.getCurrentSiteId()); - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { - this.refreshContent(); + this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + (data: CoreCoursesMyCoursesUpdatedEventData) => { + + if (data.action == CoreCoursesProvider.ACTION_ENROL || data.action == CoreCoursesProvider.ACTION_STATE_CHANGED) { + this.refreshCourseList(); + } }, this.sitesProvider.getCurrentSiteId()); this.currentSite = this.sitesProvider.getCurrentSite(); @@ -151,12 +155,9 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem promises.push(this.coursesProvider.invalidateUserCourses().finally(() => { // Invalidate course completion data. - promises.push(this.coursesProvider.invalidateUserCourses().finally(() => { - // Invalidate course completion data. - return this.utils.allPromises(this.courseIds.map((courseId) => { - return this.courseCompletionProvider.invalidateCourseCompletion(courseId); - })); - })); + return this.utils.allPromises(this.courseIds.map((courseId) => { + return this.courseCompletionProvider.invalidateCourseCompletion(courseId); + })); })); promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); @@ -318,6 +319,23 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem }); } + /** + * Refresh the list of courses. + * + * @return Promise resolved when done. + */ + protected async refreshCourseList(): Promise { + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); + + try { + await this.coursesProvider.invalidateUserCourses(); + } catch (error) { + // Ignore errors. + } + + await this.loadContent(true); + } + /** * The selected courses filter have changed. */ diff --git a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts b/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts index 4b61bfeb1..a1eb249ce 100644 --- a/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts +++ b/src/addon/block/recentlyaccessedcourses/components/recentlyaccessedcourses/recentlyaccessedcourses.ts @@ -15,7 +15,7 @@ import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; @@ -72,8 +72,12 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom }, this.sitesProvider.getCurrentSiteId()); - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { - this.refreshContent(); + this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + (data: CoreCoursesMyCoursesUpdatedEventData) => { + + if (this.shouldRefreshOnUpdatedEvent(data)) { + this.refreshCourseList(); + } }, this.sitesProvider.getCurrentSiteId()); super.ngOnInit(); @@ -130,6 +134,23 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom }); } + /** + * Refresh the list of courses. + * + * @return Promise resolved when done. + */ + protected async refreshCourseList(): Promise { + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); + + try { + await this.coursesProvider.invalidateUserCourses(); + } catch (error) { + // Ignore errors. + } + + await this.loadContent(true); + } + /** * Initialize the prefetch icon for selected courses. */ @@ -146,6 +167,49 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom }); } + /** + * Whether list should be refreshed based on a EVENT_MY_COURSES_UPDATED event. + * + * @param data Event data. + * @return Whether to refresh. + */ + protected shouldRefreshOnUpdatedEvent(data: CoreCoursesMyCoursesUpdatedEventData): boolean { + if (data.action == CoreCoursesProvider.ACTION_ENROL) { + // Always update if user enrolled in a course. + return true; + } + + if (data.action == CoreCoursesProvider.ACTION_VIEW && data.courseId != this.sitesProvider.getCurrentSiteHomeId() && + this.courses[0] && data.courseId != this.courses[0].id) { + // Update list if user viewed a course that isn't the most recent one and isn't site home. + return true; + } + + if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && data.state == CoreCoursesProvider.STATE_FAVOURITE && + this.hasCourse(data.courseId)) { + // Update list if a visible course is now favourite or unfavourite. + return true; + } + + return false; + } + + /** + * Check if a certain course is in the list of courses. + * + * @param courseId Course ID to search. + * @return Whether it's in the list. + */ + protected hasCourse(courseId: number): boolean { + if (!this.courses) { + return false; + } + + return !!this.courses.find((course) => { + return course.id == courseId; + }); + } + /** * Prefetch all the shown courses. * diff --git a/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts b/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts index 8acddb2a2..0fc513209 100644 --- a/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts +++ b/src/addon/block/starredcourses/components/starredcourses/starredcourses.ts @@ -15,7 +15,7 @@ import { Component, OnInit, OnDestroy, Injector, Input, OnChanges, SimpleChange } from '@angular/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '@core/courses/providers/courses'; import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; @@ -72,7 +72,12 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im }, this.sitesProvider.getCurrentSiteId()); - this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { + this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + (data: CoreCoursesMyCoursesUpdatedEventData) => { + + if (this.shouldRefreshOnUpdatedEvent(data)) { + this.refreshCourseList(); + } this.refreshContent(); }, this.sitesProvider.getCurrentSiteId()); @@ -130,6 +135,44 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im }); } + /** + * Refresh the list of courses. + * + * @return Promise resolved when done. + */ + protected async refreshCourseList(): Promise { + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); + + try { + await this.coursesProvider.invalidateUserCourses(); + } catch (error) { + // Ignore errors. + } + + await this.loadContent(true); + } + + /** + * Whether list should be refreshed based on a EVENT_MY_COURSES_UPDATED event. + * + * @param data Event data. + * @return Whether to refresh. + */ + protected shouldRefreshOnUpdatedEvent(data: CoreCoursesMyCoursesUpdatedEventData): boolean { + if (data.action == CoreCoursesProvider.ACTION_ENROL) { + // Always update if user enrolled in a course. + // New courses shouldn't be favourite by default, but just in case. + return true; + } + + if (data.action == CoreCoursesProvider.ACTION_STATE_CHANGED && data.state == CoreCoursesProvider.STATE_FAVOURITE) { + // Update list when making a course favourite or not. + return true; + } + + return false; + } + /** * Initialize the prefetch icon for selected courses. */ diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index 09bcca98c..1facbf99f 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -862,8 +862,10 @@ export class CoreCourseProvider { if (!response.status) { return Promise.reject(null); } else { - this.eventsProvider.trigger( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {course: courseId}, site.getId()); + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + courseId: courseId, + action: CoreCoursesProvider.ACTION_VIEW, + }, site.getId()); } }); }); diff --git a/src/core/courses/components/course-progress/course-progress.ts b/src/core/courses/components/course-progress/course-progress.ts index 60dc67d30..cafc7c142 100644 --- a/src/core/courses/components/course-progress/course-progress.ts +++ b/src/core/courses/components/course-progress/course-progress.ts @@ -218,8 +218,13 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { // We should use null to unset the preference. this.userProvider.updateUserPreference('block_myoverview_hidden_course_' + this.course.id, hide ? 1 : null).then(() => { this.course.hidden = hide; - this.eventsProvider.trigger( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {course: this.course}, this.sitesProvider.getCurrentSiteId()); + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + courseId: this.course.id, + course: this.course, + action: CoreCoursesProvider.ACTION_STATE_CHANGED, + state: CoreCoursesProvider.STATE_HIDDEN, + value: hide, + }, this.sitesProvider.getCurrentSiteId()); }).catch((error) => { if (!this.isDestroyed) { this.domUtils.showErrorModalDefault(error, 'Error changing course visibility.'); @@ -239,8 +244,13 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { this.coursesProvider.setFavouriteCourse(this.course.id, favourite).then(() => { this.course.isfavourite = favourite; - this.eventsProvider.trigger( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {course: this.course}, this.sitesProvider.getCurrentSiteId()); + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + courseId: this.course.id, + course: this.course, + action: CoreCoursesProvider.ACTION_STATE_CHANGED, + state: CoreCoursesProvider.STATE_FAVOURITE, + value: favourite, + }, this.sitesProvider.getCurrentSiteId()); }).catch((error) => { if (!this.isDestroyed) { this.domUtils.showErrorModalDefault(error, 'Error changing course favourite attribute.'); diff --git a/src/core/courses/components/my-courses/my-courses.ts b/src/core/courses/components/my-courses/my-courses.ts index 1c51b5b31..0476a6ae3 100644 --- a/src/core/courses/components/my-courses/my-courses.ts +++ b/src/core/courses/components/my-courses/my-courses.ts @@ -17,7 +17,7 @@ import { Searchbar } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCoursesProvider } from '../../providers/courses'; +import { CoreCoursesProvider, CoreCoursesMyCoursesUpdatedEventData } from '../../providers/courses'; import { CoreCoursesHelperProvider } from '../../providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; @@ -63,8 +63,13 @@ export class CoreCoursesMyCoursesComponent implements OnInit, OnDestroy { this.coursesLoaded = true; }); - this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { - this.fetchCourses(); + // Update list if user enrols in a course. + this.myCoursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, + (data: CoreCoursesMyCoursesUpdatedEventData) => { + + if (data.action == CoreCoursesProvider.ACTION_ENROL) { + this.fetchCourses(); + } }, this.sitesProvider.getCurrentSiteId()); // Refresh the enabled flags if site is updated. diff --git a/src/core/courses/pages/course-preview/course-preview.ts b/src/core/courses/pages/course-preview/course-preview.ts index 639b4d52f..4ca797ecf 100644 --- a/src/core/courses/pages/course-preview/course-preview.ts +++ b/src/core/courses/pages/course-preview/course-preview.ts @@ -365,8 +365,11 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy { this.waitForEnrolled(true).then(() => { this.refreshData().finally(() => { // My courses have been updated, trigger event. - this.eventsProvider.trigger( - CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {course: this.course}, this.sitesProvider.getCurrentSiteId()); + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, { + courseId: this.course.id, + course: this.course, + action: CoreCoursesProvider.ACTION_ENROL, + }, this.sitesProvider.getCurrentSiteId()); }); }); }).catch((error) => { diff --git a/src/core/courses/providers/courses.ts b/src/core/courses/providers/courses.ts index b0529d926..2ef00a33c 100644 --- a/src/core/courses/providers/courses.ts +++ b/src/core/courses/providers/courses.ts @@ -18,6 +18,17 @@ import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSite } from '@classes/site'; +/** + * Data sent to the EVENT_MY_COURSES_UPDATED. + */ +export type CoreCoursesMyCoursesUpdatedEventData = { + action: string; // Action performed. + courseId?: number; // Course ID affected (if any). + course?: any; // Course affected (if any). + state?: string; // Only for ACTION_STATE_CHANGED. The state that changed (hidden, favourite). + value?: boolean; // The new value for the state changed. +}; + /** * Service that provides some features regarding lists of courses and categories. */ @@ -30,6 +41,15 @@ export class CoreCoursesProvider { static EVENT_MY_COURSES_REFRESHED = 'courses_my_courses_refreshed'; static EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED = 'dashboard_download_enabled_changed'; + // Actions for event EVENT_MY_COURSES_UPDATED. + static ACTION_ENROL = 'enrol'; // User enrolled in a course. + static ACTION_STATE_CHANGED = 'state_changed'; // Course state changed (hidden, favourite). + static ACTION_VIEW = 'view'; // Course viewed. + + // Possible states changed. + static STATE_HIDDEN = 'hidden'; + static STATE_FAVOURITE = 'favourite'; + protected ROOT_CACHE_KEY = 'mmCourses:'; protected logger; protected userCoursesIds: {[id: number]: boolean}; // Use an object to make it faster to search.