Merge pull request #4017 from alfonso-salces/MOBILE-4430
Mobile 4430 courses: Update progress changesmain
commit
bfd37520bf
|
@ -47,6 +47,7 @@
|
|||
&[value]::-webkit-progress-value {
|
||||
background-color: var(--progressbar-color);
|
||||
border-radius: var(--height);
|
||||
transition: width 500ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,13 @@ import { CoreCourses, CoreCourseAnyCourseData } from '@features/courses/services
|
|||
import {
|
||||
CoreCourse,
|
||||
CoreCourseCompletionActivityStatus,
|
||||
CoreCourseModuleCompletionStatus,
|
||||
CoreCourseProvider,
|
||||
} from '@features/course/services/course';
|
||||
import {
|
||||
CoreCourseHelper,
|
||||
CoreCourseModuleCompletionData,
|
||||
CoreCourseModuleData,
|
||||
CoreCourseSection,
|
||||
} from '@features/course/services/course-helper';
|
||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
||||
|
@ -38,6 +41,7 @@ import {
|
|||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-context';
|
||||
import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
|
||||
import { CoreSites } from '@services/sites';
|
||||
|
||||
/**
|
||||
* Page that displays the contents of a course.
|
||||
|
@ -315,22 +319,51 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon
|
|||
* @returns Promise resolved when done.
|
||||
*/
|
||||
async onCompletionChange(completionData: CoreCourseModuleCompletionData): Promise<void> {
|
||||
const shouldReload = completionData.valueused === undefined || completionData.valueused;
|
||||
|
||||
if (!shouldReload) {
|
||||
// Invalidate the completion.
|
||||
await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.course.id));
|
||||
|
||||
this.debouncedUpdateCachedCompletion?.();
|
||||
|
||||
if (completionData.courseId != this.course?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreUtils.ignoreErrors(this.invalidateData());
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
const shouldReload = completionData.valueused === true;
|
||||
|
||||
if (!shouldReload) {
|
||||
|
||||
if (!this.course || !('progress' in this.course) || typeof this.course.progress != 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.sections) {
|
||||
// If the completion value is not used, the page won't be reloaded, so update the progress bar.
|
||||
const completionModules = (<CoreCourseModuleData[]> [])
|
||||
.concat(...this.sections.map((section) => section.modules))
|
||||
.map((module) => module.completion && module.completion > 0 ? 1 : module.completion)
|
||||
.reduce((accumulator, currentValue) => (accumulator || 0) + (currentValue || 0), 0);
|
||||
|
||||
const moduleProgressPercent = 100 / (completionModules || 1);
|
||||
// Use min/max here to avoid floating point rounding errors over/under-flowing the progress bar.
|
||||
if (completionData.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
|
||||
this.course.progress = Math.min(100, this.course.progress + moduleProgressPercent);
|
||||
} else {
|
||||
this.course.progress = Math.max(0, this.course.progress - moduleProgressPercent);
|
||||
}
|
||||
}
|
||||
|
||||
await CoreUtils.ignoreErrors(this.invalidateData());
|
||||
this.debouncedUpdateCachedCompletion?.();
|
||||
} else {
|
||||
await CoreUtils.ignoreErrors(this.invalidateData());
|
||||
await this.showLoadingAndRefresh(true, false);
|
||||
}
|
||||
|
||||
if (!('progress' in this.course) || this.course.progress === undefined || this.course.progress === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreEvents.trigger(CoreCourseProvider.PROGRESS_UPDATED, {
|
||||
courseId: this.course.id, progress: this.course.progress,
|
||||
}, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the data.
|
||||
*
|
||||
|
|
|
@ -20,7 +20,7 @@ import { CoreCourseFormatDelegate } from '../../services/format-delegate';
|
|||
import { CoreCourseOptionsDelegate } from '../../services/course-options-delegate';
|
||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseWSSection } from '@features/course/services/course';
|
||||
import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/course/services/course';
|
||||
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||
|
@ -29,6 +29,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
|||
import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/courses/services/courses-helper';
|
||||
import { CoreColors } from '@singletons/colors';
|
||||
import { CorePath } from '@singletons/path';
|
||||
import { CoreSites } from '@services/sites';
|
||||
|
||||
/**
|
||||
* Page that displays the list of courses the user is enrolled in.
|
||||
|
@ -54,7 +55,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
protected currentPagePath = '';
|
||||
protected fullScreenObserver: CoreEventObserver;
|
||||
protected selectTabObserver: CoreEventObserver;
|
||||
protected completionObserver: CoreEventObserver;
|
||||
protected progressObserver: CoreEventObserver;
|
||||
protected sections: CoreCourseWSSection[] = []; // List of course sections.
|
||||
protected firstTabName?: string;
|
||||
protected module?: CoreCourseModuleData;
|
||||
|
@ -89,33 +90,16 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
}
|
||||
});
|
||||
|
||||
// The completion of any of the modules have changed.
|
||||
this.completionObserver = CoreEvents.on(CoreEvents.MANUAL_COMPLETION_CHANGED, (data) => {
|
||||
if (data.completion.courseId != this.course?.id) {
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
this.progressObserver = CoreEvents.on(CoreCourseProvider.PROGRESS_UPDATED, (data) => {
|
||||
if (!this.course || this.course.id !== data.courseId || !('progress' in this.course)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.completion.valueused !== false || !this.course || !('progress' in this.course) ||
|
||||
typeof this.course.progress != 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the completion value is not used, the page won't be reloaded, so update the progress bar.
|
||||
const completionModules = (<CoreCourseModuleData[]> [])
|
||||
.concat(...this.sections.map((section) => section.modules))
|
||||
.map((module) => module.completion && module.completion > 0 ? 1 : module.completion)
|
||||
.reduce((accumulator, currentValue) => (accumulator || 0) + (currentValue || 0), 0);
|
||||
|
||||
const moduleProgressPercent = 100 / (completionModules || 1);
|
||||
// Use min/max here to avoid floating point rounding errors over/under-flowing the progress bar.
|
||||
if (data.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
|
||||
this.course.progress = Math.min(100, this.course.progress + moduleProgressPercent);
|
||||
} else {
|
||||
this.course.progress = Math.max(0, this.course.progress - moduleProgressPercent);
|
||||
}
|
||||
|
||||
this.course.progress = data.progress;
|
||||
this.updateProgress();
|
||||
});
|
||||
}, siteId);
|
||||
|
||||
this.fullScreenObserver = CoreEvents.on(CoreEvents.FULL_SCREEN_CHANGED, (event: { enabled: boolean }) => {
|
||||
this.fullScreenEnabled = event.enabled;
|
||||
|
@ -267,7 +251,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
|
||||
CoreNavigator.decreaseRouteDepth(path.replace(/(\/deep)+/, ''));
|
||||
this.selectTabObserver?.off();
|
||||
this.completionObserver?.off();
|
||||
this.progressObserver?.off();
|
||||
this.fullScreenObserver?.off();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-si
|
|||
|
||||
const ROOT_CACHE_KEY = 'mmCourse:';
|
||||
|
||||
export type CoreCourseProgressUpdated = { progress: number; courseId: number };
|
||||
|
||||
declare module '@singletons/events' {
|
||||
|
||||
/**
|
||||
|
@ -73,6 +75,7 @@ declare module '@singletons/events' {
|
|||
*/
|
||||
export interface CoreEventsData {
|
||||
[CoreCourseSyncProvider.AUTO_SYNCED]: CoreCourseAutoSyncData;
|
||||
[CoreCourseProvider.PROGRESS_UPDATED]: CoreCourseProgressUpdated;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -121,6 +124,7 @@ export class CoreCourseProvider {
|
|||
static readonly ALL_SECTIONS_ID = -2;
|
||||
static readonly STEALTH_MODULES_SECTION_ID = -1;
|
||||
static readonly ALL_COURSES_CLEARED = -1;
|
||||
static readonly PROGRESS_UPDATED = 'progress_updated';
|
||||
|
||||
/**
|
||||
* @deprecated since 4.4 Not used anymore. Use CoreCourseAccessDataType instead.
|
||||
|
|
|
@ -65,9 +65,19 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
|
|||
protected courseStatusObserver?: CoreEventObserver;
|
||||
|
||||
protected element: HTMLElement;
|
||||
protected progressObserver: CoreEventObserver;
|
||||
|
||||
constructor(element: ElementRef) {
|
||||
this.element = element.nativeElement;
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
this.progressObserver = CoreEvents.on(CoreCourseProvider.PROGRESS_UPDATED, (data) => {
|
||||
if (!this.course || this.course.id !== data.courseId || !('progress' in this.course)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.course.progress = data.progress;
|
||||
this.progress = this.course.progress ?? undefined;
|
||||
}, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -387,6 +397,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
|
|||
ngOnDestroy(): void {
|
||||
this.isDestroyed = true;
|
||||
this.courseStatusObserver?.off();
|
||||
this.progressObserver.off();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue