MOBILE-3021 calendar: Display offline events too

main
Dani Palou 2019-07-03 15:59:27 +02:00
parent 661709acb4
commit b202d92dc3
7 changed files with 494 additions and 69 deletions

View File

@ -19,7 +19,7 @@
</ion-grid>
<!-- Calendar view. -->
<ion-grid no-padding>
<ion-grid padding-horizontal>
<!-- List of days. -->
<ion-row>
<ion-col text-center *ngFor="let day of weekDays" class="addon-calendar-weekdays">
@ -38,11 +38,15 @@
<!-- In tablet, display list of events. -->
<div class="hidden-phone" class="addon-calendar-day-events">
<p *ngFor="let event of day.filteredEvents | slice:0:3">
<span class="calendar_event_type calendar_event_{{event.eventtype}}"></span>
{{event.name}}
</p>
<p *ngIf="day.filteredEvents.length > 3">{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</p>
<ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index">
<p *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event" (click)="eventClicked(event)">
<span class="calendar_event_type calendar_event_{{event.eventtype}}"></span>
<ion-icon *ngIf="event.offline && !event.deleted" name="time"></ion-icon>
<ion-icon *ngIf="event.deleted" name="trash"></ion-icon>
{{event.name}}
</p>
</ng-container>
<p *ngIf="day.filteredEvents.length > 4"><b>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</b></p>
</div>
</ion-col>
<ion-col *ngFor="let value of week.postpadding" class="dayblank"></ion-col> <!-- Empty slots (last week). -->

View File

@ -13,6 +13,15 @@ ion-app.app-root addon-calendar-calendar {
.addon-calendar-day-events {
@include text-align('start');
ion-icon {
@include margin-horizontal(null, 2px);
font-size: 1em;
}
}
.addon-calendar-event {
cursor: pointer;
}
.calendar_event_type {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnDestroy, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';
import { Component, OnDestroy, OnInit, Input, OnChanges, SimpleChange, Output, EventEmitter } from '@angular/core';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
@ -20,6 +20,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { AddonCalendarProvider } from '../../providers/calendar';
import { AddonCalendarHelperProvider } from '../../providers/helper';
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
import { CoreCoursesProvider } from '@core/courses/providers/courses';
/**
@ -35,6 +36,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
@Input() courseId: number | string;
@Input() categoryId: number | string; // Category ID the course belongs to.
@Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true.
@Output() onEventClicked = new EventEmitter<number>();
periodName: string;
weekDays: any[];
@ -45,16 +47,39 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
protected month: number;
protected categoriesRetrieved = false;
protected categories = {};
protected currentSiteId: string;
protected offlineEvents: {[monthId: string]: {[day: number]: any[]}} = {}; // Offline events classified in month & day.
protected offlineEditedEventsIds = []; // IDs of events edited in offline.
protected deletedEvents = []; // Events deleted in offline.
// Observers.
protected undeleteEventObserver: any;
constructor(eventsProvider: CoreEventsProvider,
sitesProvider: CoreSitesProvider,
private calendarProvider: AddonCalendarProvider,
private calendarHelper: AddonCalendarHelperProvider,
private calendarOffline: AddonCalendarOfflineProvider,
private domUtils: CoreDomUtilsProvider,
private timeUtils: CoreTimeUtilsProvider,
private utils: CoreUtilsProvider,
private coursesProvider: CoreCoursesProvider) {
this.currentSiteId = sitesProvider.getCurrentSiteId();
// Listen for events "undeleted" (offline).
this.undeleteEventObserver = eventsProvider.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, (data) => {
if (data && data.eventId) {
// Mark it as undeleted, no need to refresh.
this.undeleteEvent(data.eventId);
// Remove it from the list of deleted events if it's there.
const index = this.deletedEvents.indexOf(data.eventId);
if (index != -1) {
this.deletedEvents.splice(index, 1);
}
}
}, this.currentSiteId);
}
/**
@ -76,27 +101,63 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
if ((changes.courseId || changes.categoryId) && this.weeks) {
const courseId = this.courseId ? Number(this.courseId) : undefined,
categoryId = this.categoryId ? Number(this.categoryId) : undefined;
this.filterEvents(courseId, categoryId);
this.filterEvents();
}
}
/**
* Fetch contacts.
*
* @param {boolean} [refresh=false] True if we are refreshing contacts, false if we are loading more.
* @param {boolean} [refresh=false] True if we are refreshing events.
* @return {Promise<any>} Promise resolved when done.
*/
fetchData(refresh: boolean = false): Promise<any> {
const courseId = this.courseId ? Number(this.courseId) : undefined,
categoryId = this.categoryId ? Number(this.categoryId) : undefined,
promises = [];
const promises = [];
promises.push(this.loadCategories());
promises.push(this.calendarProvider.getMonthlyEvents(this.year, this.month, courseId, categoryId).then((result) => {
// Get offline events.
promises.push(this.calendarOffline.getAllEditedEvents().then((events) => {
// Format data.
events.forEach((event) => {
event.offline = true;
this.calendarHelper.formatEventData(event);
});
// Classify them by month.
this.offlineEvents = this.calendarHelper.classifyIntoMonths(events);
// Get the IDs of events edited in offline.
const filtered = events.filter((event) => {
return event.id > 0;
});
this.offlineEditedEventsIds = filtered.map((event) => {
return event.id;
});
}));
// Get events deleted in offline.
promises.push(this.calendarOffline.getAllDeletedEventsIds().then((ids) => {
this.deletedEvents = ids;
}));
return Promise.all(promises).then(() => {
return this.fetchEvents();
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
}).finally(() => {
this.loaded = true;
});
}
/**
* Fetch the events for current month.
*
* @return {Promise<any>} Promise resolved when done.
*/
fetchEvents(): Promise<any> {
// Don't pass courseId and categoryId, we'll filter them locally.
return this.calendarProvider.getMonthlyEvents(this.year, this.month).then((result) => {
// Calculate the period name. We don't use the one in result because it's in server's language.
this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear');
@ -104,13 +165,11 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
this.weekDays = this.calendarProvider.getWeekDays(result.daynames[0].dayno);
this.weeks = result.weeks;
this.filterEvents(courseId, categoryId);
}));
// Merge the online events with offline data.
this.mergeEvents();
return Promise.all(promises).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
}).finally(() => {
this.loaded = true;
// Filter events by course.
this.filterEvents();
});
}
@ -140,11 +199,10 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
/**
* Filter events to only display events belonging to a certain course.
*
* @param {number} courseId Course ID.
* @param {number} categoryId Category the course belongs to.
*/
filterEvents(courseId: number, categoryId: number): void {
filterEvents(): void {
const courseId = this.courseId ? Number(this.courseId) : undefined,
categoryId = this.categoryId ? Number(this.categoryId) : undefined;
this.weeks.forEach((week) => {
week.days.forEach((day) => {
@ -165,9 +223,11 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
/**
* Refresh events.
*
* @param {boolean} [sync] Whether it should try to synchronize offline events.
* @param {boolean} [showErrors] Whether to show sync errors to the user.
* @return {Promise<any>} Promise resolved when done.
*/
refreshData(): Promise<any> {
refreshData(sync?: boolean, showErrors?: boolean): Promise<any> {
const promises = [];
promises.push(this.calendarProvider.invalidateMonthlyEvents(this.year, this.month));
@ -184,38 +244,145 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
* Load next month.
*/
loadNext(): void {
if (this.month === 12) {
this.month = 1;
this.year++;
} else {
this.month++;
}
this.increaseMonth();
this.loaded = false;
this.fetchData();
this.fetchEvents().catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
this.decreaseMonth();
}).finally(() => {
this.loaded = true;
});
}
/**
* Load previous month.
*/
loadPrevious(): void {
this.decreaseMonth();
this.loaded = false;
this.fetchEvents().catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
this.increaseMonth();
}).finally(() => {
this.loaded = true;
});
}
/**
* An event was clicked.
*
* @param {any} event Event.
*/
eventClicked(event: any): void {
this.onEventClicked.emit(event.id);
}
/**
* Decrease the current month.
*/
protected decreaseMonth(): void {
if (this.month === 1) {
this.month = 12;
this.year--;
} else {
this.month--;
}
}
this.loaded = false;
/**
* Increase the current month.
*/
protected increaseMonth(): void {
if (this.month === 12) {
this.month = 1;
this.year++;
} else {
this.month++;
}
}
this.fetchData();
/**
* Merge online events with the offline events of that period.
*/
protected mergeEvents(): void {
const monthOfflineEvents = this.offlineEvents[this.calendarHelper.getMonthId(this.year, this.month)];
if (!monthOfflineEvents && !this.deletedEvents.length) {
// No offline events, nothing to merge.
return;
}
this.weeks.forEach((week) => {
week.days.forEach((day) => {
if (this.deletedEvents.length) {
// Mark as deleted the events that were deleted in offline.
day.events.forEach((event) => {
event.deleted = this.deletedEvents.indexOf(event.id) != -1;
});
}
if (this.offlineEditedEventsIds.length) {
// Remove the online events that were modified in offline.
day.events = day.events.filter((event) => {
return this.offlineEditedEventsIds.indexOf(event.id) == -1;
});
}
if (monthOfflineEvents && monthOfflineEvents[day.mday]) {
// Add the offline events (either new or edited).
day.events = this.sortEvents(day.events.concat(monthOfflineEvents[day.mday]));
}
});
});
}
/**
* 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;
});
}
/**
* Undelete a certain event.
*
* @param {number} eventId Event ID.
*/
protected undeleteEvent(eventId: number): void {
if (!this.weeks) {
return;
}
this.weeks.forEach((week) => {
week.days.forEach((day) => {
const event = day.events.find((event) => {
return event.id == eventId;
});
if (event) {
event.deleted = false;
}
});
});
}
/**
* Component destroyed.
*/
ngOnDestroy(): void {
// @todo
this.undeleteEventObserver && this.undeleteEventObserver.off();
}
}

View File

@ -7,6 +7,7 @@
</button>
<core-context-menu>
<core-context-menu-item [hidden]="!notificationsEnabled" [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()" [iconAction]="'cog'"></core-context-menu-item>
<core-context-menu-item [hidden]="!loaded || !hasOffline || !isOnline" [priority]="400" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
</core-context-menu>
</ion-buttons>
</ion-navbar>
@ -16,7 +17,12 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<addon-calendar-calendar [courseId]="courseId" [categoryId]="categoryId"></addon-calendar-calendar>
<!-- There is data to be synchronized -->
<ion-card class="core-warning-card" icon-start *ngIf="hasOffline">
<ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }}
</ion-card>
<addon-calendar-calendar [courseId]="courseId" [categoryId]="categoryId" (onEventClicked)="gotoEvent($event)"></addon-calendar-calendar>
<!-- Create a calendar event. -->
<ion-fab core-fab bottom end *ngIf="canCreate">

View File

@ -12,16 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild, NgZone } from '@angular/core';
import { IonicPage, NavParams, NavController, PopoverController } from 'ionic-angular';
import { CoreAppProvider } from '@providers/app';
import { CoreEventsProvider } from '@providers/events';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { AddonCalendarProvider } from '../../providers/calendar';
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
import { AddonCalendarHelperProvider } from '../../providers/helper';
import { AddonCalendarCalendarComponent } from '../../components/calendar/calendar';
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
import { CoreCoursesProvider } from '@core/courses/providers/courses';
import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover';
import { TranslateService } from '@ngx-translate/core';
import { Network } from '@ionic-native/network';
/**
* Page that displays the calendar events.
@ -31,7 +37,7 @@ import { TranslateService } from '@ngx-translate/core';
selector: 'page-addon-calendar-index',
templateUrl: 'index.html',
})
export class AddonCalendarIndexPage implements OnInit {
export class AddonCalendarIndexPage implements OnInit, OnDestroy {
@ViewChild(AddonCalendarCalendarComponent) calendarComponent: AddonCalendarCalendarComponent;
protected allCourses = {
@ -39,6 +45,18 @@ export class AddonCalendarIndexPage implements OnInit {
fullname: this.translate.instant('core.fulllistofcourses'),
category: -1
};
protected eventId: number;
protected currentSiteId: string;
// Observers.
protected newEventObserver: any;
protected discardedObserver: any;
protected editEventObserver: any;
protected deleteEventObserver: any;
protected undeleteEventObserver: any;
protected syncObserver: any;
protected manualSyncObserver: any;
protected onlineObserver: any;
courseId: number;
categoryId: number;
@ -46,63 +64,177 @@ export class AddonCalendarIndexPage implements OnInit {
courses: any[];
notificationsEnabled = false;
loaded = false;
hasOffline = false;
isOnline = false;
syncIcon: string;
constructor(localNotificationsProvider: CoreLocalNotificationsProvider,
navParams: NavParams,
network: Network,
zone: NgZone,
sitesProvider: CoreSitesProvider,
private navCtrl: NavController,
private domUtils: CoreDomUtilsProvider,
private calendarProvider: AddonCalendarProvider,
private calendarOffline: AddonCalendarOfflineProvider,
private calendarHelper: AddonCalendarHelperProvider,
private calendarSync: AddonCalendarSyncProvider,
private translate: TranslateService,
private eventsProvider: CoreEventsProvider,
private coursesProvider: CoreCoursesProvider,
private popoverCtrl: PopoverController) {
private popoverCtrl: PopoverController,
private appProvider: CoreAppProvider) {
this.courseId = navParams.get('courseId');
this.eventId = navParams.get('eventId') || false;
this.notificationsEnabled = localNotificationsProvider.isAvailable();
this.currentSiteId = sitesProvider.getCurrentSiteId();
// Listen for events added. When an event is added, reload the data.
this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => {
if (data && data.event) {
this.loaded = false;
this.refreshData(true, false);
}
}, this.currentSiteId);
// Listen for new event discarded event. When it does, reload the data.
this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => {
this.loaded = false;
this.refreshData(true, false);
}, this.currentSiteId);
// Listen for events edited. When an event is edited, reload the data.
this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => {
if (data && data.event) {
this.loaded = false;
this.refreshData(true, false);
}
}, this.currentSiteId);
// Refresh data if calendar events are synchronized automatically.
this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => {
this.loaded = false;
this.refreshData();
}, this.currentSiteId);
// Refresh data if calendar events are synchronized manually but not by this page.
this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => {
if (data && data.source != 'index') {
this.loaded = false;
this.refreshData();
}
}, this.currentSiteId);
// Update the events when an event is deleted.
this.deleteEventObserver = eventsProvider.on(AddonCalendarProvider.DELETED_EVENT_EVENT, (data) => {
this.loaded = false;
this.refreshData();
}, this.currentSiteId);
// Update the "hasOffline" property if an event deleted in offline is restored.
this.undeleteEventObserver = eventsProvider.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, (data) => {
this.calendarOffline.hasOfflineData().then((hasOffline) => {
this.hasOffline = hasOffline;
});
}, this.currentSiteId);
// Refresh online status when changes.
this.onlineObserver = network.onchange().subscribe(() => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
zone.run(() => {
this.isOnline = this.appProvider.isOnline();
});
});
}
/**
* View loaded.
*/
ngOnInit(): void {
this.fetchData();
if (this.eventId) {
// There is an event to load, open the event in a new state.
this.gotoEvent(this.eventId);
}
this.fetchData(true, false);
}
/**
* Fetch all the data required for the view.
*
* @param {boolean} [sync] Whether it should try to synchronize offline events.
* @param {boolean} [showErrors] Whether to show sync errors to the user.
* @return {Promise<any>} Promise resolved when done.
*/
fetchData(): Promise<any> {
const promises = [];
fetchData(sync?: boolean, showErrors?: boolean): Promise<any> {
// Load courses for the popover.
promises.push(this.coursesProvider.getUserCourses(false).then((courses) => {
// Add "All courses".
courses.unshift(this.allCourses);
this.courses = courses;
this.syncIcon = 'spinner';
this.isOnline = this.appProvider.isOnline();
if (this.courseId) {
// Search the course to get the category.
const course = this.courses.find((course) => {
return course.id == this.courseId;
});
let promise;
if (course) {
this.categoryId = course.category;
if (sync) {
// Try to synchronize offline events.
promise = this.calendarSync.syncEvents().then((result) => {
if (result.warnings && result.warnings.length) {
this.domUtils.showErrorModal(result.warnings[0]);
}
}
}));
// Check if user can create events.
promises.push(this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => {
this.canCreate = canEdit;
}));
if (result.updated) {
// Trigger a manual sync event.
result.source = 'index';
return Promise.all(promises).catch((error) => {
this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId);
}
}).catch((error) => {
if (showErrors) {
this.domUtils.showErrorModalDefault(error, 'core.errorsync', true);
}
});
} else {
promise = Promise.resolve();
}
return promise.then(() => {
const promises = [];
this.hasOffline = false;
// Load courses for the popover.
promises.push(this.coursesProvider.getUserCourses(false).then((courses) => {
// Add "All courses".
courses.unshift(this.allCourses);
this.courses = courses;
if (this.courseId) {
// Search the course to get the category.
const course = this.courses.find((course) => {
return course.id == this.courseId;
});
if (course) {
this.categoryId = course.category;
}
}
}));
// Check if user can create events.
promises.push(this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => {
this.canCreate = canEdit;
}));
// Check if there is offline data.
promises.push(this.calendarOffline.hasOfflineData().then((hasOffline) => {
this.hasOffline = hasOffline;
}));
return Promise.all(promises);
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true);
}).finally(() => {
this.loaded = true;
this.syncIcon = 'sync';
});
}
@ -110,13 +242,31 @@ export class AddonCalendarIndexPage implements OnInit {
* Refresh the data.
*
* @param {any} [refresher] Refresher.
* @param {Function} [done] Function to call when done.
* @param {boolean} [showErrors] Whether to show sync errors to the user.
* @return {Promise<any>} Promise resolved when done.
*/
doRefresh(refresher?: any): void {
if (!this.loaded) {
return;
doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise<any> {
if (this.loaded) {
return this.refreshData(true, showErrors).finally(() => {
refresher && refresher.complete();
done && done();
});
}
return Promise.resolve();
}
/**
* Refresh the data.
*
* @param {boolean} [sync] Whether it should try to synchronize offline events.
* @param {boolean} [showErrors] Whether to show sync errors to the user.
* @return {Promise<any>} Promise resolved when done.
*/
refreshData(sync?: boolean, showErrors?: boolean): Promise<any> {
this.syncIcon = 'spinner';
const promises = [];
promises.push(this.calendarProvider.invalidateAllowedEventTypes().then(() => {
@ -126,11 +276,27 @@ export class AddonCalendarIndexPage implements OnInit {
// Refresh the sub-component.
promises.push(this.calendarComponent.refreshData());
Promise.all(promises).finally(() => {
refresher && refresher.complete();
return Promise.all(promises).finally(() => {
return this.fetchData(sync, showErrors);
});
}
/**
* Navigate to a particular event.
*
* @param {number} eventId Event to load.
*/
gotoEvent(eventId: number): void {
if (eventId < 0) {
// It's an offline event, go to the edit page.
this.openEdit(eventId);
} else {
this.navCtrl.push('AddonCalendarEventPage', {
id: eventId
});
}
}
/**
* Show the context menu.
*
@ -182,4 +348,18 @@ export class AddonCalendarIndexPage implements OnInit {
openSettings(): void {
this.navCtrl.push('AddonCalendarSettingsPage');
}
/**
* Page destroyed.
*/
ngOnDestroy(): void {
this.newEventObserver && this.newEventObserver.off();
this.discardedObserver && this.discardedObserver.off();
this.editEventObserver && this.editEventObserver.off();
this.deleteEventObserver && this.deleteEventObserver.off();
this.undeleteEventObserver && this.undeleteEventObserver.off();
this.syncObserver && this.syncObserver.off();
this.manualSyncObserver && this.manualSyncObserver.off();
this.onlineObserver && this.onlineObserver.unsubscribe();
}
}

View File

@ -280,6 +280,18 @@ export class AddonCalendarOfflineProvider {
});
}
/**
* Check whether there's offline data for a site.
*
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with boolean: true if has offline data, false otherwise.
*/
hasOfflineData(siteId?: string): Promise<boolean> {
return this.getAllEventsIds(siteId).then((ids) => {
return ids.length > 0;
});
}
/**
* Check if an event is deleted.
*

View File

@ -18,6 +18,7 @@ import { CoreSitesProvider } from '@providers/sites';
import { CoreCourseProvider } from '@core/course/providers/course';
import { AddonCalendarProvider } from './calendar';
import { CoreConstants } from '@core/constants';
import * as moment from 'moment';
/**
* Service that provides some features regarding lists of courses and categories.
@ -85,6 +86,41 @@ export class AddonCalendarHelperProvider {
});
}
/**
* Classify events into their respective months and days. If an event duration covers more than one day,
* it will be included in all the days it lasts.
*
* @param {any[]} events Events to classify.
* @return {{[monthId: string]: {[day: number]: any[]}}} Object with the classified events.
*/
classifyIntoMonths(events: any[]): {[monthId: string]: {[day: number]: any[]}} {
const result = {};
events.forEach((event) => {
const treatedDay = moment(new Date(event.timestart * 1000)),
endDay = moment(new Date((event.timestart + (event.timeduration || 0)) * 1000));
// Add the event to all the days it lasts.
while (!treatedDay.isAfter(endDay, 'day')) {
const monthId = this.getMonthId(treatedDay.year(), treatedDay.month() + 1),
day = treatedDay.date();
if (!result[monthId]) {
result[monthId] = {};
}
if (!result[monthId][day]) {
result[monthId][day] = [];
}
result[monthId][day].push(event);
treatedDay.add(1, 'day'); // Treat next day.
}
});
return result;
}
/**
* Convenience function to format some event data to be rendered.
*
@ -97,7 +133,7 @@ export class AddonCalendarHelperProvider {
e.moduleIcon = e.icon;
}
if (e.id < 0) {
if (typeof e.duration != 'undefined') {
// It's an offline event, add some calculated data.
e.format = 1;
e.visible = 1;
@ -140,6 +176,17 @@ export class AddonCalendarHelperProvider {
return options;
}
/**
* Get the month "id" (year + month).
*
* @param {number} year Year.
* @param {number} month Month.
* @return {string} The "id".
*/
getMonthId(year: number, month: number): string {
return year + '#' + month;
}
/**
* Check if the data of an event has changed.
*