MOBILE-3686 courses: Merge list courses and my courses page

main
Pau Ferrer Ocaña 2021-10-13 16:17:40 +02:00
parent 18f20dc12e
commit 8acb8b74e2
13 changed files with 147 additions and 349 deletions

View File

@ -1551,6 +1551,7 @@
"core.courses.selfenrolment": "local_moodlemobileapp",
"core.courses.sendpaymentbutton": "enrol_paypal",
"core.courses.show": "block_myoverview",
"core.courses.showonlyenrolled": "local_moodlemobileapp",
"core.courses.therearecourses": "moodle",
"core.courses.totalcoursesearchresults": "local_moodlemobileapp",
"core.currentdevice": "local_moodlemobileapp",

View File

@ -6,6 +6,10 @@
<img [src]="course.courseImage" core-external-content alt=""/>
</ion-avatar>
<ion-label>
<h2>
<core-format-text [text]="course.displayname || course.fullname" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</h2>
<p *ngIf="course.categoryname || (course.displayname && course.shortname && course.fullname != course.displayname)"
class="core-course-additional-info">
<span *ngIf="course.categoryname" class="core-course-category">
@ -19,10 +23,6 @@
</core-format-text>
</span>
</p>
<h2>
<core-format-text [text]="course.displayname || course.fullname" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</h2>
<p *ngIf="isEnrolled && course.progress! >= 0 && course.completionusertracked !== false">
<core-progress-bar [progress]="course.progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar>
</p>

View File

@ -15,8 +15,11 @@
import { Component, Input, OnInit } from '@angular/core';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreNavigator } from '@services/navigator';
import { CoreCourses, CoreCourseSearchedData } from '../../services/courses';
import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '../../services/courses-helper';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreCourseListItem, CoreCourses } from '../../services/courses';
import { CoreCoursesHelper } from '../../services/courses-helper';
/**
* This directive is meant to display an item for a list of courses.
@ -32,10 +35,7 @@ import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '../../services/c
})
export class CoreCoursesCourseListItemComponent implements OnInit {
@Input() course!: CoreCourseSearchedData & CoreCourseWithImageAndColor & {
completionusertracked?: boolean; // If the user is completion tracked.
progress?: number | null; // Progress percentage.
}; // The course to render.
@Input() course!: CoreCourseListItem; // The course to render.
icons: CoreCoursesEnrolmentIcons[] = [];
isEnrolled = false;
@ -46,15 +46,21 @@ export class CoreCoursesCourseListItemComponent implements OnInit {
async ngOnInit(): Promise<void> {
CoreCoursesHelper.loadCourseColorAndImage(this.course);
// Check if the user is enrolled in the course.
try {
const course = await CoreCourses.getUserCourse(this.course.id);
this.course.progress = course.progress;
this.course.completionusertracked = course.completionusertracked;
this.isEnrolled = this.course.progress !== undefined;
this.isEnrolled = true;
} catch {
this.isEnrolled = false;
if (!this.isEnrolled) {
try {
const course = await CoreCourses.getUserCourse(this.course.id);
this.course.progress = course.progress;
this.course.completionusertracked = course.completionusertracked;
this.isEnrolled = true;
} catch {
this.isEnrolled = false;
}
}
if (!this.isEnrolled) {
this.icons = [];
this.course.enrollmentmethods.forEach((instance) => {

View File

@ -18,7 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
redirectTo: 'my',
redirectTo: 'list',
pathMatch: 'full',
},
{
@ -38,12 +38,6 @@ const routes: Routes = [
import('./pages/list/list.module')
.then(m => m.CoreCoursesListPageModule),
},
{
path: 'my',
loadChildren: () =>
import('./pages/my-courses/my-courses.module')
.then(m => m.CoreCoursesMyCoursesPageModule),
},
];
@NgModule({

View File

@ -44,7 +44,7 @@ const mainMenuHomeChildrenRoutes: Routes = [
},
{
path: CoreCoursesMyCoursesHomeHandlerService.PAGE_NAME,
loadChildren: () => import('./pages/my-courses/my-courses.module').then(m => m.CoreCoursesMyCoursesPageModule),
loadChildren: () => import('./pages/list/list.module').then(m => m.CoreCoursesListPageModule),
},
];

View File

@ -40,6 +40,7 @@
"selfenrolment": "Self enrolment",
"sendpaymentbutton": "Send payment via PayPal",
"show": "Restore to view",
"showonlyenrolled": "Show only my courses",
"therearecourses": "There are {{$a}} courses",
"totalcoursesearchresults": "Total courses: {{$a}}"
}

View File

@ -3,7 +3,19 @@
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
</ion-buttons>
<h1>{{ 'core.courses.availablecourses' | translate }}</h1>
<h1 *ngIf="!showOnlyEnrolled">{{ 'core.courses.availablecourses' | translate }}</h1>
<h1 *ngIf="showOnlyEnrolled">{{ 'core.courses.mycourses' | translate }}</h1>
<ion-buttons slot="end"></ion-buttons>
<core-navbar-buttons slot="end">
<core-context-menu>
<core-context-menu-item *ngIf="downloadCourseEnabled || downloadCoursesEnabled" [priority]="1000"
[content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload(!downloadEnabled)"
iconAction="toggle" [toggle]="downloadEnabled"></core-context-menu-item>
<core-context-menu-item *ngIf="myCoursesEnabled" [priority]="900"
[content]="'core.courses.showonlyenrolled' | translate" (action)="toggleEnrolled(!showOnlyEnrolled)"
iconAction="toggle" [toggle]="showOnlyEnrolled"></core-context-menu-item>
</core-context-menu>
</core-navbar-buttons>
</ion-toolbar>
</ion-header>
<ion-content>

View File

@ -13,14 +13,15 @@
// limitations under the License.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfo } from '@features/courses/services/courses-helper';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreCourseBasicSearchedData, CoreCourses } from '../../services/courses';
import { CoreCourseBasicSearchedData, CoreCourses, CoreCoursesProvider } from '../../services/courses';
type CoreCoursesListMode = 'search' | 'all';
type CoreCoursesListMode = 'search' | 'all' | 'my';
/**
* Page that shows a list of courses.
@ -31,31 +32,60 @@ type CoreCoursesListMode = 'search' | 'all';
})
export class CoreCoursesListPage implements OnInit, OnDestroy {
downloadAllCoursesEnabled = false;
searchEnabled = false;
myCoursesEnabled = true;
searchMode = false;
searchCanLoadMore = false;
searchLoadMoreError = false;
searchTotal = 0;
mode: CoreCoursesListMode = 'all';
downloadEnabled = false;
downloadCourseEnabled = false;
downloadCoursesEnabled = false;
courses: CoreCourseBasicSearchedData[] = [];
mode: CoreCoursesListMode = 'my';
courses: (CoreCourseBasicSearchedData|CoreEnrolledCourseDataWithExtraInfo)[] = [];
coursesLoaded = false;
showOnlyEnrolled = false;
protected currentSiteId: string;
protected frontpageCourseId: number;
protected searchPage = 0;
protected searchText = '';
protected myCoursesObserver: CoreEventObserver;
protected siteUpdatedObserver: CoreEventObserver;
protected courseIds = '';
protected isDestroyed = false;
constructor() {
this.currentSiteId = CoreSites.getRequiredCurrentSite().getId();
this.frontpageCourseId = CoreSites.getRequiredCurrentSite().getSiteHomeId();
// Update list if user enrols in a course.
this.myCoursesObserver = CoreEvents.on(
CoreCoursesProvider.EVENT_MY_COURSES_UPDATED,
(data) => {
if (data.action == CoreCoursesProvider.ACTION_ENROL) {
this.fetchCourses();
}
},
this.currentSiteId,
);
// Refresh the enabled flags if site is updated.
this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
this.myCoursesEnabled = !CoreCourses.isMyCoursesDisabledInSite();
this.downloadEnabled = (this.downloadCourseEnabled || this.downloadCoursesEnabled) && this.downloadEnabled;
if (!this.searchEnabled) {
this.searchMode = false;
@ -68,12 +98,25 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
* @inheritdoc
*/
ngOnInit(): void {
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
this.mode = CoreNavigator.getRouteParam<CoreCoursesListMode>('mode') || this.mode;
this.myCoursesEnabled = !CoreCourses.isMyCoursesDisabledInSite();
if (this.mode == 'my' && !this.myCoursesEnabled) {
this.mode = 'all';
this.showOnlyEnrolled = false;
}
if (this.mode == 'search') {
this.searchMode = true;
}
if (this.mode == 'my') {
this.showOnlyEnrolled = true;
}
this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
if (!this.searchEnabled) {
this.searchMode = false;
@ -91,6 +134,8 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
try {
if (this.searchMode && this.searchText) {
await this.search(this.searchText);
} else if (this.showOnlyEnrolled) {
await this.loadMyCourses();
} else {
await this.loadAvailableCourses();
}
@ -99,6 +144,24 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
}
}
/**
* Fetch the user courses.
*
* @return Promise resolved when done.
*/
protected async loadMyCourses(): Promise<void> {
try {
const courses: CoreEnrolledCourseDataWithExtraInfo[] = await CoreCourses.getUserCourses();
this.courseIds = courses.map((course) => course.id).join(',');
await CoreCoursesHelper.loadCoursesExtraInfo(courses, true);
this.courses = courses;
} catch (error) {
!this.isDestroyed && CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
}
}
/**
* Load the courses.
*
@ -110,7 +173,7 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
this.courses = courses.filter((course) => course.id != this.frontpageCourseId);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
!this.isDestroyed && CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
}
}
@ -124,6 +187,9 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
promises.push(CoreCourses.invalidateUserCourses());
promises.push(CoreCourses.invalidateCoursesByField());
if (this.courseIds) {
promises.push(CoreCourses.invalidateCoursesByField('ids', this.courseIds));
}
Promise.all(promises).finally(() => {
this.fetchCourses().finally(() => {
@ -145,7 +211,7 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
this.searchTotal = 0;
const modal = await CoreDomUtils.showModalLoading('core.searching', true);
this.searchCourses().finally(() => {
await this.searchCourses().finally(() => {
modal.dismiss();
});
}
@ -184,7 +250,7 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
this.searchLoadMoreError = false;
try {
const response = await CoreCourses.search(this.searchText, this.searchPage);
const response = await CoreCourses.search(this.searchText, this.searchPage, undefined, this.showOnlyEnrolled);
if (this.searchPage === 0) {
this.courses = response.courses;
@ -197,15 +263,34 @@ export class CoreCoursesListPage implements OnInit, OnDestroy {
this.searchCanLoadMore = this.courses.length < this.searchTotal;
} catch (error) {
this.searchLoadMoreError = true; // Set to prevent infinite calls with infinite-loading.
CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorsearching', true);
!this.isDestroyed && CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorsearching', true);
}
}
/**
* Toggle show only my courses.
*/
toggleEnrolled(enabled: boolean): void {
this.coursesLoaded = false;
this.showOnlyEnrolled = enabled;
this.fetchCourses();
}
/**
* Toggle download enabled.
*/
toggleDownload(enabled: boolean): void {
this.downloadEnabled = enabled;
}
/**
* @inheritdoc
*/
ngOnDestroy(): void {
this.myCoursesObserver?.off();
this.siteUpdatedObserver?.off();
this.isDestroyed = true;
}
}

View File

@ -1,54 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
</ion-buttons>
<h1>{{ 'core.courses.mycourses' | translate }}</h1>
<ion-buttons slot="end">
<core-navbar-buttons>
<ion-button *ngIf="searchEnabled" (click)="openSearch()"
[attr.aria-label]="'core.courses.searchcourses' | translate">
<ion-icon name="fas-search" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button [hidden]="!downloadAllCoursesEnabled || !courses || courses.length < 2 || downloadAllCoursesLoading"
(click)="prefetchCourses()" [attr.aria-label]="'core.courses.downloadcourses' | translate">
<ion-icon [name]="downloadAllCoursesIcon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-spinner [hidden]="!downloadAllCoursesEnabled || !courses || courses.length < 2 ||
downloadAllCoursesBadge != '' || !downloadAllCoursesLoading"
[attr.aria-label]="'core.loading' | translate"></ion-spinner>
<ion-badge [hidden]="!downloadAllCoursesEnabled || !courses || courses.length < 2 || !downloadAllCoursesLoading ||
downloadAllCoursesBadge == '' || !downloadAllCoursesLoading"
role="progressbar" [attr.aria-valuemax]="downloadAllCoursesTotal"
[attr.aria-valuenow]="downloadAllCoursesCount" [attr.aria-valuetext]="downloadAllCoursesBadgeA11yText">
{{downloadAllCoursesBadge}}
</ion-badge>
</core-navbar-buttons>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher slot="fixed" [disabled]="!coursesLoaded" (ionRefresh)="refreshCourses($event.target)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="coursesLoaded">
<ion-searchbar #searchbar *ngIf="courses && courses.length > 5" [(ngModel)]="filter" (ionInput)="filterChanged($event)"
(ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate">
</ion-searchbar>
<ion-grid class="ion-no-padding safe-area-padding">
<ion-row class="ion-no-padding">
<ion-col *ngFor="let course of filteredCourses" class="ion-no-padding"
size="12" size-sm="6" size-md="6" size-lg="4" size-xl="4">
<core-courses-course-progress [course]="course" class="core-courseoverview" showAll="true">
</core-courses-course-progress>
</ion-col>
</ion-row>
</ion-grid>
<core-empty-box *ngIf="!courses || !courses.length" icon="fas-graduation-cap"
[message]="'core.courses.nocourses' | translate">
<p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p>
</core-empty-box>
</core-loading>
</ion-content>

View File

@ -1,40 +0,0 @@
// (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 { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreCoursesMyCoursesPage } from './my-courses';
import { CoreCoursesComponentsModule } from '../../components/components.module';
const routes: Routes = [
{
path: '',
component: CoreCoursesMyCoursesPage,
},
];
@NgModule({
imports: [
RouterModule.forChild(routes),
CoreSharedModule,
CoreCoursesComponentsModule,
],
declarations: [
CoreCoursesMyCoursesPage,
],
exports: [RouterModule],
})
export class CoreCoursesMyCoursesPageModule { }

View File

@ -1,219 +0,0 @@
// (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 { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { IonSearchbar, IonRefresher } from '@ionic/angular';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import {
CoreCoursesProvider,
CoreCourses,
} from '../../services/courses';
import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreConstants } from '@/core/constants';
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
import { CoreNavigator } from '@services/navigator';
import { Translate } from '@singletons';
/**
* Page that displays the list of courses the user is enrolled in.
*/
@Component({
selector: 'page-core-courses-my-courses',
templateUrl: 'my-courses.html',
})
export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
@ViewChild(IonSearchbar) searchbar!: IonSearchbar;
courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[] = [];
filteredCourses: CoreEnrolledCourseDataWithExtraInfoAndOptions[] = [];
searchEnabled = false;
filter = '';
showFilter = false;
coursesLoaded = false;
downloadAllCoursesIcon = CoreConstants.ICON_NOT_DOWNLOADED;
downloadAllCoursesLoading = false;
downloadAllCoursesBadge = '';
downloadAllCoursesEnabled = false;
downloadAllCoursesCount?: number;
downloadAllCoursesTotal?: number;
downloadAllCoursesBadgeA11yText = '';
protected myCoursesObserver: CoreEventObserver;
protected siteUpdatedObserver: CoreEventObserver;
protected isDestroyed = false;
protected courseIds = '';
constructor() {
// Update list if user enrols in a course.
this.myCoursesObserver = CoreEvents.on(
CoreCoursesProvider.EVENT_MY_COURSES_UPDATED,
(data) => {
if (data.action == CoreCoursesProvider.ACTION_ENROL) {
this.fetchCourses();
}
},
CoreSites.getCurrentSiteId(),
);
// Refresh the enabled flags if site is updated.
this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.downloadAllCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
}, CoreSites.getCurrentSiteId());
}
/**
* Component being initialized.
*/
ngOnInit(): void {
this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
this.downloadAllCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite();
this.fetchCourses().finally(() => {
this.coursesLoaded = true;
});
}
/**
* Fetch the user courses.
*
* @return Promise resolved when done.
*/
protected async fetchCourses(): Promise<void> {
try {
const courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[] = await CoreCourses.getUserCourses();
const courseIds = courses.map((course) => course.id);
this.courseIds = courseIds.join(',');
await CoreCoursesHelper.loadCoursesExtraInfo(courses);
const options = await CoreCourses.getCoursesAdminAndNavOptions(courseIds);
courses.forEach((course) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];
});
this.courses = courses;
this.filteredCourses = this.courses;
this.filter = '';
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
}
}
/**
* Refresh the courses.
*
* @param refresher Refresher.
*/
refreshCourses(refresher: IonRefresher): void {
const promises: Promise<void>[] = [];
promises.push(CoreCourses.invalidateUserCourses());
promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions());
if (this.courseIds) {
promises.push(CoreCourses.invalidateCoursesByField('ids', this.courseIds));
}
Promise.all(promises).finally(() => {
this.fetchCourses().finally(() => {
refresher?.complete();
});
});
}
/**
* Show or hide the filter.
*/
switchFilter(): void {
this.filter = '';
this.showFilter = !this.showFilter;
this.filteredCourses = this.courses;
if (this.showFilter) {
setTimeout(() => {
this.searchbar.setFocus();
}, 500);
}
}
/**
* The filter has changed.
*
* @param Received Event.
*/
filterChanged(event?: Event): void {
const target = <HTMLInputElement>event?.target || null;
const newValue = target ? String(target.value).trim().toLowerCase() : null;
if (!newValue || !this.courses) {
this.filteredCourses = this.courses;
} else {
// Use displayname if available, or fullname if not.
if (this.courses.length > 0 && typeof this.courses[0].displayname != 'undefined') {
this.filteredCourses = this.courses.filter((course) => course.displayname!.toLowerCase().indexOf(newValue) > -1);
} else {
this.filteredCourses = this.courses.filter((course) => course.fullname.toLowerCase().indexOf(newValue) > -1);
}
}
}
/**
* Prefetch all the courses.
*
* @return Promise resolved when done.
*/
async prefetchCourses(): Promise<void> {
this.downloadAllCoursesLoading = true;
try {
await CoreCourseHelper.confirmAndPrefetchCourses(this.courses, { onProgress: (progress) => {
this.downloadAllCoursesBadge = progress.count + ' / ' + progress.total;
this.downloadAllCoursesBadgeA11yText =
Translate.instant('core.course.downloadcoursesprogressdescription', progress);
this.downloadAllCoursesCount = progress.count;
this.downloadAllCoursesTotal = progress.total;
} });
} catch (error) {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
}
}
this.downloadAllCoursesBadge = '';
this.downloadAllCoursesLoading = false;
}
/**
* Go to search courses.
*/
openSearch(): void {
CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'search' } });
}
/**
* Page destroyed.
*/
ngOnDestroy(): void {
this.isDestroyed = true;
this.myCoursesObserver?.off();
this.siteUpdatedObserver?.off();
}
}

View File

@ -20,6 +20,7 @@ import { makeSingleton } from '@singletons';
import { CoreStatusWithWarningsWSResponse, CoreWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
import { CoreEvents } from '@singletons/events';
import { CoreWSError } from '@classes/errors/wserror';
import { CoreCourseWithImageAndColor } from './courses-helper';
const ROOT_CACHE_KEY = 'mmCourses:';
@ -1121,6 +1122,7 @@ export class CoreCoursesProvider {
* @param text Text to search.
* @param page Page to get.
* @param perPage Number of courses per page. Defaults to CoreCoursesProvider.SEARCH_PER_PAGE.
* @param limitToEnrolled Limit to enrolled courses.
* @param siteId Site ID. If not defined, use current site.
* @return Promise resolved with the courses and the total of matches.
*/
@ -1128,6 +1130,7 @@ export class CoreCoursesProvider {
text: string,
page: number = 0,
perPage: number = CoreCoursesProvider.SEARCH_PER_PAGE,
limitToEnrolled: boolean = false,
siteId?: string,
): Promise<{ total: number; courses: CoreCourseBasicSearchedData[] }> {
const site = await CoreSites.getSite(siteId);
@ -1136,6 +1139,7 @@ export class CoreCoursesProvider {
criteriavalue: text,
page: page,
perpage: perPage,
limittoenrolled: limitToEnrolled,
};
const preSets: CoreSiteWSPreSets = {
getFromCache: false,
@ -1358,6 +1362,14 @@ export type CoreCourseSearchedData = CoreCourseBasicSearchedData & {
courseformatoptions?: CoreCourseFormatOption[]; // Additional options for particular course format.
};
/**
* Course to render as list item.
*/
export type CoreCourseListItem = CoreCourseSearchedData & CoreCourseWithImageAndColor & {
completionusertracked?: boolean; // If the user is completion tracked.
progress?: number | null; // Progress percentage.
};
export type CoreCourseGetCoursesData = CoreEnrolledCourseBasicData & {
categoryid: number; // Category id.
categorysortorder?: number; // Sort order into the category.

View File

@ -223,7 +223,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
* Go to my courses.
*/
openMyCourses(): void {
CoreNavigator.navigateToSitePath('courses/my');
CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'my' } });
}
/**