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…
Reference in New Issue