diff --git a/src/addon/calendar/pages/list/list.html b/src/addon/calendar/pages/list/list.html index 5b8dfd808..33ec3cebc 100644 --- a/src/addon/calendar/pages/list/list.html +++ b/src/addon/calendar/pages/list/list.html @@ -29,9 +29,7 @@ - - - + \ No newline at end of file diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts index 324d4b76f..90d85d4e9 100644 --- a/src/addon/calendar/pages/list/list.ts +++ b/src/addon/calendar/pages/list/list.ts @@ -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} Resolved when done. + */ + loadMoreEvents(infiniteComplete?: any): Promise { + return this.fetchEvents().finally(() => { + infiniteComplete && infiniteComplete(); + }); + } + /** * Get filtered events. * diff --git a/src/addon/messages/pages/discussion/discussion.html b/src/addon/messages/pages/discussion/discussion.html index 89bd5a6be..da8d7e7c4 100644 --- a/src/addon/messages/pages/discussion/discussion.html +++ b/src/addon/messages/pages/discussion/discussion.html @@ -15,9 +15,7 @@ - - - + diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index e8cb226dc..3080b974a 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -543,10 +543,10 @@ export class AddonMessagesDiscussionPage implements OnDestroy { /** * 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} Resolved when done. */ - loadPrevious(infiniteScroll: any): Promise { + loadPrevious(infiniteComplete?: any): Promise { // If there is an ongoing fetch, wait for it to finish. return this.waitForFetch().finally(() => { this.pagesLoaded++; @@ -555,7 +555,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.pagesLoaded--; this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true); }).finally(() => { - infiniteScroll.complete(); + infiniteComplete && infiniteComplete(); }); }); } diff --git a/src/addon/mod/forum/components/index/addon-mod-forum-index.html b/src/addon/mod/forum/components/index/addon-mod-forum-index.html index 8d88b2378..463d9bbdf 100644 --- a/src/addon/mod/forum/components/index/addon-mod-forum-index.html +++ b/src/addon/mod/forum/components/index/addon-mod-forum-index.html @@ -88,9 +88,7 @@ - - - + diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts index 8ab76ab1f..685c671c0 100644 --- a/src/addon/mod/forum/components/index/index.ts +++ b/src/addon/mod/forum/components/index/index.ts @@ -305,13 +305,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom /** * Convenience function to load more forum discussions. * + * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading. * @return {Promise} Promise resolved when done. */ - protected fetchMoreDiscussions(): Promise { + fetchMoreDiscussions(infiniteComplete?: any): Promise { return this.fetchDiscussions(false).catch((message) => { this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true); this.canLoadMore = false; // Set to false to prevent infinite calls with infinite-loading. + }).finally(() => { + infiniteComplete && infiniteComplete(); }); } diff --git a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html index 4ad1add14..8745e6f6b 100644 --- a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html @@ -56,9 +56,7 @@ - - - + diff --git a/src/addon/mod/glossary/components/index/index.ts b/src/addon/mod/glossary/components/index/index.ts index 461178374..9cb87dd68 100644 --- a/src/addon/mod/glossary/components/index/index.ts +++ b/src/addon/mod/glossary/components/index/index.ts @@ -289,11 +289,14 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity /** * Convenience function to load more forum discussions. * + * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading. * @return {Promise} Promise resolved when done. */ - loadMoreEntries(): Promise { + loadMoreEntries(infiniteComplete?: any): Promise { return this.fetchEntries(true).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentries', true); + }).finally(() => { + infiniteComplete && infiniteComplete(); }); } diff --git a/src/addon/notifications/pages/list/list.html b/src/addon/notifications/pages/list/list.html index 05d17c75d..0ee4eb357 100644 --- a/src/addon/notifications/pages/list/list.html +++ b/src/addon/notifications/pages/list/list.html @@ -32,8 +32,6 @@ - - - + diff --git a/src/addon/notifications/pages/list/list.ts b/src/addon/notifications/pages/list/list.ts index 07de7da86..fe21ee62f 100644 --- a/src/addon/notifications/pages/list/list.ts +++ b/src/addon/notifications/pages/list/list.ts @@ -204,11 +204,11 @@ export class AddonNotificationsListPage { /** * 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(() => { - infiniteScroll.complete(); + infiniteComplete && infiniteComplete(); }); } diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 6163a7d83..487778e49 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -49,6 +49,7 @@ import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar'; import { CoreAttachmentsComponent } from './attachments/attachments'; import { CoreIonTabsComponent } from './ion-tabs/ion-tabs'; import { CoreIonTabComponent } from './ion-tabs/ion-tab'; +import { CoreInfiniteLoadingComponent } from './infinite-loading/infinite-loading'; @NgModule({ declarations: [ @@ -83,7 +84,8 @@ import { CoreIonTabComponent } from './ion-tabs/ion-tab'; CoreNavigationBarComponent, CoreAttachmentsComponent, CoreIonTabsComponent, - CoreIonTabComponent + CoreIonTabComponent, + CoreInfiniteLoadingComponent ], entryComponents: [ CoreContextMenuPopoverComponent, @@ -125,7 +127,8 @@ import { CoreIonTabComponent } from './ion-tabs/ion-tab'; CoreNavigationBarComponent, CoreAttachmentsComponent, CoreIonTabsComponent, - CoreIonTabComponent + CoreIonTabComponent, + CoreInfiniteLoadingComponent ] }) export class CoreComponentsModule {} diff --git a/src/components/infinite-loading/core-infinite-loading.html b/src/components/infinite-loading/core-infinite-loading.html new file mode 100644 index 000000000..e52c393a5 --- /dev/null +++ b/src/components/infinite-loading/core-infinite-loading.html @@ -0,0 +1,19 @@ +
+ +
+ + + + + +
+ +
+ +
+ +
\ No newline at end of file diff --git a/src/components/infinite-loading/infinite-loading.ts b/src/components/infinite-loading/infinite-loading.ts new file mode 100644 index 000000000..7c7b3b4d6 --- /dev/null +++ b/src/components/infinite-loading/infinite-loading.ts @@ -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: + * + */ +@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; + } + +} diff --git a/src/core/course/components/format/core-course-format.html b/src/core/course/components/format/core-course-format.html index 56eedc8d8..4b244729d 100644 --- a/src/core/course/components/format/core-course-format.html +++ b/src/core/course/components/format/core-course-format.html @@ -44,10 +44,14 @@
- - + + + + + +
diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index cd9a0bd00..37b239a6e 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -41,6 +41,8 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp templateUrl: 'core-course-format.html' }) 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() sections: any[]; // List of course sections. @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; singleSectionComponent: any; allSectionsComponent: any; + canLoadMore = false; + showSectionId = 0; // Data to pass to the components. data: any = {}; @@ -273,6 +277,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { } else { this.previousSection = null; this.nextSection = null; + this.canLoadMore = false; + this.showSectionId = 0; + this.showMoreActivities(); } if (this.moduleId && typeof previousValue == 'undefined') { @@ -363,6 +370,46 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { 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. */ diff --git a/src/core/course/formats/weeks/providers/handler.ts b/src/core/course/formats/weeks/providers/handler.ts index 675e343b1..ccaef9d44 100644 --- a/src/core/course/formats/weeks/providers/handler.ts +++ b/src/core/course/formats/weeks/providers/handler.ts @@ -47,8 +47,8 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler { const now = this.timeUtils.timestamp(); if (now < course.startdate || (course.enddate && now > course.enddate)) { - // Course hasn't started yet or it has ended already. Return the first section. - return sections[1]; + // Course hasn't started yet or it has ended already. Return all sections. + return sections[0]; } 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. - return sections[1]; + // The section wasn't found, return all sections. + return sections[0]; } /** diff --git a/src/core/course/providers/default-format.ts b/src/core/course/providers/default-format.ts index 45bcfe551..50ae2701c 100644 --- a/src/core/course/providers/default-format.ts +++ b/src/core/course/providers/default-format.ts @@ -16,7 +16,6 @@ import { Injectable } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreCourseFormatHandler } from './format-delegate'; -import { CoreCourseProvider } from './course'; /** * 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 { if (!this.coursesProvider.isGetCoursesByFieldAvailable()) { - // Cannot get the current section, return the first one. - if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) { - return sections[0]; - } - - return sections[1]; + // Cannot get the current section, return all of them. + return sections[0]; } // We need the "marker" to determine the current section. return this.coursesProvider.getCoursesByField('id', course.id).catch(() => { // Ignore errors. }).then((courses) => { - if (courses && courses[0]) { + if (courses && courses[0] && courses[0].marker > 0) { // Find the marked section. - const course = courses[0]; - for (let i = 0; i < sections.length; i++) { - const section = sections[i]; - if (section.section == course.marker) { - return section; - } - } - } + const course = courses[0], + section = sections.find((sect) => { + return sect.section == course.marker; + }); - // Marked section not found or we couldn't retrieve the marker. Return the first section. - for (let i = 0; i < sections.length; i++) { - const section = sections[i]; - if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) { + if (section) { return section; } } - return Promise.reject(null); + // Marked section not found or we couldn't retrieve the marker. Return all sections. + return sections[0]; }); } diff --git a/src/core/course/providers/format-delegate.ts b/src/core/course/providers/format-delegate.ts index 4a2b476c4..40a2668d4 100644 --- a/src/core/course/providers/format-delegate.ts +++ b/src/core/course/providers/format-delegate.ts @@ -17,7 +17,6 @@ import { NavController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCourseProvider } from './course'; import { CoreCourseFormatDefaultHandler } from './default-format'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; @@ -285,14 +284,11 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with current section. */ getCurrentSection(course: any, sections: any[]): Promise { + // Convert the result to a Promise if it isn't. return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCurrentSection', [course, sections])).catch(() => { - // This function should never fail. Just return the first section. - if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) { - return sections[0]; - } - - return sections[1]; + // This function should never fail. Just return all the sections. + return sections[0]; }); } diff --git a/src/core/courses/pages/search/search.html b/src/core/courses/pages/search/search.html index 875eb16fb..0174eb683 100644 --- a/src/core/courses/pages/search/search.html +++ b/src/core/courses/pages/search/search.html @@ -10,9 +10,7 @@ {{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }} - - - + diff --git a/src/core/courses/pages/search/search.ts b/src/core/courses/pages/search/search.ts index 43c988d3d..38607e129 100644 --- a/src/core/courses/pages/search/search.ts +++ b/src/core/courses/pages/search/search.ts @@ -54,11 +54,11 @@ export class CoreCoursesSearchPage { /** * 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(() => { - infiniteScroll.complete(); + infiniteComplete && infiniteComplete(); }); } diff --git a/src/core/courses/providers/courses.ts b/src/core/courses/providers/courses.ts index b1adb7cb6..bf5345fb1 100644 --- a/src/core/courses/providers/courses.ts +++ b/src/core/courses/providers/courses.ts @@ -390,6 +390,7 @@ export class CoreCoursesProvider { * @param {any} [value] The value to match. * @param {string} [siteId] Site ID. If not defined, use current site. * @return {Promise} Promise resolved with the courses. + * @since 3.2 */ getCoursesByField(field?: string, value?: any, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); @@ -473,6 +474,7 @@ export class CoreCoursesProvider { * Check if get courses by field WS is available. * * @return {boolean} Whether get courses by field is available. + * @since 3.2 */ isGetCoursesByFieldAvailable(): boolean { return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_courses_by_field'); diff --git a/src/core/user/components/participants/core-user-participants.html b/src/core/user/components/participants/core-user-participants.html index 266a79210..287c76baa 100644 --- a/src/core/user/components/participants/core-user-participants.html +++ b/src/core/user/components/participants/core-user-participants.html @@ -16,10 +16,7 @@

{{ 'core.lastaccess' | translate }}: {{ participant.lastaccess * 1000 | coreFormatDate:"dfmediumdate"}}

- - - - + \ No newline at end of file diff --git a/src/core/user/components/participants/participants.ts b/src/core/user/components/participants/participants.ts index ff4434a9e..f933be290 100644 --- a/src/core/user/components/participants/participants.ts +++ b/src/core/user/components/participants/participants.ts @@ -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} Resolved when done. + */ + loadMoreData(infiniteComplete?: any): Promise { + return this.fetchData().finally(() => { + infiniteComplete && infiniteComplete(); + }); + } + /** * Refresh data. *