diff --git a/src/core/features/courses/courses-lazy.module.ts b/src/core/features/courses/courses-lazy.module.ts
index 0fa55f3fb..ad6fc7cdf 100644
--- a/src/core/features/courses/courses-lazy.module.ts
+++ b/src/core/features/courses/courses-lazy.module.ts
@@ -33,16 +33,10 @@ const routes: Routes = [
.then(m => m.CoreCoursesCategoriesPageModule),
},
{
- path: 'all',
+ path: 'list',
loadChildren: () =>
- import('./pages/available-courses/available-courses.module')
- .then(m => m.CoreCoursesAvailableCoursesPageModule),
- },
- {
- path: 'search',
- loadChildren: () =>
- import('./pages/search/search.module')
- .then(m => m.CoreCoursesSearchPageModule),
+ import('./pages/list/list.module')
+ .then(m => m.CoreCoursesListPageModule),
},
{
path: 'my',
diff --git a/src/core/features/courses/pages/available-courses/available-courses.html b/src/core/features/courses/pages/available-courses/available-courses.html
deleted file mode 100644
index 07aebce00..000000000
--- a/src/core/features/courses/pages/available-courses/available-courses.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
- {{ 'core.courses.availablecourses' | translate }}
-
-
-
-
-
-
-
- 0">
-
-
-
-
-
diff --git a/src/core/features/courses/pages/available-courses/available-courses.module.ts b/src/core/features/courses/pages/available-courses/available-courses.module.ts
deleted file mode 100644
index c272cfa72..000000000
--- a/src/core/features/courses/pages/available-courses/available-courses.module.ts
+++ /dev/null
@@ -1,41 +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 { CoreCoursesComponentsModule } from '../../components/components.module';
-
-import { CoreCoursesAvailableCoursesPage } from './available-courses';
-
-const routes: Routes = [
- {
- path: '',
- component: CoreCoursesAvailableCoursesPage,
- },
-];
-
-@NgModule({
- imports: [
- RouterModule.forChild(routes),
- CoreSharedModule,
- CoreCoursesComponentsModule,
- ],
- declarations: [
- CoreCoursesAvailableCoursesPage,
- ],
- exports: [RouterModule],
-})
-export class CoreCoursesAvailableCoursesPageModule { }
diff --git a/src/core/features/courses/pages/available-courses/available-courses.ts b/src/core/features/courses/pages/available-courses/available-courses.ts
deleted file mode 100644
index c7a5b70ef..000000000
--- a/src/core/features/courses/pages/available-courses/available-courses.ts
+++ /dev/null
@@ -1,78 +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 } from '@angular/core';
-import { IonRefresher } from '@ionic/angular';
-
-import { CoreSites } from '@services/sites';
-import { CoreDomUtils } from '@services/utils/dom';
-import { CoreCourses, CoreCourseSearchedData } from '../../services/courses';
-
-/**
- * Page that displays available courses in current site.
- */
-@Component({
- selector: 'page-core-courses-available-courses',
- templateUrl: 'available-courses.html',
-})
-export class CoreCoursesAvailableCoursesPage implements OnInit {
-
- courses: CoreCourseSearchedData[] = [];
- coursesLoaded = false;
-
- /**
- * View loaded.
- */
- ngOnInit(): void {
- this.loadCourses().finally(() => {
- this.coursesLoaded = true;
- });
- }
-
- /**
- * Load the courses.
- *
- * @return Promise resolved when done.
- */
- protected async loadCourses(): Promise {
- const frontpageCourseId = CoreSites.getCurrentSiteHomeId();
-
- try {
- const courses = await CoreCourses.getCoursesByField();
-
- this.courses = courses.filter((course) => course.id != frontpageCourseId);
- } catch (error) {
- CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
- }
- }
-
- /**
- * Refresh the courses.
- *
- * @param refresher Refresher.
- */
- refreshCourses(refresher: IonRefresher): void {
- const promises: Promise[] = [];
-
- promises.push(CoreCourses.invalidateUserCourses());
- promises.push(CoreCourses.invalidateCoursesByField());
-
- Promise.all(promises).finally(() => {
- this.loadCourses().finally(() => {
- refresher?.complete();
- });
- });
- }
-
-}
diff --git a/src/core/features/courses/pages/dashboard/dashboard.ts b/src/core/features/courses/pages/dashboard/dashboard.ts
index 1c67c49c9..56ef7d56c 100644
--- a/src/core/features/courses/pages/dashboard/dashboard.ts
+++ b/src/core/features/courses/pages/dashboard/dashboard.ts
@@ -167,7 +167,7 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
* Go to search courses.
*/
async openSearch(): Promise {
- CoreNavigator.navigateToSitePath('/courses/search');
+ CoreNavigator.navigateToSitePath('/courses/list', { params : { mode: 'search' } });
}
/**
diff --git a/src/core/features/courses/pages/list/list.html b/src/core/features/courses/pages/list/list.html
new file mode 100644
index 000000000..3b2471ad6
--- /dev/null
+++ b/src/core/features/courses/pages/list/list.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ {{ 'core.courses.availablecourses' | translate }}
+
+
+
+
+
+
+
+
+
+
+ 0">
+
+ {{ 'core.courses.totalcoursesearchresults' | translate:{$a: searchTotal} }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/courses/pages/search/search.module.ts b/src/core/features/courses/pages/list/list.module.ts
similarity index 87%
rename from src/core/features/courses/pages/search/search.module.ts
rename to src/core/features/courses/pages/list/list.module.ts
index 497e6f4d1..c0d3a2c36 100644
--- a/src/core/features/courses/pages/search/search.module.ts
+++ b/src/core/features/courses/pages/list/list.module.ts
@@ -19,12 +19,12 @@ import { CoreSharedModule } from '@/core/shared.module';
import { CoreCoursesComponentsModule } from '../../components/components.module';
import { CoreSearchComponentsModule } from '@features/search/components/components.module';
-import { CoreCoursesSearchPage } from './search';
+import { CoreCoursesListPage } from './list';
const routes: Routes = [
{
path: '',
- component: CoreCoursesSearchPage,
+ component: CoreCoursesListPage,
},
];
@@ -36,8 +36,8 @@ const routes: Routes = [
CoreSearchComponentsModule,
],
declarations: [
- CoreCoursesSearchPage,
+ CoreCoursesListPage,
],
exports: [RouterModule],
})
-export class CoreCoursesSearchPageModule { }
+export class CoreCoursesListPageModule { }
diff --git a/src/core/features/courses/pages/list/list.ts b/src/core/features/courses/pages/list/list.ts
new file mode 100644
index 000000000..12c0cf317
--- /dev/null
+++ b/src/core/features/courses/pages/list/list.ts
@@ -0,0 +1,211 @@
+// (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, OnDestroy, OnInit } from '@angular/core';
+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';
+
+type CoreCoursesListMode = 'search' | 'all';
+
+/**
+ * Page that shows a list of courses.
+ */
+@Component({
+ selector: 'page-core-courses-list',
+ templateUrl: 'list.html',
+})
+export class CoreCoursesListPage implements OnInit, OnDestroy {
+
+ searchEnabled = false;
+ searchMode = false;
+ searchCanLoadMore = false;
+ searchLoadMoreError = false;
+ searchTotal = 0;
+
+ mode: CoreCoursesListMode = 'all';
+
+ courses: CoreCourseBasicSearchedData[] = [];
+ coursesLoaded = false;
+
+ protected currentSiteId: string;
+ protected frontpageCourseId: number;
+ protected searchPage = 0;
+ protected searchText = '';
+ protected siteUpdatedObserver: CoreEventObserver;
+
+ constructor() {
+ this.currentSiteId = CoreSites.getRequiredCurrentSite().getId();
+ this.frontpageCourseId = CoreSites.getRequiredCurrentSite().getSiteHomeId();
+
+ // Refresh the enabled flags if site is updated.
+ this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
+ this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
+
+ if (!this.searchEnabled) {
+ this.searchMode = false;
+
+ this.fetchCourses();
+ }
+ }, this.currentSiteId);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnInit(): void {
+ this.mode = CoreNavigator.getRouteParam('mode') || this.mode;
+
+ if (this.mode == 'search') {
+ this.searchMode = true;
+ }
+
+ this.searchEnabled = !CoreCourses.isSearchCoursesDisabledInSite();
+ if (!this.searchEnabled) {
+ this.searchMode = false;
+ }
+
+ this.fetchCourses();
+ }
+
+ /**
+ * Load the course list.
+ *
+ * @return Promise resolved when done.
+ */
+ protected async fetchCourses(): Promise {
+ try {
+ if (this.searchMode && this.searchText) {
+ await this.search(this.searchText);
+ } else {
+ await this.loadAvailableCourses();
+ }
+ } finally {
+ this.coursesLoaded = true;
+ }
+ }
+
+ /**
+ * Load the courses.
+ *
+ * @return Promise resolved when done.
+ */
+ protected async loadAvailableCourses(): Promise {
+ try {
+ const courses = await CoreCourses.getCoursesByField();
+
+ this.courses = courses.filter((course) => course.id != this.frontpageCourseId);
+ } catch (error) {
+ CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorloadcourses', true);
+ }
+ }
+
+ /**
+ * Refresh the courses.
+ *
+ * @param refresher Refresher.
+ */
+ refreshCourses(refresher: IonRefresher): void {
+ const promises: Promise[] = [];
+
+ promises.push(CoreCourses.invalidateUserCourses());
+ promises.push(CoreCourses.invalidateCoursesByField());
+
+ Promise.all(promises).finally(() => {
+ this.fetchCourses().finally(() => {
+ refresher?.complete();
+ });
+ });
+ }
+
+ /**
+ * Search a new text.
+ *
+ * @param text The text to search.
+ */
+ async search(text: string): Promise {
+ this.searchMode = true;
+ this.searchText = text;
+ this.courses = [];
+ this.searchPage = 0;
+ this.searchTotal = 0;
+
+ const modal = await CoreDomUtils.showModalLoading('core.searching', true);
+ this.searchCourses().finally(() => {
+ modal.dismiss();
+ });
+ }
+
+ /**
+ * Clear search box.
+ */
+ clearSearch(): void {
+ this.searchText = '';
+ this.courses = [];
+ this.searchPage = 0;
+ this.searchTotal = 0;
+ this.searchMode = false;
+
+ this.coursesLoaded = false;
+ this.fetchCourses();
+ }
+
+ /**
+ * Load more results.
+ *
+ * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
+ */
+ loadMoreResults(infiniteComplete?: () => void ): void {
+ this.searchCourses().finally(() => {
+ infiniteComplete && infiniteComplete();
+ });
+ }
+
+ /**
+ * Search courses or load the next page of current search.
+ *
+ * @return Promise resolved when done.
+ */
+ protected async searchCourses(): Promise {
+ this.searchLoadMoreError = false;
+
+ try {
+ const response = await CoreCourses.search(this.searchText, this.searchPage);
+
+ if (this.searchPage === 0) {
+ this.courses = response.courses;
+ } else {
+ this.courses = this.courses.concat(response.courses);
+ }
+ this.searchTotal = response.total;
+
+ this.searchPage++;
+ 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);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnDestroy(): void {
+ this.siteUpdatedObserver?.off();
+ }
+
+}
diff --git a/src/core/features/courses/pages/my-courses/my-courses.ts b/src/core/features/courses/pages/my-courses/my-courses.ts
index 576d7e8df..675933daf 100644
--- a/src/core/features/courses/pages/my-courses/my-courses.ts
+++ b/src/core/features/courses/pages/my-courses/my-courses.ts
@@ -204,7 +204,7 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
* Go to search courses.
*/
openSearch(): void {
- CoreNavigator.navigateToSitePath('courses/search');
+ CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'search' } });
}
/**
diff --git a/src/core/features/courses/pages/search/search.html b/src/core/features/courses/pages/search/search.html
deleted file mode 100644
index fb33940a9..000000000
--- a/src/core/features/courses/pages/search/search.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
- {{ 'core.courses.searchcourses' | translate }}
-
-
-
-
-
- 0">
-
- {{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }}
-
-
-
-
-
-
-
diff --git a/src/core/features/courses/pages/search/search.ts b/src/core/features/courses/pages/search/search.ts
deleted file mode 100644
index 651aee76f..000000000
--- a/src/core/features/courses/pages/search/search.ts
+++ /dev/null
@@ -1,100 +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 } from '@angular/core';
-import { CoreDomUtils } from '@services/utils/dom';
-import { CoreCourseBasicSearchedData, CoreCourses } from '../../services/courses';
-
-/**
- * Page that allows searching for courses.
- */
-@Component({
- selector: 'page-core-courses-search',
- templateUrl: 'search.html',
-})
-export class CoreCoursesSearchPage {
-
- total = 0;
- courses: CoreCourseBasicSearchedData[] = [];
- canLoadMore = false;
- loadMoreError = false;
-
- protected page = 0;
- protected currentSearch = '';
-
- /**
- * Search a new text.
- *
- * @param text The text to search.
- */
- async search(text: string): Promise {
- this.currentSearch = text;
- this.courses = [];
- this.page = 0;
- this.total = 0;
-
- const modal = await CoreDomUtils.showModalLoading('core.searching', true);
- this.searchCourses().finally(() => {
- modal.dismiss();
- });
- }
-
- /**
- * Clear search box.
- */
- clearSearch(): void {
- this.currentSearch = '';
- this.courses = [];
- this.page = 0;
- this.total = 0;
- }
-
- /**
- * Load more results.
- *
- * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
- */
- loadMoreResults(infiniteComplete?: () => void ): void {
- this.searchCourses().finally(() => {
- infiniteComplete && infiniteComplete();
- });
- }
-
- /**
- * Search courses or load the next page of current search.
- *
- * @return Promise resolved when done.
- */
- protected async searchCourses(): Promise {
- this.loadMoreError = false;
-
- try {
- const response = await CoreCourses.search(this.currentSearch, this.page);
-
- if (this.page === 0) {
- this.courses = response.courses;
- } else {
- this.courses = this.courses.concat(response.courses);
- }
- this.total = response.total;
-
- this.page++;
- this.canLoadMore = this.courses.length < this.total;
- } catch (error) {
- this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
- CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorsearching', true);
- }
- }
-
-}
diff --git a/src/core/features/courses/services/handlers/courses-index-link.ts b/src/core/features/courses/services/handlers/courses-index-link.ts
index 500287239..2ff9c3f92 100644
--- a/src/core/features/courses/services/handlers/courses-index-link.ts
+++ b/src/core/features/courses/services/handlers/courses-index-link.ts
@@ -42,14 +42,17 @@ export class CoreCoursesIndexLinkHandlerService extends CoreContentLinksHandlerB
return [{
action: (siteId): void => {
let pageName = CoreCoursesMyCoursesHomeHandlerService.PAGE_NAME;
+ const pageParams: Params = {};
if (params.categoryid) {
pageName += '/categories/' + params.categoryid;
} else {
- pageName += '/all';
+ pageName += '/list';
+ pageParams.mode = 'all';
}
- CoreNavigator.navigateToSitePath(pageName, { siteId });
+
+ CoreNavigator.navigateToSitePath(pageName, { params: pageParams, siteId });
},
}];
}
diff --git a/src/core/features/sitehome/pages/index/index.ts b/src/core/features/sitehome/pages/index/index.ts
index 7f59dae6d..fd1796d53 100644
--- a/src/core/features/sitehome/pages/index/index.ts
+++ b/src/core/features/sitehome/pages/index/index.ts
@@ -218,14 +218,14 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
* Go to search courses.
*/
openSearch(): void {
- CoreNavigator.navigateToSitePath('courses/search');
+ CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'search' } });
}
/**
* Go to available courses.
*/
openAvailableCourses(): void {
- CoreNavigator.navigateToSitePath('courses/all');
+ CoreNavigator.navigateToSitePath('courses/list', { params : { mode: 'all' } });
}
/**