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 {
|
&[value]::-webkit-progress-value {
|
||||||
background-color: var(--progressbar-color);
|
background-color: var(--progressbar-color);
|
||||||
border-radius: var(--height);
|
border-radius: var(--height);
|
||||||
|
transition: width 500ms ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,13 @@ import { CoreCourses, CoreCourseAnyCourseData } from '@features/courses/services
|
||||||
import {
|
import {
|
||||||
CoreCourse,
|
CoreCourse,
|
||||||
CoreCourseCompletionActivityStatus,
|
CoreCourseCompletionActivityStatus,
|
||||||
|
CoreCourseModuleCompletionStatus,
|
||||||
|
CoreCourseProvider,
|
||||||
} from '@features/course/services/course';
|
} from '@features/course/services/course';
|
||||||
import {
|
import {
|
||||||
CoreCourseHelper,
|
CoreCourseHelper,
|
||||||
CoreCourseModuleCompletionData,
|
CoreCourseModuleCompletionData,
|
||||||
|
CoreCourseModuleData,
|
||||||
CoreCourseSection,
|
CoreCourseSection,
|
||||||
} from '@features/course/services/course-helper';
|
} from '@features/course/services/course-helper';
|
||||||
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
|
||||||
|
@ -38,6 +41,7 @@ import {
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-context';
|
import { CoreRefreshContext, CORE_REFRESH_CONTEXT } from '@/core/utils/refresh-context';
|
||||||
import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
|
import { CoreCoursesHelper } from '@features/courses/services/courses-helper';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the contents of a course.
|
* Page that displays the contents of a course.
|
||||||
|
@ -315,22 +319,51 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async onCompletionChange(completionData: CoreCourseModuleCompletionData): Promise<void> {
|
async onCompletionChange(completionData: CoreCourseModuleCompletionData): Promise<void> {
|
||||||
const shouldReload = completionData.valueused === undefined || completionData.valueused;
|
if (completionData.courseId != this.course?.id) {
|
||||||
|
|
||||||
if (!shouldReload) {
|
|
||||||
// Invalidate the completion.
|
|
||||||
await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.course.id));
|
|
||||||
|
|
||||||
this.debouncedUpdateCachedCompletion?.();
|
|
||||||
|
|
||||||
return;
|
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);
|
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.
|
* Invalidate the data.
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreCourseFormatDelegate } from '../../services/format-delegate';
|
||||||
import { CoreCourseOptionsDelegate } from '../../services/course-options-delegate';
|
import { CoreCourseOptionsDelegate } from '../../services/course-options-delegate';
|
||||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
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 { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
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 { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/courses/services/courses-helper';
|
||||||
import { CoreColors } from '@singletons/colors';
|
import { CoreColors } from '@singletons/colors';
|
||||||
import { CorePath } from '@singletons/path';
|
import { CorePath } from '@singletons/path';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the list of courses the user is enrolled in.
|
* 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 currentPagePath = '';
|
||||||
protected fullScreenObserver: CoreEventObserver;
|
protected fullScreenObserver: CoreEventObserver;
|
||||||
protected selectTabObserver: CoreEventObserver;
|
protected selectTabObserver: CoreEventObserver;
|
||||||
protected completionObserver: CoreEventObserver;
|
protected progressObserver: CoreEventObserver;
|
||||||
protected sections: CoreCourseWSSection[] = []; // List of course sections.
|
protected sections: CoreCourseWSSection[] = []; // List of course sections.
|
||||||
protected firstTabName?: string;
|
protected firstTabName?: string;
|
||||||
protected module?: CoreCourseModuleData;
|
protected module?: CoreCourseModuleData;
|
||||||
|
@ -89,33 +90,16 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// The completion of any of the modules have changed.
|
const siteId = CoreSites.getCurrentSiteId();
|
||||||
this.completionObserver = CoreEvents.on(CoreEvents.MANUAL_COMPLETION_CHANGED, (data) => {
|
|
||||||
if (data.completion.courseId != this.course?.id) {
|
this.progressObserver = CoreEvents.on(CoreCourseProvider.PROGRESS_UPDATED, (data) => {
|
||||||
|
if (!this.course || this.course.id !== data.courseId || !('progress' in this.course)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.completion.valueused !== false || !this.course || !('progress' in this.course) ||
|
this.course.progress = data.progress;
|
||||||
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.updateProgress();
|
this.updateProgress();
|
||||||
});
|
}, siteId);
|
||||||
|
|
||||||
this.fullScreenObserver = CoreEvents.on(CoreEvents.FULL_SCREEN_CHANGED, (event: { enabled: boolean }) => {
|
this.fullScreenObserver = CoreEvents.on(CoreEvents.FULL_SCREEN_CHANGED, (event: { enabled: boolean }) => {
|
||||||
this.fullScreenEnabled = event.enabled;
|
this.fullScreenEnabled = event.enabled;
|
||||||
|
@ -267,7 +251,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
CoreNavigator.decreaseRouteDepth(path.replace(/(\/deep)+/, ''));
|
CoreNavigator.decreaseRouteDepth(path.replace(/(\/deep)+/, ''));
|
||||||
this.selectTabObserver?.off();
|
this.selectTabObserver?.off();
|
||||||
this.completionObserver?.off();
|
this.progressObserver?.off();
|
||||||
this.fullScreenObserver?.off();
|
this.fullScreenObserver?.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,8 @@ import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-si
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'mmCourse:';
|
const ROOT_CACHE_KEY = 'mmCourse:';
|
||||||
|
|
||||||
|
export type CoreCourseProgressUpdated = { progress: number; courseId: number };
|
||||||
|
|
||||||
declare module '@singletons/events' {
|
declare module '@singletons/events' {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +75,7 @@ declare module '@singletons/events' {
|
||||||
*/
|
*/
|
||||||
export interface CoreEventsData {
|
export interface CoreEventsData {
|
||||||
[CoreCourseSyncProvider.AUTO_SYNCED]: CoreCourseAutoSyncData;
|
[CoreCourseSyncProvider.AUTO_SYNCED]: CoreCourseAutoSyncData;
|
||||||
|
[CoreCourseProvider.PROGRESS_UPDATED]: CoreCourseProgressUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -121,6 +124,7 @@ export class CoreCourseProvider {
|
||||||
static readonly ALL_SECTIONS_ID = -2;
|
static readonly ALL_SECTIONS_ID = -2;
|
||||||
static readonly STEALTH_MODULES_SECTION_ID = -1;
|
static readonly STEALTH_MODULES_SECTION_ID = -1;
|
||||||
static readonly ALL_COURSES_CLEARED = -1;
|
static readonly ALL_COURSES_CLEARED = -1;
|
||||||
|
static readonly PROGRESS_UPDATED = 'progress_updated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated since 4.4 Not used anymore. Use CoreCourseAccessDataType instead.
|
* @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 courseStatusObserver?: CoreEventObserver;
|
||||||
|
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
|
protected progressObserver: CoreEventObserver;
|
||||||
|
|
||||||
constructor(element: ElementRef) {
|
constructor(element: ElementRef) {
|
||||||
this.element = element.nativeElement;
|
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 {
|
ngOnDestroy(): void {
|
||||||
this.isDestroyed = true;
|
this.isDestroyed = true;
|
||||||
this.courseStatusObserver?.off();
|
this.courseStatusObserver?.off();
|
||||||
|
this.progressObserver.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue