2
0
Fork 0

Merge pull request #2382 from dpalou/MOBILE-3401

Mobile 3401
main
Juan Leyva 2020-05-25 11:39:36 +02:00 committed by GitHub
commit 2752580203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 201 additions and 30 deletions

View File

@ -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<void> {
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.
*/

View File

@ -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<void> {
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.
*

View File

@ -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<void> {
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.
*/

View File

@ -5,8 +5,8 @@
<ng-container *ngFor="let event of dayEvents.events">
<a ion-item text-wrap detail-none class="core-course-module-handler item-media" (click)="action($event, event.url)" [title]="event.name">
<img item-start [src]="event.iconUrl" alt="" role="presentation" *ngIf="event.iconUrl" class="core-module-icon">
<h2><core-format-text [text]="event.name" contextLevel="module" [contextInstanceId]="event.id" [courseId]="event.course.id"></core-format-text></h2>
<p *ngIf="showCourse">
<h2><core-format-text [text]="event.name" contextLevel="module" [contextInstanceId]="event.id" [courseId]="event.course && event.course.id"></core-format-text></h2>
<p *ngIf="showCourse && event.course">
<core-format-text [text]="event.course.fullnamedisplay" contextLevel="course" [contextInstanceId]="event.course.id"></core-format-text>
</p>

View File

@ -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());
}
});
});

View File

@ -243,11 +243,17 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
*/
protected clearCoursesHandlers(courseId?: number): void {
if (courseId) {
if (!this.loaded[courseId]) {
// Don't clear if not loaded, it's probably an ongoing load and it could cause JS errors.
return;
}
this.loaded[courseId] = false;
delete this.coursesHandlers[courseId];
} else {
this.loaded = {};
this.coursesHandlers = {};
for (const courseId in this.coursesHandlers) {
this.clearCoursesHandlers(Number(courseId));
}
}
}
@ -484,7 +490,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
const promises = [],
courseData = this.coursesHandlers[courseId];
if (!courseData) {
if (!courseData || !courseData.enabledHandlers) {
return Promise.resolve();
}

View File

@ -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.');

View File

@ -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.

View File

@ -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) => {

View File

@ -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.