MOBILE-3659 course: Implement course format delegate

main
Dani Palou 2021-01-13 13:25:13 +01:00
parent 94b8707ce6
commit c617ec2dce
6 changed files with 598 additions and 50 deletions

View File

@ -23,12 +23,9 @@ import { CoreFilepool } from '@services/filepool';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import {
CoreCourseAnyCourseData,
CoreCourseBasicData,
CoreCourseGetCoursesData,
CoreCourses,
CoreCourseSearchedData,
CoreEnrolledCourseBasicData,
CoreEnrolledCourseData,
} from '@features/courses/services/courses';
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
import { CoreArray } from '@singletons/array';
@ -463,10 +460,10 @@ export class CoreCourseHelperProvider {
async getCourse(
courseId: number,
siteId?: string,
): Promise<{ enrolled: boolean; course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData }> {
): Promise<{ enrolled: boolean; course: CoreCourseAnyCourseData }> {
siteId = siteId || CoreSites.instance.getCurrentSiteId();
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData;
let course: CoreCourseAnyCourseData;
// Try with enrolled courses first.
try {
@ -495,11 +492,12 @@ export class CoreCourseHelperProvider {
* @param courseId Course ID.
* @param params Other params to pass to the course page.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when done.
*/
async getAndOpenCourse(courseId: number, params?: Params, siteId?: string): Promise<any> {
async getAndOpenCourse(courseId: number, params?: Params, siteId?: string): Promise<void> {
const modal = await CoreDomUtils.instance.showModalLoading();
let course: CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData | { id: number };
let course: CoreCourseAnyCourseData | { id: number };
try {
const data = await this.getCourse(courseId, siteId);
@ -575,7 +573,7 @@ export class CoreCourseHelperProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when done.
*/
async loadOfflineCompletion(courseId: number, sections: any[], siteId?: string): Promise<void> {
async loadOfflineCompletion(courseId: number, sections: CoreCourseSection[], siteId?: string): Promise<void> {
const offlineCompletions = await CoreCourseOffline.instance.getCourseManualCompletions(courseId, siteId);
if (!offlineCompletions || !offlineCompletions.length) {
@ -602,7 +600,7 @@ export class CoreCourseHelperProvider {
offlineCompletion.timecompleted >= module.completiondata.timecompleted * 1000) {
// The module has offline completion. Load it.
module.completiondata.state = offlineCompletion.completed;
module.completiondata.offline = true;
// @todo module.completiondata.offline = true;
// If all completions have been loaded, stop.
loaded++;
@ -758,7 +756,7 @@ export class CoreCourseHelperProvider {
* @return Section download ID.
* @todo section type.
*/
getSectionDownloadId(section: any): string {
getSectionDownloadId(section: CoreCourseSection): string {
return 'Section-' + section.id;
}
@ -978,7 +976,7 @@ export class CoreCourseHelperProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when done.
*/
async openCourse(course: CoreEnrolledCourseBasicData | { id: number }, params?: Params, siteId?: string): Promise<void> {
async openCourse(course: CoreCourseAnyCourseData | { id: number }, params?: Params, siteId?: string): Promise<void> {
if (!siteId || siteId == CoreSites.instance.getCurrentSiteId()) {
// Current site, we can open the course.
return CoreCourse.instance.openCourse(course, params);

View File

@ -30,12 +30,14 @@ import { CoreCourseStatusDBRecord, COURSE_STATUS_TABLE } from './database/course
import { CoreCourseOffline } from './course-offline';
import { CoreError } from '@classes/errors/error';
import {
CoreCourses,
CoreCourseAnyCourseData,
CoreCoursesProvider,
} from '../../courses/services/courses';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreWSError } from '@classes/errors/wserror';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
import { CoreCourseHelper } from './course-helper';
import { CoreCourseFormatDelegate } from './format-delegate';
const ROOT_CACHE_KEY = 'mmCourse:';
@ -71,9 +73,6 @@ export class CoreCourseProvider {
protected logger: CoreLogger;
constructor() {
// @todo
// protected courseFormatDelegate: CoreCourseFormatDelegate,
// protected sitePluginsProvider: CoreSitePluginsProvider,
this.logger = CoreLogger.getInstance('CoreCourseProvider');
}
@ -981,39 +980,22 @@ export class CoreCourseProvider {
* @param params Other params to pass to the course page.
* @return Promise resolved when done.
*/
async openCourse(
course: { id: number ; format?: string },
params?: Params, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<void> {
async openCourse(course: CoreCourseAnyCourseData | { id: number }, params?: Params): Promise<void> {
// @todo const loading = await CoreDomUtils.instance.showModalLoading();
// Wait for site plugins to be fetched.
// @todo await this.sitePluginsProvider.waitFetchPlugins();
if (typeof course.format == 'undefined') {
// This block can be replaced by a call to CourseHelper.getCourse(), but it is circular dependant.
const coursesProvider = CoreCourses.instance;
try {
course = await coursesProvider.getUserCourse(course.id, true);
} catch (error) {
// Not enrolled or an error happened. Try to use another WebService.
const available = coursesProvider.isGetCoursesByFieldAvailableInSite();
try {
if (available) {
course = await coursesProvider.getCourseByField('id', course.id);
} else {
course = await coursesProvider.getCourse(course.id);
}
} catch (error) {
// Ignore errors.
}
}
if (!('format' in course) || typeof course.format == 'undefined') {
const result = await CoreCourseHelper.instance.getCourse(course.id);
course = result.course;
}
/* @todo
if (!this.sitePluginsProvider.sitePluginPromiseExists('format_' + course.format)) {
// No custom format plugin. We don't need to wait for anything.
await this.courseFormatDelegate.openCourse(course, params);
await CoreCourseFormatDelegate.instance.openCourse(course, params);
loading.dismiss();
return;
@ -1024,20 +1006,17 @@ export class CoreCourseProvider {
/* @todo await this.sitePluginsProvider.sitePluginLoaded('format_' + course.format);
// The format loaded successfully, but the handlers wont be registered until all site plugins have loaded.
if (this.sitePluginsProvider.sitePluginsFinishedLoading) {
return this.courseFormatDelegate.openCourse(course, params);
return CoreCourseFormatDelegate.instance.openCourse(course, params);
}*/
// Wait for plugins to be loaded.
const deferred = CoreUtils.instance.promiseDefer<void>();
const observer = CoreEvents.on(CoreEvents.SITE_PLUGINS_LOADED, () => {
observer && observer.off();
observer?.off();
/* @todo this.courseFormatDelegate.openCourse(course, params).then((response) => {
deferred.resolve(response);
}).catch((error) => {
deferred.reject(error);
});*/
CoreCourseFormatDelegate.instance.openCourse(<CoreCourseAnyCourseData> course, params)
.then(deferred.resolve).catch(deferred.reject);
});
return deferred.promise;

View File

@ -0,0 +1,377 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable, Type } from '@angular/core';
import { Params } from '@angular/router';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { makeSingleton } from '@singletons';
import { CoreCourseSection } from './course';
import { CoreCourseFormatDefaultHandler } from './handlers/default-format';
/**
* Interface that all course format handlers must implement.
*/
export interface CoreCourseFormatHandler extends CoreDelegateHandler {
/**
* Name of the format the handler supports. E.g. 'singleactivity'.
*/
format: string;
/**
* Get the title to use in course page. If not defined, course fullname.
* This function will be called without sections first, and then call it again when the sections are retrieved.
*
* @param course The course.
* @param sections List of sections.
* @return Title.
*/
getCourseTitle?(course: CoreCourseAnyCourseData, sections?: CoreCourseSection[]): string;
/**
* Whether it allows seeing all sections at the same time. Defaults to true.
*
* @param course The course to check.
* @return Whether it can view all sections.
*/
canViewAllSections?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the option blocks should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether it can display blocks.
*/
displayBlocks?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the option to enable section/module download should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the option to enable section/module download should be displayed.
*/
displayEnableDownload?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the default section selector should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the default section selector should be displayed.
*/
displaySectionSelector?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
*
* @param course The course to check.
* @param sections List of course sections.
* @return Whether the refresher should be displayed.
*/
displayRefresher?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean;
/**
* Given a list of sections, get the "current" section that should be displayed first. Defaults to first section.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Promise resolved with current section.
*/
getCurrentSection?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection>;
/**
* Open the page to display a course. If not defined, the page CoreCourseSectionPage will be opened.
* Implement it only if you want to create your own page to display the course. In general it's better to use the method
* getCourseFormatComponent because it will display the course handlers at the top.
* Your page should include the course handlers using CoreCoursesDelegate.
*
* @param course The course to open. It should contain a "format" attribute.
* @param params Params to pass to the course page.
* @return Promise resolved when done.
*/
openCourse?(course: CoreCourseAnyCourseData, params?: Params): Promise<void>;
/**
* Return the Component to use to display the course format instead of using the default one.
* Use it if you want to display a format completely different from the default one.
* If you want to customize the default format there are several methods to customize parts of it.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getCourseFormatComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Return the Component to use to display the course summary inside the default course format.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getCourseSummaryComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Return the Component to use to display the section selector inside the default course format.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getSectionSelectorComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Return the Component to use to display a single section. This component will only be used if the user is viewing a
* single section. If all the sections are displayed at once then it won't be used.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getSingleSectionComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Return the Component to use to display all sections in a course.
* It's recommended to return the class of the component, but you can also return an instance of the component.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
getAllSectionsComponent?(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined>;
/**
* Invalidate the data required to load the course format.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Promise resolved when the data is invalidated.
*/
invalidateData?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void>;
/**
* Whether the view should be refreshed when completion changes. If your course format doesn't display
* activity completion then you should return false.
*
* @param course The course.
* @return Whether course view should be refreshed when an activity completion changes.
*/
shouldRefreshWhenCompletionChanges?(course: CoreCourseAnyCourseData): Promise<boolean>;
}
/**
* Service to interact with course formats.
*/
@Injectable({ providedIn: 'root' })
export class CoreCourseFormatDelegateService extends CoreDelegate<CoreCourseFormatHandler> {
protected featurePrefix = 'CoreCourseFormatDelegate_';
protected handlerNameProperty = 'format';
constructor(protected defaultHandler: CoreCourseFormatDefaultHandler) {
super('CoreCoursesCourseFormatDelegate', true);
}
/**
* Whether it allows seeing all sections at the same time. Defaults to true.
*
* @param course The course to check.
* @return Whether it allows seeing all sections at the same time.
*/
canViewAllSections(course: CoreCourseAnyCourseData): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'canViewAllSections', [course]);
}
/**
* Whether the option blocks should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether it can display blocks.
*/
displayBlocks(course: CoreCourseAnyCourseData): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayBlocks', [course]);
}
/**
* Whether the option to enable section/module download should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the option to enable section/module download should be displayed
*/
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayEnableDownload', [course]);
}
/**
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
*
* @param course The course to check.
* @param sections List of course sections.
* @return Whether the refresher should be displayed.
*/
displayRefresher(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displayRefresher', [course, sections]);
}
/**
* Whether the default section selector should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the section selector should be displayed.
*/
displaySectionSelector(course: CoreCourseAnyCourseData): boolean {
return !!this.executeFunctionOnEnabled<boolean>(course.format || '', 'displaySectionSelector', [course]);
}
/**
* Get the component to use to display all sections in a course.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getAllSectionsComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getAllSectionsComponent', [course]);
} catch (error) {
this.logger.error('Error getting all sections component', error);
}
}
/**
* Get the component to use to display a course format.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getCourseFormatComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getCourseFormatComponent', [course]);
} catch (error) {
this.logger.error('Error getting course format component', error);
}
}
/**
* Get the component to use to display the course summary in the default course format.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getCourseSummaryComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getCourseSummaryComponent', [course]);
} catch (error) {
this.logger.error('Error getting course summary component', error);
}
}
/**
* Given a course, return the title to use in the course page.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Course title.
*/
getCourseTitle(course: CoreCourseAnyCourseData, sections?: CoreCourseSection[]): string | undefined {
return this.executeFunctionOnEnabled(course.format || '', 'getCourseTitle', [course, sections]);
}
/**
* Given a course and a list of sections, return the current section that should be displayed first.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Promise resolved with current section.
*/
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
try {
const section = await this.executeFunctionOnEnabled<CoreCourseSection>(
course.format || '',
'getCurrentSection',
[course, sections],
);
return section || sections[0];
} catch {
// This function should never fail. Just return the first section (usually, "All sections").
return sections[0];
}
}
/**
* Get the component to use to display the section selector inside the default course format.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getSectionSelectorComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getSectionSelectorComponent', [course]);
} catch (error) {
this.logger.error('Error getting section selector component', error);
}
}
/**
* Get the component to use to display a single section. This component will only be used if the user is viewing
* a single section. If all the sections are displayed at once then it won't be used.
*
* @param course The course to render.
* @return Promise resolved with component to use, undefined if not found.
*/
async getSingleSectionComponent(course: CoreCourseAnyCourseData): Promise<Type<unknown> | undefined> {
try {
return await this.executeFunctionOnEnabled<Type<unknown>>(course.format || '', 'getSingleSectionComponent', [course]);
} catch (error) {
this.logger.error('Error getting single section component', error);
}
}
/**
* Invalidate the data required to load the course format.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Promise resolved when the data is invalidated.
*/
async invalidateData(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void> {
await this.executeFunctionOnEnabled(course.format || '', 'invalidateData', [course, sections]);
}
/**
* Open a course. Should not be called directly. Call CoreCourseHelper.openCourse instead.
*
* @param course The course to open. It should contain a "format" attribute.
* @param params Params to pass to the course page.
* @return Promise resolved when done.
*/
async openCourse(course: CoreCourseAnyCourseData, params?: Params): Promise<void> {
await this.executeFunctionOnEnabled(course.format || '', 'openCourse', [course, params]);
}
/**
* Whether the view should be refreshed when completion changes. If your course format doesn't display
* activity completion then you should return false.
*
* @param course The course.
* @return Whether course view should be refreshed when an activity completion changes.
*/
async shouldRefreshWhenCompletionChanges(course: CoreCourseAnyCourseData): Promise<boolean | undefined> {
return await this.executeFunctionOnEnabled(course.format || '', 'shouldRefreshWhenCompletionChanges', [course]);
}
}
export class CoreCourseFormatDelegate extends makeSingleton(CoreCourseFormatDelegateService) {}

View File

@ -0,0 +1,193 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Params } from '@angular/router';
import { CoreCourseAnyCourseData, CoreCourses } from '@features/courses/services/courses';
import { CoreUtils } from '@services/utils/utils';
import { CoreCourseSection } from '../course';
import { CoreCourseFormatHandler } from '../format-delegate';
/**
* Default handler used when the course format doesn't have a specific implementation.
*/
@Injectable({ providedIn: 'root' })
export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
name = 'CoreCourseFormatDefault';
format = 'default';
/**
* Whether or not the handler is enabled on a site level.
*
* @return Promise resolved with true if enabled.
*/
async isEnabled(): Promise<boolean> {
return true;
}
/**
* Get the title to use in course page.
*
* @param course The course.
* @return Title.
*/
getCourseTitle(course: CoreCourseAnyCourseData): string {
if (course.displayname) {
return course.displayname;
} else if (course.fullname) {
return course.fullname;
}
return '';
}
/**
* Whether it allows seeing all sections at the same time. Defaults to true.
*
* @param course The course to check.
* @return Whether it can view all sections.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
canViewAllSections(course: CoreCourseAnyCourseData): boolean {
return true;
}
/**
* Whether the option blocks should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether it can display blocks.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
displayBlocks(course: CoreCourseAnyCourseData): boolean {
return true;
}
/**
* Whether the option to enable section/module download should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the option to enable section/module download should be displayed
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
displayEnableDownload(course: CoreCourseAnyCourseData): boolean {
return true;
}
/**
* Whether the default section selector should be displayed. Defaults to true.
*
* @param course The course to check.
* @return Whether the default section selector should be displayed.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
displaySectionSelector(course: CoreCourseAnyCourseData): boolean {
return true;
}
/**
* Whether the course refresher should be displayed. If it returns false, a refresher must be included in the course format,
* and the doRefresh method of CoreCourseSectionPage must be called on refresh. Defaults to true.
*
* @param course The course to check.
* @param sections List of course sections.
* @return Whether the refresher should be displayed.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
displayRefresher?(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): boolean {
return true;
}
/**
* Given a list of sections, get the "current" section that should be displayed first.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Current section (or promise resolved with current section).
*/
async getCurrentSection(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<CoreCourseSection> {
let marker: number | undefined;
// We need the "marker" to determine the current section.
if ('marker' in course) {
// We already have it.
marker = course.marker;
} else if (!CoreCourses.instance.isGetCoursesByFieldAvailable()) {
// Cannot get the current section, return all of them.
return sections[0];
} else {
// Try to retrieve the marker.
const courseData = await CoreUtils.instance.ignoreErrors(CoreCourses.instance.getCourseByField('id', course.id));
marker = courseData?.marker;
}
if (marker && marker > 0) {
// Find the marked section.
const section = sections.find((sect) => sect.section == marker);
if (section) {
return section;
}
}
// Marked section not found or we couldn't retrieve the marker. Return all sections.
return sections[0];
}
/**
* Invalidate the data required to load the course format.
*
* @param course The course to get the title.
* @param sections List of sections.
* @return Promise resolved when the data is invalidated.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async invalidateData(course: CoreCourseAnyCourseData, sections: CoreCourseSection[]): Promise<void> {
await CoreCourses.instance.invalidateCoursesByField('id', course.id);
}
/**
* Open the page to display a course. If not defined, the page CoreCourseSectionPage will be opened.
* Implement it only if you want to create your own page to display the course. In general it's better to use the method
* getCourseFormatComponent because it will display the course handlers at the top.
* Your page should include the course handlers using CoreCoursesDelegate.
*
* @param course The course to open. It should contain a "format" attribute.
* @param params Params to pass to the course page.
* @return Promise resolved when done.
*/
async openCourse(course: CoreCourseAnyCourseData, params?: Params): Promise<void> {
params = params || {};
Object.assign(params, { course: course });
// Don't return the .push promise, we don't want to display a loading modal during the page transition.
// @todo navCtrl.push('CoreCourseSectionPage', params);
}
/**
* Whether the view should be refreshed when completion changes. If your course format doesn't display
* activity completion then you should return false.
*
* @param course The course.
* @return Whether course view should be refreshed when an activity completion changes.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async shouldRefreshWhenCompletionChanges(course: CoreCourseAnyCourseData): Promise<boolean> {
return true;
}
}

View File

@ -13,6 +13,7 @@
// limitations under the License.
import { Component, Input, OnInit } from '@angular/core';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { NavController } from '@ionic/angular';
import { CoreCourses, CoreCourseSearchedData } from '../../services/courses';
import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '../../services/courses-helper';
@ -95,13 +96,11 @@ export class CoreCoursesCourseListItemComponent implements OnInit {
* @param course The course to open.
*/
openCourse(): void {
/* if (this.isEnrolled) {
if (this.isEnrolled) {
CoreCourseHelper.instance.openCourse(this.course);
} else {
this.navCtrl.navigateForward('/main/home/courses/preview', { queryParams: { course: this.course } });
} */
// @todo while opencourse function is not completed, open preview page.
this.navCtrl.navigateForward('/main/home/courses/preview', { queryParams: { course: this.course } });
}
}
}

View File

@ -1588,3 +1588,5 @@ type CoreCourseSetFavouriteCoursesWSParams = {
favourite: boolean; // Favourite status.
}[];
};
export type CoreCourseAnyCourseData = CoreEnrolledCourseData | CoreCourseSearchedData | CoreCourseGetCoursesData;