MOBILE-3087 calendar: Display offline events in their right position

main
Dani Palou 2019-06-27 10:29:24 +02:00
parent 089c56b56b
commit 649ad7a1a2
5 changed files with 100 additions and 38 deletions

View File

@ -9,7 +9,7 @@
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<form ion-list [formGroup]="eventForm"> <form ion-list [formGroup]="eventForm" *ngIf="!error">
<!-- Event name. --> <!-- Event name. -->
<ion-item text-wrap> <ion-item text-wrap>
<ion-label stacked><h2 [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</h2></ion-label> <ion-label stacked><h2 [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</h2></ion-label>

View File

@ -71,6 +71,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
protected types: any; // Object with the supported types. protected types: any; // Object with the supported types.
protected showAll: boolean; protected showAll: boolean;
protected isDestroyed = false; protected isDestroyed = false;
protected error = false;
constructor(navParams: NavParams, constructor(navParams: NavParams,
private navCtrl: NavController, private navCtrl: NavController,
@ -146,6 +147,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
protected fetchData(refresh?: boolean): Promise<any> { protected fetchData(refresh?: boolean): Promise<any> {
let accessInfo; let accessInfo;
this.error = false;
// Get access info. // Get access info.
return this.calendarProvider.getAccessInformation(this.courseId).then((info) => { return this.calendarProvider.getAccessInformation(this.courseId).then((info) => {
accessInfo = info; accessInfo = info;
@ -254,8 +257,12 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
}).catch((error) => { }).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error getting data.'); this.domUtils.showErrorModalDefault(error, 'Error getting data.');
this.error = true;
if (!this.svComponent || !this.svComponent.isOn()) {
this.originalData = null; // Avoid asking for confirmation. this.originalData = null; // Avoid asking for confirmation.
this.navCtrl.pop(); this.navCtrl.pop();
}
}); });
} }

View File

@ -26,20 +26,6 @@
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate"> <core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate">
</core-empty-box> </core-empty-box>
<ion-list *ngIf="offlineEvents && offlineEvents.length" no-margin>
<ng-container *ngFor="let event of offlineEvents">
<ion-item-divider> {{ 'core.notsent' | translate }}</ion-item-divider>
<a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId">
<core-icon *ngIf="event.icon" [name]="event.icon" item-start></core-icon>
<h2><core-format-text [text]="event.name"></core-format-text></h2>
<p>
{{ event.timestart * 1000 | coreFormatDate: "strftimedatetimeshort" }}
<span *ngIf="event.timeduration"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}</span>
</p>
</a>
</ng-container>
</ion-list>
<ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin> <ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin>
<ng-container *ngFor="let event of filteredEvents"> <ng-container *ngFor="let event of filteredEvents">
<ion-item-divider *ngIf="event.showDate"> <ion-item-divider *ngIf="event.showDate">
@ -54,6 +40,10 @@
<span *ngIf="event.timeduration && event.endsSameDay"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }}</span> <span *ngIf="event.timeduration && event.endsSameDay"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }}</span>
<span *ngIf="event.timeduration && !event.endsSameDay"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}</span> <span *ngIf="event.timeduration && !event.endsSameDay"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}</span>
</p> </p>
<ion-note *ngIf="event.offline" item-end>
<ion-icon name="time"></ion-icon>
{{ 'core.notsent' | translate }}
</ion-note>
</a> </a>
</ng-container> </ng-container>
</ion-list> </ion-list>

View File

@ -31,6 +31,7 @@ import { CoreAppProvider } from '@providers/app';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import * as moment from 'moment'; import * as moment from 'moment';
import { Network } from '@ionic-native/network'; import { Network } from '@ionic-native/network';
import { CoreConstants } from '@core/constants';
/** /**
* Page that displays the list of calendar events. * Page that displays the list of calendar events.
@ -69,7 +70,8 @@ export class AddonCalendarListPage implements OnDestroy {
courses: any[]; courses: any[];
eventsLoaded = false; eventsLoaded = false;
events = []; events = []; // Events (both online and offline).
onlineEvents = [];
offlineEvents = []; offlineEvents = [];
notificationsEnabled = false; notificationsEnabled = false;
filteredEvents = []; filteredEvents = [];
@ -98,7 +100,7 @@ export class AddonCalendarListPage implements OnDestroy {
if (this.notificationsEnabled) { if (this.notificationsEnabled) {
// Re-schedule events if default time changes. // Re-schedule events if default time changes.
this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => {
calendarProvider.scheduleEventsNotifications(this.events); calendarProvider.scheduleEventsNotifications(this.onlineEvents);
}, this.currentSiteId); }, this.currentSiteId);
} }
@ -179,8 +181,12 @@ export class AddonCalendarListPage implements OnDestroy {
this.fetchData(false, true, false).then(() => { this.fetchData(false, true, false).then(() => {
if (!this.eventId && this.splitviewCtrl.isOn() && this.events.length > 0) { if (!this.eventId && this.splitviewCtrl.isOn() && this.events.length > 0) {
// Take first and load it. // Take first online event and load it. If no online event, load the first offline.
this.gotoEvent(this.events[0].id); if (this.onlineEvents[0]) {
this.gotoEvent(this.onlineEvents[0].id);
} else {
this.gotoEvent(this.offlineEvents[0].id);
}
} }
}); });
} }
@ -252,8 +258,11 @@ export class AddonCalendarListPage implements OnDestroy {
this.hasOffline = !!events.length; this.hasOffline = !!events.length;
// Format data and sort by timestart. // Format data and sort by timestart.
events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); events.forEach((event) => {
this.offlineEvents = events.sort((a, b) => a.timestart - b.timestart); event.offline = true;
this.calendarHelper.formatEventData(event);
});
this.offlineEvents = this.sortEvents(events);
})); }));
return Promise.all(promises); return Promise.all(promises);
@ -273,39 +282,37 @@ export class AddonCalendarListPage implements OnDestroy {
this.loadMoreError = false; this.loadMoreError = false;
return this.calendarProvider.getEventsList(this.initialTime, this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL) return this.calendarProvider.getEventsList(this.initialTime, this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL)
.then((events) => { .then((onlineEvents) => {
this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL; if (onlineEvents.length === 0) {
if (events.length === 0) {
this.emptyEventsTimes++; this.emptyEventsTimes++;
if (this.emptyEventsTimes > 5) { // Stop execution if we retrieve empty list 6 consecutive times. if (this.emptyEventsTimes > 5) { // Stop execution if we retrieve empty list 6 consecutive times.
this.canLoadMore = false; this.canLoadMore = false;
if (refresh) { if (refresh) {
this.events = []; this.onlineEvents = [];
this.filteredEvents = []; this.filteredEvents = [];
this.events = this.offlineEvents;
} }
} else { } else {
// No events returned, load next events. // No events returned, load next events.
this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
return this.fetchEvents(); return this.fetchEvents();
} }
} else { } else {
events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper));
// Sort the events by timestart, they're ordered by id. // Get the merged events of this period.
events.sort((a, b) => { const events = this.mergeEvents(onlineEvents);
if (a.timestart == b.timestart) {
return a.timeduration - b.timeduration;
}
return a.timestart - b.timestart; this.getCategories = this.shouldLoadCategories(onlineEvents);
});
this.getCategories = this.shouldLoadCategories(events);
if (refresh) { if (refresh) {
this.onlineEvents = onlineEvents;
this.events = events; this.events = events;
} else { } else {
// Filter events with same ID. Repeated events are returned once per WS call, show them only once. // Filter events with same ID. Repeated events are returned once per WS call, show them only once.
this.onlineEvents = this.utils.mergeArraysWithoutDuplicates(this.onlineEvents, onlineEvents, 'id');
this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id'); this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id');
} }
this.filteredEvents = this.getFilteredEvents(); this.filteredEvents = this.getFilteredEvents();
@ -318,7 +325,9 @@ export class AddonCalendarListPage implements OnDestroy {
this.canLoadMore = true; this.canLoadMore = true;
// Schedule notifications for the events retrieved (might have new events). // Schedule notifications for the events retrieved (might have new events).
this.calendarProvider.scheduleEventsNotifications(this.events); this.calendarProvider.scheduleEventsNotifications(this.onlineEvents);
this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
} }
// Resize the content so infinite loading is able to calculate if it should load more items or not. // Resize the content so infinite loading is able to calculate if it should load more items or not.
@ -441,6 +450,61 @@ export class AddonCalendarListPage implements OnDestroy {
}); });
} }
/**
* Merge a period of online events with the offline events of that period.
*
* @param {any[]} onlineEvents Online events.
* @return {any[]} Merged events.
*/
protected mergeEvents(onlineEvents: any[]): any[] {
if (!this.offlineEvents || !this.offlineEvents.length) {
// No offline events, nothing to merge.
return onlineEvents;
}
const start = this.initialTime + (CoreConstants.SECONDS_DAY * this.daysLoaded),
end = start + (CoreConstants.SECONDS_DAY * AddonCalendarProvider.DAYS_INTERVAL) - 1;
// First of all, remove the online events that were modified in offline.
let result = onlineEvents.filter((event) => {
const offlineEvent = this.offlineEvents.find((ev) => {
return ev.id == event.id;
});
return !offlineEvent;
});
// Now get the offline events that belong to this period.
const periodOfflineEvents = this.offlineEvents.filter((event) => {
if (this.daysLoaded == 0 && event.timestart < start) {
// Display offline events that are previous to current time to allow editing them.
return true;
}
return (event.timestart >= start || event.timestart + event.timeduration >= start) && event.timestart <= end;
});
// Merge both arrays and sort them.
result = result.concat(periodOfflineEvents);
return this.sortEvents(result);
}
/**
* Sort events by timestart.
*
* @param {any[]} events List to sort.
*/
protected sortEvents(events: any[]): any[] {
return events.sort((a, b) => {
if (a.timestart == b.timestart) {
return a.timeduration - b.timeduration;
}
return a.timestart - b.timestart;
});
}
/** /**
* Refresh the data. * Refresh the data.
* *

View File

@ -591,6 +591,7 @@ export class AddonCalendarProvider {
const preSets = { const preSets = {
cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval), cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval),
getCacheUsingCacheKey: true, getCacheUsingCacheKey: true,
uniqueCacheKey: true,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };