forked from EVOgeek/Vmeda.Online
		
	
						commit
						f0c544df03
					
				| @ -27,7 +27,6 @@ import { CoreDomUtils } from '@services/utils/dom'; | |||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; | import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; | ||||||
| import { IonSearchbar } from '@ionic/angular'; | import { IonSearchbar } from '@ionic/angular'; | ||||||
| import moment from 'moment'; |  | ||||||
| import { CoreNavigator } from '@services/navigator'; | import { CoreNavigator } from '@services/navigator'; | ||||||
| 
 | 
 | ||||||
| const FILTER_PRIORITY: AddonBlockMyOverviewTimeFilters[] = | const FILTER_PRIORITY: AddonBlockMyOverviewTimeFilters[] = | ||||||
| @ -478,13 +477,19 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem | |||||||
|                     break; |                     break; | ||||||
|                 case 'inprogress': |                 case 'inprogress': | ||||||
|                     this.filteredCourses = this.filteredCourses.filter((course) => |                     this.filteredCourses = this.filteredCourses.filter((course) => | ||||||
|                         !course.hidden && !this.isPastCourse(course) && !this.isFutureCourse(course)); |                         !course.hidden && | ||||||
|  |                         !CoreCoursesHelper.isPastCourse(course, this.gradePeriodAfter) && | ||||||
|  |                         !CoreCoursesHelper.isFutureCourse(course, this.gradePeriodAfter, this.gradePeriodBefore)); | ||||||
|                     break; |                     break; | ||||||
|                 case 'future': |                 case 'future': | ||||||
|                     this.filteredCourses = this.filteredCourses.filter((course) => !course.hidden && this.isFutureCourse(course)); |                     this.filteredCourses = this.filteredCourses.filter((course) => | ||||||
|  |                         !course.hidden && | ||||||
|  |                         CoreCoursesHelper.isFutureCourse(course, this.gradePeriodAfter, this.gradePeriodBefore)); | ||||||
|                     break; |                     break; | ||||||
|                 case 'past': |                 case 'past': | ||||||
|                     this.filteredCourses = this.filteredCourses.filter((course) => !course.hidden && this.isPastCourse(course)); |                     this.filteredCourses = this.filteredCourses.filter((course) => | ||||||
|  |                         !course.hidden && | ||||||
|  |                         CoreCoursesHelper.isPastCourse(course, this.gradePeriodAfter)); | ||||||
|                     break; |                     break; | ||||||
|                 case 'favourite': |                 case 'favourite': | ||||||
|                     this.filteredCourses = this.filteredCourses.filter((course) => !course.hidden && course.isfavourite); |                     this.filteredCourses = this.filteredCourses.filter((course) => !course.hidden && course.isfavourite); | ||||||
| @ -515,44 +520,6 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem | |||||||
|         this.initPrefetchCoursesIcons(); |         this.initPrefetchCoursesIcons(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Calculates if course date is past. |  | ||||||
|      * |  | ||||||
|      * @param course Course Object. |  | ||||||
|      * @return Wether the course is past. |  | ||||||
|      */ |  | ||||||
|     protected isPastCourse(course: CoreEnrolledCourseDataWithOptions): boolean { |  | ||||||
|         if (course.completed) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!course.enddate) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Calculate the end date to use for display classification purposes, incorporating the grace period, if any.
 |  | ||||||
|         const endDate = moment(course.enddate * 1000).add(this.gradePeriodAfter, 'days').valueOf(); |  | ||||||
| 
 |  | ||||||
|         return endDate < this.today; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Calculates if course date is future. |  | ||||||
|      * |  | ||||||
|      * @param course Course Object. |  | ||||||
|      * @return Wether the course is future. |  | ||||||
|      */ |  | ||||||
|     protected isFutureCourse(course: CoreEnrolledCourseDataWithOptions): boolean { |  | ||||||
|         if (this.isPastCourse(course) || !course.startdate) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Calculate the start date to use for display classification purposes, incorporating the grace period, if any.
 |  | ||||||
|         const startDate = moment(course.startdate * 1000).subtract(this.gradePeriodBefore, 'days').valueOf(); |  | ||||||
| 
 |  | ||||||
|         return startDate > this.today; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Sort courses |      * Sort courses | ||||||
|      * |      * | ||||||
|  | |||||||
| @ -18,10 +18,10 @@ import { CoreDomUtils } from '@services/utils/dom'; | |||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { CoreTimeUtils } from '@services/utils/time'; | import { CoreTimeUtils } from '@services/utils/time'; | ||||||
| import { CoreCourse } from '@features/course/services/course'; | import { CoreCourse } from '@features/course/services/course'; | ||||||
| import moment from 'moment'; |  | ||||||
| import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; | import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; | ||||||
| import { AddonCalendarEvent } from '@addons/calendar/services/calendar'; | import { AddonCalendarEvent } from '@addons/calendar/services/calendar'; | ||||||
| import { CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper'; | import { CoreEnrolledCourseDataWithOptions } from '@features/courses/services/courses-helper'; | ||||||
|  | import { AddonBlockTimeline } from '../../services/timeline'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Directive to render a list of events in course overview. |  * Directive to render a list of events in course overview. | ||||||
| @ -37,6 +37,7 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { | |||||||
|     @Input() course?: CoreEnrolledCourseDataWithOptions; // Whether to show the course name.
 |     @Input() course?: CoreEnrolledCourseDataWithOptions; // Whether to show the course name.
 | ||||||
|     @Input() from = 0; // Number of days from today to offset the events.
 |     @Input() from = 0; // Number of days from today to offset the events.
 | ||||||
|     @Input() to?: number; // Number of days from today to limit the events to. If not defined, no limit.
 |     @Input() to?: number; // Number of days from today to limit the events to. If not defined, no limit.
 | ||||||
|  |     @Input() overdue = false; // If filtering overdue events or not.
 | ||||||
|     @Input() canLoadMore = false; // Whether more events can be loaded.
 |     @Input() canLoadMore = false; // Whether more events can be loaded.
 | ||||||
|     @Output() loadMore = new EventEmitter(); // Notify that more events should be loaded.
 |     @Output() loadMore = new EventEmitter(); // Notify that more events should be loaded.
 | ||||||
| 
 | 
 | ||||||
| @ -53,18 +54,13 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { | |||||||
| 
 | 
 | ||||||
|         if (changes.events || changes.from || changes.to) { |         if (changes.events || changes.from || changes.to) { | ||||||
|             if (this.events) { |             if (this.events) { | ||||||
|                 const filteredEvents = await this.filterEventsByTime(this.from, this.to); |                 const filteredEvents = await this.filterEventsByTime(); | ||||||
|                 this.empty = !filteredEvents || filteredEvents.length <= 0; |                 this.empty = !filteredEvents || filteredEvents.length <= 0; | ||||||
| 
 | 
 | ||||||
|                 const now = CoreTimeUtils.timestamp(); |                 const eventsByDay: Record<number, AddonBlockTimelineEvent[]> = {}; | ||||||
| 
 |  | ||||||
|                 const eventsByDay: Record<number, AddonCalendarEvent[]> = {}; |  | ||||||
|                 filteredEvents.forEach((event) => { |                 filteredEvents.forEach((event) => { | ||||||
|                     const dayTimestamp = CoreTimeUtils.getMidnightForTimestamp(event.timesort); |                     const dayTimestamp = CoreTimeUtils.getMidnightForTimestamp(event.timesort); | ||||||
| 
 | 
 | ||||||
|                     // Already calculated on 4.0 onwards but this will be live.
 |  | ||||||
|                     event.overdue = event.timesort < now; |  | ||||||
| 
 |  | ||||||
|                     if (eventsByDay[dayTimestamp]) { |                     if (eventsByDay[dayTimestamp]) { | ||||||
|                         eventsByDay[dayTimestamp].push(event); |                         eventsByDay[dayTimestamp].push(event); | ||||||
|                     } else { |                     } else { | ||||||
| @ -89,20 +85,34 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { | |||||||
|     /** |     /** | ||||||
|      * Filter the events by time. |      * Filter the events by time. | ||||||
|      * |      * | ||||||
|      * @param start Number of days to start getting events from today. E.g. -1 will get events from yesterday. |  | ||||||
|      * @param end Number of days after the start. |  | ||||||
|      * @return Filtered events. |      * @return Filtered events. | ||||||
|      */ |      */ | ||||||
|     protected async filterEventsByTime(start: number, end?: number): Promise<AddonBlockTimelineEvent[]> { |     protected async filterEventsByTime(): Promise<AddonBlockTimelineEvent[]> { | ||||||
|         start = moment().add(start, 'days').startOf('day').unix(); |         const start = AddonBlockTimeline.getDayStart(this.from); | ||||||
|         end = end !== undefined ? moment().add(end, 'days').startOf('day').unix() : end; |         const end = this.to !== undefined | ||||||
|  |             ? AddonBlockTimeline.getDayStart(this.to) | ||||||
|  |             : undefined; | ||||||
|  | 
 | ||||||
|  |         const now = CoreTimeUtils.timestamp(); | ||||||
|  |         const midnight = AddonBlockTimeline.getDayStart(); | ||||||
| 
 | 
 | ||||||
|         return await Promise.all(this.events.filter((event) => { |         return await Promise.all(this.events.filter((event) => { | ||||||
|             if (end) { |             if (start > event.timesort || (end && event.timesort >= end)) { | ||||||
|                 return start <= event.timesort && event.timesort < end; |                 return false; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return start <= event.timesort; |             // Already calculated on 4.0 onwards but this will be live.
 | ||||||
|  |             event.overdue = event.timesort < now; | ||||||
|  | 
 | ||||||
|  |             if (event.eventtype === 'open' || event.eventtype === 'opensubmission') { | ||||||
|  |                 const dayTimestamp = CoreTimeUtils.getMidnightForTimestamp(event.timesort); | ||||||
|  | 
 | ||||||
|  |                 return dayTimestamp > midnight; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue.
 | ||||||
|  |             // This means if filtering by overdue, some events fetched might not be required (eg if due later today).
 | ||||||
|  |             return (!this.overdue || event.overdue); | ||||||
|         }).map(async (event) => { |         }).map(async (event) => { | ||||||
|             event.iconUrl = await CoreCourse.getModuleIconSrc(event.icon.component); |             event.iconUrl = await CoreCourse.getModuleIconSrc(event.icon.component); | ||||||
|             event.modulename = event.modulename || event.icon.component; |             event.modulename = event.modulename || event.icon.component; | ||||||
| @ -147,7 +157,8 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type AddonBlockTimelineEvent = AddonCalendarEvent & { | type AddonBlockTimelineEvent = Omit<AddonCalendarEvent, 'eventtype'> & { | ||||||
|  |     eventtype: string; | ||||||
|     iconUrl?: string; |     iconUrl?: string; | ||||||
|     iconTitle?: string; |     iconTitle?: string; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -60,12 +60,12 @@ | |||||||
| 
 | 
 | ||||||
|     <core-loading [hideUntil]="timeline.loaded" [hidden]="sort != 'sortbydates'"> |     <core-loading [hideUntil]="timeline.loaded" [hidden]="sort != 'sortbydates'"> | ||||||
|         <addon-block-timeline-events [events]="timeline.events" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMore()" |         <addon-block-timeline-events [events]="timeline.events" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMore()" | ||||||
|             [from]="dataFrom" [to]="dataTo"></addon-block-timeline-events> |             [from]="dataFrom" [to]="dataTo" [overdue]="overdue"></addon-block-timeline-events> | ||||||
|     </core-loading> |     </core-loading> | ||||||
|     <core-loading [hideUntil]="timelineCourses.loaded" [hidden]="sort != 'sortbycourses'"> |     <core-loading [hideUntil]="timelineCourses.loaded" [hidden]="sort != 'sortbycourses'"> | ||||||
|         <ng-container *ngFor="let course of timelineCourses.courses"> |         <ng-container *ngFor="let course of timelineCourses.courses"> | ||||||
|             <addon-block-timeline-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMore(course)" |             <addon-block-timeline-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMore(course)" | ||||||
|                 [course]="course" [from]="dataFrom" [to]="dataTo"></addon-block-timeline-events> |                 [course]="course" [from]="dataFrom" [to]="dataTo" [overdue]="overdue"></addon-block-timeline-events> | ||||||
|         </ng-container> |         </ng-container> | ||||||
|         <core-empty-box *ngIf="timelineCourses.courses.length == 0" image="assets/img/icons/courses.svg" |         <core-empty-box *ngIf="timelineCourses.courses.length == 0" image="assets/img/icons/courses.svg" | ||||||
|             [message]="'addon.block_timeline.noevents' | translate"></core-empty-box> |             [message]="'addon.block_timeline.noevents' | translate"></core-empty-box> | ||||||
|  | |||||||
| @ -58,12 +58,15 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen | |||||||
| 
 | 
 | ||||||
|     dataFrom?: number; |     dataFrom?: number; | ||||||
|     dataTo?: number; |     dataTo?: number; | ||||||
|  |     overdue = false; | ||||||
| 
 | 
 | ||||||
|     searchEnabled = false; |     searchEnabled = false; | ||||||
|     searchText = ''; |     searchText = ''; | ||||||
| 
 | 
 | ||||||
|     protected courseIds: number[] = []; |     protected courseIdsToInvalidate: number[] = []; | ||||||
|     protected fetchContentDefaultError = 'Error getting timeline data.'; |     protected fetchContentDefaultError = 'Error getting timeline data.'; | ||||||
|  |     protected gradePeriodAfter = 0; | ||||||
|  |     protected gradePeriodBefore = 0; | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         super('AddonBlockTimelineComponent'); |         super('AddonBlockTimelineComponent'); | ||||||
| @ -105,8 +108,8 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen | |||||||
|         promises.push(AddonBlockTimeline.invalidateActionEventsByCourses()); |         promises.push(AddonBlockTimeline.invalidateActionEventsByCourses()); | ||||||
|         promises.push(CoreCourses.invalidateUserCourses()); |         promises.push(CoreCourses.invalidateUserCourses()); | ||||||
|         promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions()); |         promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions()); | ||||||
|         if (this.courseIds.length > 0) { |         if (this.courseIdsToInvalidate.length > 0) { | ||||||
|             promises.push(CoreCourses.invalidateCoursesByField('ids', this.courseIds.join(','))); |             promises.push(CoreCourses.invalidateCoursesByField('ids', this.courseIdsToInvalidate.join(','))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return CoreUtils.allPromises(promises); |         return CoreUtils.allPromises(promises); | ||||||
| @ -171,13 +174,26 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen | |||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected async fetchMyOverviewTimelineByCourses(): Promise<void> { |     protected async fetchMyOverviewTimelineByCourses(): Promise<void> { | ||||||
|  |         try { | ||||||
|  |             this.gradePeriodAfter = parseInt(await this.currentSite.getConfig('coursegraceperiodafter'), 10); | ||||||
|  |             this.gradePeriodBefore = parseInt(await this.currentSite.getConfig('coursegraceperiodbefore'), 10); | ||||||
|  |         } catch { | ||||||
|  |             this.gradePeriodAfter = 0; | ||||||
|  |             this.gradePeriodBefore = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Do not filter courses by date because they can contain activities due.
 |         // Do not filter courses by date because they can contain activities due.
 | ||||||
|         this.timelineCourses.courses = await CoreCoursesHelper.getUserCoursesWithOptions(); |         this.timelineCourses.courses = await CoreCoursesHelper.getUserCoursesWithOptions(); | ||||||
|  |         this.courseIdsToInvalidate = this.timelineCourses.courses.map((course) => course.id); | ||||||
|  | 
 | ||||||
|  |         // Filter only in progress courses.
 | ||||||
|  |         this.timelineCourses.courses = this.timelineCourses.courses.filter((course) => | ||||||
|  |             !course.hidden && | ||||||
|  |             !CoreCoursesHelper.isPastCourse(course, this.gradePeriodAfter) && | ||||||
|  |             !CoreCoursesHelper.isFutureCourse(course, this.gradePeriodAfter, this.gradePeriodBefore)); | ||||||
| 
 | 
 | ||||||
|         if (this.timelineCourses.courses.length > 0) { |         if (this.timelineCourses.courses.length > 0) { | ||||||
|             this.courseIds = this.timelineCourses.courses.map((course) => course.id); |             const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(this.courseIdsToInvalidate, this.searchText); | ||||||
| 
 |  | ||||||
|             const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(this.courseIds, this.searchText); |  | ||||||
| 
 | 
 | ||||||
|             this.timelineCourses.courses = this.timelineCourses.courses.filter((course) => { |             this.timelineCourses.courses = this.timelineCourses.courses.filter((course) => { | ||||||
|                 if (courseEvents[course.id].events.length == 0) { |                 if (courseEvents[course.id].events.length == 0) { | ||||||
| @ -200,6 +216,7 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen | |||||||
|     switchFilter(filter: string): void { |     switchFilter(filter: string): void { | ||||||
|         this.filter = filter; |         this.filter = filter; | ||||||
|         this.currentSite.setLocalSiteConfig('AddonBlockTimelineFilter', this.filter); |         this.currentSite.setLocalSiteConfig('AddonBlockTimelineFilter', this.filter); | ||||||
|  |         this.overdue = this.filter === 'overdue'; | ||||||
| 
 | 
 | ||||||
|         switch (this.filter) { |         switch (this.filter) { | ||||||
|             case 'overdue': |             case 'overdue': | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ export class AddonBlockTimelineProvider { | |||||||
|     ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { |     ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { | ||||||
|         const site = await CoreSites.getSite(siteId); |         const site = await CoreSites.getSite(siteId); | ||||||
| 
 | 
 | ||||||
|         const time = moment().subtract(14, 'days').unix(); // Check two weeks ago.
 |         const time = this.getDayStart(-14); // Check two weeks ago.
 | ||||||
| 
 | 
 | ||||||
|         const data: AddonCalendarGetActionEventsByCourseWSParams = { |         const data: AddonCalendarGetActionEventsByCourseWSParams = { | ||||||
|             timesortfrom: time, |             timesortfrom: time, | ||||||
| @ -109,7 +109,7 @@ export class AddonBlockTimelineProvider { | |||||||
|     ): Promise<{[courseId: string]: { events: AddonCalendarEvent[]; canLoadMore?: number } }> { |     ): Promise<{[courseId: string]: { events: AddonCalendarEvent[]; canLoadMore?: number } }> { | ||||||
|         const site = await CoreSites.getSite(siteId); |         const site = await CoreSites.getSite(siteId); | ||||||
| 
 | 
 | ||||||
|         const time = moment().subtract(14, 'days').unix(); // Check two weeks ago.
 |         const time = this.getDayStart(-14); // Check two weeks ago.
 | ||||||
| 
 | 
 | ||||||
|         const data: AddonCalendarGetActionEventsByCoursesWSParams = { |         const data: AddonCalendarGetActionEventsByCoursesWSParams = { | ||||||
|             timesortfrom: time, |             timesortfrom: time, | ||||||
| @ -164,7 +164,7 @@ export class AddonBlockTimelineProvider { | |||||||
|     ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { |     ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { | ||||||
|         const site = await CoreSites.getSite(siteId); |         const site = await CoreSites.getSite(siteId); | ||||||
| 
 | 
 | ||||||
|         const timesortfrom = moment().subtract(14, 'days').unix(); // Check two weeks ago.
 |         const timesortfrom = this.getDayStart(-14); // Check two weeks ago.
 | ||||||
|         const limitnum = AddonBlockTimelineProvider.EVENTS_LIMIT; |         const limitnum = AddonBlockTimelineProvider.EVENTS_LIMIT; | ||||||
| 
 | 
 | ||||||
|         const data: AddonCalendarGetActionEventsByTimesortWSParams = { |         const data: AddonCalendarGetActionEventsByTimesortWSParams = { | ||||||
| @ -275,6 +275,16 @@ export class AddonBlockTimelineProvider { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the timestamp at the start of the day with an optional offset. | ||||||
|  |      * | ||||||
|  |      * @param daysOffset Offset days to add or substract. | ||||||
|  |      * @return timestamp. | ||||||
|  |      */ | ||||||
|  |     getDayStart(daysOffset = 0): number { | ||||||
|  |         return moment().startOf('day').add(daysOffset, 'days').unix(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const AddonBlockTimeline = makeSingleton(AddonBlockTimelineProvider); | export const AddonBlockTimeline = makeSingleton(AddonBlockTimelineProvider); | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ import { CoreText } from '@singletons/text'; | |||||||
| const ROOT_CACHE_KEY = 'mmaCalendar:'; | const ROOT_CACHE_KEY = 'mmaCalendar:'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Context levels enumeration. |  * Main calendar Event types enumeration. | ||||||
|  */ |  */ | ||||||
| export enum AddonCalendarEventType { | export enum AddonCalendarEventType { | ||||||
|     SITE = 'site', |     SITE = 'site', | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ import { | |||||||
| import { makeSingleton, Translate } from '@singletons'; | import { makeSingleton, Translate } from '@singletons'; | ||||||
| import { CoreWSExternalFile } from '@services/ws'; | import { CoreWSExternalFile } from '@services/ws'; | ||||||
| import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; | import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion'; | ||||||
|  | import moment from 'moment'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Helper to gather some common courses functions. |  * Helper to gather some common courses functions. | ||||||
| @ -293,6 +294,51 @@ export class CoreCoursesHelperProvider { | |||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Calculates if course date is past. | ||||||
|  |      * | ||||||
|  |      * @param course Course Object. | ||||||
|  |      * @param gradePeriodAfter Classify past courses as in progress for these many days after the course end date. | ||||||
|  |      * @return Wether the course is past. | ||||||
|  |      */ | ||||||
|  |     isPastCourse(course: CoreEnrolledCourseDataWithOptions, gradePeriodAfter = 0): boolean { | ||||||
|  |         if (course.completed) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!course.enddate) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Calculate the end date to use for display classification purposes, incorporating the grace period, if any.
 | ||||||
|  |         const endDate = moment(course.enddate * 1000).add(gradePeriodAfter, 'days').valueOf(); | ||||||
|  | 
 | ||||||
|  |         return endDate < Date.now(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Calculates if course date is future. | ||||||
|  |      * | ||||||
|  |      * @param course Course Object. | ||||||
|  |      * @param gradePeriodAfter Classify past courses as in progress for these many days after the course end date. | ||||||
|  |      * @param gradePeriodBefore Classify future courses as in progress for these many days prior to the course start date. | ||||||
|  |      * @return Wether the course is future. | ||||||
|  |      */ | ||||||
|  |     isFutureCourse( | ||||||
|  |         course: CoreEnrolledCourseDataWithOptions, | ||||||
|  |         gradePeriodAfter = 0, | ||||||
|  |         gradePeriodBefore = 0, | ||||||
|  |     ): boolean { | ||||||
|  |         if (this.isPastCourse(course, gradePeriodAfter) || !course.startdate) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Calculate the start date to use for display classification purposes, incorporating the grace period, if any.
 | ||||||
|  |         const startDate = moment(course.startdate * 1000).subtract(gradePeriodBefore, 'days').valueOf(); | ||||||
|  | 
 | ||||||
|  |         return startDate > Date.now(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const CoreCoursesHelper = makeSingleton(CoreCoursesHelperProvider); | export const CoreCoursesHelper = makeSingleton(CoreCoursesHelperProvider); | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ export class CoreTime { | |||||||
|     /** |     /** | ||||||
|      * Converts a number of seconds into a short human readable format: minutes and seconds, in fromat: 3' 27''. |      * Converts a number of seconds into a short human readable format: minutes and seconds, in fromat: 3' 27''. | ||||||
|      * |      * | ||||||
|      * @param seconds Seconds |      * @param duration Duration in seconds. | ||||||
|      * @return Short human readable text. |      * @return Short human readable text. | ||||||
|      */ |      */ | ||||||
|     static formatTimeShort(duration: number): string { |     static formatTimeShort(duration: number): string { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user