MOBILE-3087 calendar: Display offline events in their right position
parent
089c56b56b
commit
649ad7a1a2
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue