Merge pull request #1525 from crazyserver/MOBILE-2554

Mobile 2554
main
Juan Leyva 2018-09-21 16:45:49 +02:00 committed by GitHub
commit c3fc445f39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 212 additions and 69 deletions

View File

@ -29,9 +29,7 @@
</a> </a>
</ion-list> </ion-list>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="$event.waitFor(fetchEvents())"> <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreEvents($event)"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</core-loading> </core-loading>
</ion-content> </ion-content>
</core-split-view> </core-split-view>

View File

@ -179,6 +179,18 @@ export class AddonCalendarListPage implements OnDestroy {
}); });
} }
/**
* Function to load more events.
*
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
* @return {Promise<any>} Resolved when done.
*/
loadMoreEvents(infiniteComplete?: any): Promise<any> {
return this.fetchEvents().finally(() => {
infiniteComplete && infiniteComplete();
});
}
/** /**
* Get filtered events. * Get filtered events.
* *

View File

@ -15,9 +15,7 @@
<ion-content class="has-footer"> <ion-content class="has-footer">
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<!-- Load previous messages. --> <!-- Load previous messages. -->
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="loadPrevious($event)" position="top"> <core-infinite-loading [enabled]="canLoadMore" (action)="loadPrevious($event)" position="top"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-list class="addon-messages-discussion-container" [attr.aria-live]="polite"> <ion-list class="addon-messages-discussion-container" [attr.aria-live]="polite">
<ng-container *ngFor="let message of messages; index as index; last as last"> <ng-container *ngFor="let message of messages; index as index; last as last">
<ion-chip *ngIf="showDate(message, messages[index - 1])" class="addon-messages-date" color="light"> <ion-chip *ngIf="showDate(message, messages[index - 1])" class="addon-messages-date" color="light">

View File

@ -543,10 +543,10 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
/** /**
* Function to load previous messages. * Function to load previous messages.
* *
* @param {any} [infiniteScroll] Infinite scroll object. * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
* @return {Promise<any>} Resolved when done. * @return {Promise<any>} Resolved when done.
*/ */
loadPrevious(infiniteScroll: any): Promise<any> { loadPrevious(infiniteComplete?: any): Promise<any> {
// If there is an ongoing fetch, wait for it to finish. // If there is an ongoing fetch, wait for it to finish.
return this.waitForFetch().finally(() => { return this.waitForFetch().finally(() => {
this.pagesLoaded++; this.pagesLoaded++;
@ -555,7 +555,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy {
this.pagesLoaded--; this.pagesLoaded--;
this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
}).finally(() => { }).finally(() => {
infiniteScroll.complete(); infiniteComplete && infiniteComplete();
}); });
}); });
} }

View File

@ -88,9 +88,7 @@
</div> </div>
</core-empty-box> </core-empty-box>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="$event.waitFor(fetchMoreDiscussions())"> <core-infinite-loading [enabled]="canLoadMore" (action)="fetchMoreDiscussions($event)"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</core-loading> </core-loading>
<ion-fab bottom end *ngIf="forum && forum.cancreatediscussions"> <ion-fab bottom end *ngIf="forum && forum.cancreatediscussions">

View File

@ -305,13 +305,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
/** /**
* Convenience function to load more forum discussions. * Convenience function to load more forum discussions.
* *
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchMoreDiscussions(): Promise<any> { fetchMoreDiscussions(infiniteComplete?: any): Promise<any> {
return this.fetchDiscussions(false).catch((message) => { return this.fetchDiscussions(false).catch((message) => {
this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true); this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true);
this.canLoadMore = false; // Set to false to prevent infinite calls with infinite-loading. this.canLoadMore = false; // Set to false to prevent infinite calls with infinite-loading.
}).finally(() => {
infiniteComplete && infiniteComplete();
}); });
} }

View File

@ -56,9 +56,7 @@
<core-empty-box *ngIf="!entries.length && !offlineEntries.length" icon="list" [message]="'addon.mod_glossary.noentriesfound' | translate"></core-empty-box> <core-empty-box *ngIf="!entries.length && !offlineEntries.length" icon="list" [message]="'addon.mod_glossary.noentriesfound' | translate"></core-empty-box>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="$event.waitFor(loadMoreEntries())"> <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreEntries($event)"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</core-loading> </core-loading>
<ion-fab bottom end *ngIf="canAdd"> <ion-fab bottom end *ngIf="canAdd">

View File

@ -289,11 +289,14 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
/** /**
* Convenience function to load more forum discussions. * Convenience function to load more forum discussions.
* *
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
loadMoreEntries(): Promise<any> { loadMoreEntries(infiniteComplete?: any): Promise<any> {
return this.fetchEntries(true).catch((error) => { return this.fetchEntries(true).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentries', true); this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentries', true);
}).finally(() => {
infiniteComplete && infiniteComplete();
}); });
} }

View File

@ -32,8 +32,6 @@
<addon-notifications-actions [contextUrl]="notification.contexturl" [courseId]="notification.courseid"></addon-notifications-actions> <addon-notifications-actions [contextUrl]="notification.contexturl" [courseId]="notification.courseid"></addon-notifications-actions>
</ion-card> </ion-card>
<core-empty-box *ngIf="!notifications || notifications.length <= 0" icon="notifications" [message]="'addon.notifications.therearentnotificationsyet' | translate"></core-empty-box> <core-empty-box *ngIf="!notifications || notifications.length <= 0" icon="notifications" [message]="'addon.notifications.therearentnotificationsyet' | translate"></core-empty-box>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="loadMoreNotifications($event)"> <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreNotifications($event)"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</core-loading> </core-loading>
</ion-content> </ion-content>

View File

@ -204,11 +204,11 @@ export class AddonNotificationsListPage {
/** /**
* Load more results. * Load more results.
* *
* @param {any} infiniteScroll The infinit scroll instance. * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
*/ */
loadMoreNotifications(infiniteScroll: any): void { loadMoreNotifications(infiniteComplete?: any): void {
this.fetchNotifications().finally(() => { this.fetchNotifications().finally(() => {
infiniteScroll.complete(); infiniteComplete && infiniteComplete();
}); });
} }

View File

@ -49,6 +49,7 @@ import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar';
import { CoreAttachmentsComponent } from './attachments/attachments'; import { CoreAttachmentsComponent } from './attachments/attachments';
import { CoreIonTabsComponent } from './ion-tabs/ion-tabs'; import { CoreIonTabsComponent } from './ion-tabs/ion-tabs';
import { CoreIonTabComponent } from './ion-tabs/ion-tab'; import { CoreIonTabComponent } from './ion-tabs/ion-tab';
import { CoreInfiniteLoadingComponent } from './infinite-loading/infinite-loading';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -83,7 +84,8 @@ import { CoreIonTabComponent } from './ion-tabs/ion-tab';
CoreNavigationBarComponent, CoreNavigationBarComponent,
CoreAttachmentsComponent, CoreAttachmentsComponent,
CoreIonTabsComponent, CoreIonTabsComponent,
CoreIonTabComponent CoreIonTabComponent,
CoreInfiniteLoadingComponent
], ],
entryComponents: [ entryComponents: [
CoreContextMenuPopoverComponent, CoreContextMenuPopoverComponent,
@ -125,7 +127,8 @@ import { CoreIonTabComponent } from './ion-tabs/ion-tab';
CoreNavigationBarComponent, CoreNavigationBarComponent,
CoreAttachmentsComponent, CoreAttachmentsComponent,
CoreIonTabsComponent, CoreIonTabsComponent,
CoreIonTabComponent CoreIonTabComponent,
CoreInfiniteLoadingComponent
] ]
}) })
export class CoreComponentsModule {} export class CoreComponentsModule {}

View File

@ -0,0 +1,19 @@
<div *ngIf="enabled && !loadingMore && position != 'top'" padding-horizontal>
<button ion-button block (click)="loadMore()" color="light">
{{'core.loadmore' | translate }}
</button>
</div>
<ion-infinite-scroll [enabled]="enabled && !loadingMore" (ionInfinite)="loadMore($event)" [position]="position">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<div *ngIf="enabled && !loadingMore && position == 'top'" padding-horizontal>
<button ion-button block (click)="loadMore()" color="light">
{{'core.loadmore' | translate }}
</button>
</div>
<div *ngIf="loadingMore" padding text-center>
<ion-spinner></ion-spinner>
</div>

View File

@ -0,0 +1,68 @@
// (C) Copyright 2015 Martin Dougiamas
//
// 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, Input, Output, EventEmitter } from '@angular/core';
import { InfiniteScroll } from 'ionic-angular';
/**
* Component to show a infinite loading trigger and spinner while more data is being loaded.
*
* Usage:
* <core-infinite-loading [action]="loadingAction" [enabled]="dataLoaded"></core-inifinite-loading>
*/
@Component({
selector: 'core-infinite-loading',
templateUrl: 'core-infinite-loading.html',
})
export class CoreInfiniteLoadingComponent {
@Input() enabled: boolean;
@Input() position = 'bottom';
@Output() action: EventEmitter<() => void>; // Will emit an event when triggered.
loadingMore = false; // Hide button and avoid loading more.
protected infiniteScroll: InfiniteScroll;
constructor() {
this.action = new EventEmitter();
}
/**
* Load More items calling the action provided.
*
* @param {InfiniteScroll} [infiniteScroll] Infinite scroll object only if triggered from the scroll.
*/
loadMore(infiniteScroll?: InfiniteScroll): void {
if (this.loadingMore) {
return;
}
if (infiniteScroll) {
this.infiniteScroll = infiniteScroll;
}
this.loadingMore = true;
this.action.emit(this.complete.bind(this));
}
/**
* Complete loading.
*/
complete(): void {
this.loadingMore = false;
this.infiniteScroll && this.infiniteScroll.complete();
this.infiniteScroll = undefined;
}
}

View File

@ -44,10 +44,14 @@
<!-- Multiple sections. --> <!-- Multiple sections. -->
<div *ngIf="selectedSection && selectedSection.id == allSectionsId"> <div *ngIf="selectedSection && selectedSection.id == allSectionsId">
<core-dynamic-component [component]="allSectionsComponent" [data]="data"> <core-dynamic-component [component]="allSectionsComponent" [data]="data">
<ng-container *ngFor="let section of sections"> <ng-container *ngFor="let section of sections; index as i">
<ng-container *ngIf="i <= showSectionId">
<ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section}"></ng-container> <ng-container *ngTemplateOutlet="sectionTemplate; context: {section: section}"></ng-container>
</ng-container> </ng-container>
</ng-container>
</core-dynamic-component> </core-dynamic-component>
<core-infinite-loading [enabled]="canLoadMore" (action)="showMoreActivities($event)"></core-infinite-loading>
</div> </div>
</core-loading> </core-loading>
<ion-buttons padding end class="core-course-section-nav-buttons" *ngIf="displaySectionSelector && sections && sections.length"> <ion-buttons padding end class="core-course-section-nav-buttons" *ngIf="displaySectionSelector && sections && sections.length">

View File

@ -41,6 +41,8 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
templateUrl: 'core-course-format.html' templateUrl: 'core-course-format.html'
}) })
export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
static LOAD_MORE_ACTIVITIES = 20; // How many activities should load each time showMoreActivities is called.
@Input() course: any; // The course to render. @Input() course: any; // The course to render.
@Input() sections: any[]; // List of course sections. @Input() sections: any[]; // List of course sections.
@Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
@ -57,6 +59,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
sectionSelectorComponent: any; sectionSelectorComponent: any;
singleSectionComponent: any; singleSectionComponent: any;
allSectionsComponent: any; allSectionsComponent: any;
canLoadMore = false;
showSectionId = 0;
// Data to pass to the components. // Data to pass to the components.
data: any = {}; data: any = {};
@ -273,6 +277,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} else { } else {
this.previousSection = null; this.previousSection = null;
this.nextSection = null; this.nextSection = null;
this.canLoadMore = false;
this.showSectionId = 0;
this.showMoreActivities();
} }
if (this.moduleId && typeof previousValue == 'undefined') { if (this.moduleId && typeof previousValue == 'undefined') {
@ -363,6 +370,46 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* Show more activities (only used when showing all the sections at the same time).
*
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
*/
showMoreActivities(infiniteComplete?: any): void {
this.canLoadMore = false;
let modulesLoaded = 0,
i;
for (i = this.showSectionId + 1; i < this.sections.length; i++) {
if (this.sections[i].hasContent && this.sections[i].modules) {
modulesLoaded += this.sections[i].modules.reduce((total, module) => {
return module.visibleoncoursepage !== 0 ? total + 1 : total;
}, 0);
if (modulesLoaded >= CoreCourseFormatComponent.LOAD_MORE_ACTIVITIES) {
this.showSectionId = i;
break;
}
}
}
this.canLoadMore = i < this.sections.length;
if (this.canLoadMore) {
// Check if any of the following sections have any content.
let thereAreMore = false;
for (i++; i < this.sections.length; i++) {
if (this.sections[i].hasContent && this.sections[i].modules && this.sections[i].modules.length > 0) {
thereAreMore = true;
break;
}
}
this.canLoadMore = thereAreMore;
}
infiniteComplete && infiniteComplete();
}
/** /**
* Component destroyed. * Component destroyed.
*/ */

View File

@ -47,8 +47,8 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
const now = this.timeUtils.timestamp(); const now = this.timeUtils.timestamp();
if (now < course.startdate || (course.enddate && now > course.enddate)) { if (now < course.startdate || (course.enddate && now > course.enddate)) {
// Course hasn't started yet or it has ended already. Return the first section. // Course hasn't started yet or it has ended already. Return all sections.
return sections[1]; return sections[0];
} }
for (let i = 0; i < sections.length; i++) { for (let i = 0; i < sections.length; i++) {
@ -63,8 +63,8 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
} }
} }
// The section wasn't found, return the first section. // The section wasn't found, return all sections.
return sections[1]; return sections[0];
} }
/** /**

View File

@ -16,7 +16,6 @@ import { Injectable } from '@angular/core';
import { NavController } from 'ionic-angular'; import { NavController } from 'ionic-angular';
import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreCoursesProvider } from '@core/courses/providers/courses';
import { CoreCourseFormatHandler } from './format-delegate'; import { CoreCourseFormatHandler } from './format-delegate';
import { CoreCourseProvider } from './course';
/** /**
* Default handler used when the course format doesn't have a specific implementation. * Default handler used when the course format doesn't have a specific implementation.
@ -98,38 +97,28 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
*/ */
getCurrentSection(course: any, sections: any[]): any | Promise<any> { getCurrentSection(course: any, sections: any[]): any | Promise<any> {
if (!this.coursesProvider.isGetCoursesByFieldAvailable()) { if (!this.coursesProvider.isGetCoursesByFieldAvailable()) {
// Cannot get the current section, return the first one. // Cannot get the current section, return all of them.
if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
return sections[0]; return sections[0];
} }
return sections[1];
}
// We need the "marker" to determine the current section. // We need the "marker" to determine the current section.
return this.coursesProvider.getCoursesByField('id', course.id).catch(() => { return this.coursesProvider.getCoursesByField('id', course.id).catch(() => {
// Ignore errors. // Ignore errors.
}).then((courses) => { }).then((courses) => {
if (courses && courses[0]) { if (courses && courses[0] && courses[0].marker > 0) {
// Find the marked section. // Find the marked section.
const course = courses[0]; const course = courses[0],
for (let i = 0; i < sections.length; i++) { section = sections.find((sect) => {
const section = sections[i]; return sect.section == course.marker;
if (section.section == course.marker) { });
return section;
}
}
}
// Marked section not found or we couldn't retrieve the marker. Return the first section. if (section) {
for (let i = 0; i < sections.length; i++) {
const section = sections[i];
if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
return section; return section;
} }
} }
return Promise.reject(null); // Marked section not found or we couldn't retrieve the marker. Return all sections.
return sections[0];
}); });
} }

View File

@ -17,7 +17,6 @@ import { NavController } from 'ionic-angular';
import { CoreEventsProvider } from '@providers/events'; import { CoreEventsProvider } from '@providers/events';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreCourseProvider } from './course';
import { CoreCourseFormatDefaultHandler } from './default-format'; import { CoreCourseFormatDefaultHandler } from './default-format';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
@ -285,14 +284,11 @@ export class CoreCourseFormatDelegate extends CoreDelegate {
* @return {Promise<any>} Promise resolved with current section. * @return {Promise<any>} Promise resolved with current section.
*/ */
getCurrentSection(course: any, sections: any[]): Promise<any> { getCurrentSection(course: any, sections: any[]): Promise<any> {
// Convert the result to a Promise if it isn't. // Convert the result to a Promise if it isn't.
return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCurrentSection', [course, sections])).catch(() => { return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCurrentSection', [course, sections])).catch(() => {
// This function should never fail. Just return the first section. // This function should never fail. Just return all the sections.
if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
return sections[0]; return sections[0];
}
return sections[1];
}); });
} }

View File

@ -10,9 +10,7 @@
<ion-item-divider color="light">{{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }}</ion-item-divider> <ion-item-divider color="light">{{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }}</ion-item-divider>
<core-empty-box *ngIf="total == 0" icon="search" [message]="'core.courses.nosearchresults' | translate"></core-empty-box> <core-empty-box *ngIf="total == 0" icon="search" [message]="'core.courses.nosearchresults' | translate"></core-empty-box>
<core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item> <core-courses-course-list-item *ngFor="let course of courses" [course]="course"></core-courses-course-list-item>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="loadMoreResults($event)"> <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreResults($event)"></core-infinite-loading>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</div> </div>
</ion-content> </ion-content>

View File

@ -54,11 +54,11 @@ export class CoreCoursesSearchPage {
/** /**
* Load more results. * Load more results.
* *
* @param {any} infiniteScroll The infinit scroll instance. * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
*/ */
loadMoreResults(infiniteScroll: any): void { loadMoreResults(infiniteComplete?: any): void {
this.searchCourses().finally(() => { this.searchCourses().finally(() => {
infiniteScroll.complete(); infiniteComplete && infiniteComplete();
}); });
} }

View File

@ -390,6 +390,7 @@ export class CoreCoursesProvider {
* @param {any} [value] The value to match. * @param {any} [value] The value to match.
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved with the courses. * @return {Promise<any[]>} Promise resolved with the courses.
* @since 3.2
*/ */
getCoursesByField(field?: string, value?: any, siteId?: string): Promise<any[]> { getCoursesByField(field?: string, value?: any, siteId?: string): Promise<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
@ -473,6 +474,7 @@ export class CoreCoursesProvider {
* Check if get courses by field WS is available. * Check if get courses by field WS is available.
* *
* @return {boolean} Whether get courses by field is available. * @return {boolean} Whether get courses by field is available.
* @since 3.2
*/ */
isGetCoursesByFieldAvailable(): boolean { isGetCoursesByFieldAvailable(): boolean {
return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_courses_by_field'); return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_courses_by_field');

View File

@ -16,10 +16,7 @@
<p *ngIf="participant.lastaccess"><strong>{{ 'core.lastaccess' | translate }}: </strong>{{ participant.lastaccess * 1000 | coreFormatDate:"dfmediumdate"}}</p> <p *ngIf="participant.lastaccess"><strong>{{ 'core.lastaccess' | translate }}: </strong>{{ participant.lastaccess * 1000 | coreFormatDate:"dfmediumdate"}}</p>
</a> </a>
</ion-list> </ion-list>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreData($event)"></core-infinite-loading>
<ion-infinite-scroll [enabled]="canLoadMore" (ionInfinite)="$event.waitFor(fetchData())">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</core-loading> </core-loading>
</ion-content> </ion-content>
</core-split-view> </core-split-view>

View File

@ -82,6 +82,18 @@ export class CoreUserParticipantsComponent implements OnInit {
}); });
} }
/**
* Function to load more data.
*
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
* @return {Promise<any>} Resolved when done.
*/
loadMoreData(infiniteComplete?: any): Promise<any> {
return this.fetchData().finally(() => {
infiniteComplete && infiniteComplete();
});
}
/** /**
* Refresh data. * Refresh data.
* *