MOBILE-3915 course: Update course progress when completion changes

main
Pau Ferrer Ocaña 2022-01-31 14:31:17 +01:00
parent 5e29e65325
commit 7971e71e57
2 changed files with 36 additions and 65 deletions

View File

@ -19,8 +19,6 @@ import {
OnChanges,
OnDestroy,
SimpleChange,
Output,
EventEmitter,
ViewChildren,
QueryList,
Type,
@ -31,12 +29,9 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import {
CoreCourse,
CoreCourseModuleCompletionStatus,
CoreCourseProvider,
} from '@features/course/services/course';
import {
CoreCourseModuleData,
CoreCourseModuleCompletionData,
CoreCourseSection,
} from '@features/course/services/course-helper';
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
@ -56,7 +51,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg
*
* Example usage:
*
* <core-course-format [course]="course" [sections]="sections" (completionChanged)="onCompletionChange()"></core-course-format>
* <core-course-format [course]="course" [sections]="sections"></core-course-format>
*/
@Component({
selector: 'core-course-format',
@ -72,7 +67,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
@Input() initialSectionId?: number; // The section to load first (by ID).
@Input() initialSectionNumber?: number; // The section to load first (by number).
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when any module completion changes.
@ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent>;
@ -95,11 +89,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID;
stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
loaded = false;
progress?: number;
highlighted?: string;
protected selectTabObserver?: CoreEventObserver;
protected completionObserver?: CoreEventObserver;
protected lastCourseFormat?: string;
constructor(
@ -141,36 +133,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
}
});
// The completion of any of the modules have changed.
this.completionObserver = CoreEvents.on(CoreEvents.COMPLETION_CHANGED, (data) => {
if (data.completion.courseId != this.course.id) {
return;
}
// Emit a new event for other components.
this.completionChanged.emit(data.completion);
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.updateProgress();
});
}
/**
@ -187,8 +149,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.displayBlocks = CoreCourseFormatDelegate.displayBlocks(this.course);
this.hasBlocks = await CoreBlockHelper.hasCourseBlocks(this.course.id);
this.updateProgress();
}
if (changes.sections && this.sections) {
@ -205,7 +165,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.data.initialSectionId = this.initialSectionId;
this.data.initialSectionNumber = this.initialSectionNumber;
this.data.moduleId = this.moduleId;
this.data.completionChanged = this.completionChanged;
}
/**
@ -542,25 +501,6 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
}
/**
* Update course progress.
*/
protected updateProgress(): void {
if (
!this.course ||
!('progress' in this.course) ||
typeof this.course.progress !== 'number' ||
this.course.progress < 0 ||
this.course.completionusertracked === false
) {
this.progress = undefined;
return;
}
this.progress = this.course.progress;
}
}
type CoreCourseSectionToDisplay = CoreCourseSection & {

View File

@ -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 } from '@features/course/services/course';
import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseWSSection } from '@features/course/services/course';
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreUtils } from '@services/utils/utils';
import { CoreTextUtils } from '@services/utils/text';
@ -52,6 +52,8 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
protected currentPagePath = '';
protected selectTabObserver: CoreEventObserver;
protected completionObserver: CoreEventObserver;
protected sections: CoreCourseWSSection[] = []; // List of course sections.
protected firstTabName?: string;
protected module?: CoreCourseModuleData;
protected modParams?: Params;
@ -83,6 +85,34 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
}
}
});
// The completion of any of the modules have changed.
this.completionObserver = CoreEvents.on(CoreEvents.COMPLETION_CHANGED, (data) => {
if (data.completion.courseId != this.course?.id) {
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.updateProgress();
});
}
/**
@ -206,14 +236,14 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
this.updateProgress();
// Load sections.
const sections = await CoreUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true));
this.sections = await CoreUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true), []);
if (!sections) {
if (!this.sections) {
return;
}
// Get the title again now that we have sections.
this.title = CoreCourseFormatDelegate.getCourseTitle(this.course, sections);
this.title = CoreCourseFormatDelegate.getCourseTitle(this.course, this.sections);
}
/**
@ -224,6 +254,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
CoreNavigator.decreaseRouteDepth(path.replace(/(\/deep)+/, ''));
this.selectTabObserver?.off();
this.completionObserver?.off();
}
/**