MOBILE-3909 calendar: Allow setting reminders for offline events

main
Dani Palou 2021-11-15 09:03:58 +01:00
parent 56596ad30e
commit b31b0764a5
8 changed files with 166 additions and 87 deletions

View File

@ -521,12 +521,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
* @param 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 {
CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`);
}
CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`);
}
/**

View File

@ -564,7 +564,10 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
if (event) {
CoreEvents.trigger(
AddonCalendarProvider.NEW_EVENT_EVENT,
{ eventId: event.id },
{
eventId: event.id,
oldEventId: this.eventId,
},
this.currentSite.getId(),
);
} else {

View File

@ -21,8 +21,8 @@
[priority]="400" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(undefined, $event, true)"
[iconAction]="syncIcon" [closeOnClick]="false">
</core-context-menu-item>
<core-context-menu-item [hidden]="!canEdit || !event || !event.canedit || event.deleted" [priority]="300"
[content]="'core.edit' | translate" (action)="openEdit()" iconAction="fas-edit">
<core-context-menu-item [hidden]="!event || !event.canedit || event.deleted || (!canEdit && event.id > 0)"
[priority]="300" [content]="'core.edit' | translate" (action)="openEdit()" iconAction="fas-edit">
</core-context-menu-item>
<core-context-menu-item [hidden]="!event || !event.candelete || event.deleted" [priority]="200"
[content]="'core.delete' | translate" (action)="deleteEvent()"

View File

@ -52,6 +52,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
protected eventId!: number;
protected siteHomeId: number;
protected newEventObserver: CoreEventObserver;
protected editEventObserver: CoreEventObserver;
protected syncObserver: CoreEventObserver;
protected manualSyncObserver: CoreEventObserver;
@ -88,7 +89,16 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// Listen for event edited. If current event is edited, reload the data.
this.editEventObserver = CoreEvents.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => {
if (data && data.eventId == this.eventId) {
if (data && data.eventId === this.eventId) {
this.eventLoaded = false;
this.refreshEvent(true, false);
}
}, this.currentSiteId);
// Listen for event created. If user edits the data of a new offline event or it's sent to server, this event is triggered.
this.newEventObserver = CoreEvents.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => {
if (this.eventId < 0 && data && (data.eventId === this.eventId || data.oldEventId === this.eventId)) {
this.eventId = data.eventId;
this.eventLoaded = false;
this.refreshEvent(true, false);
}
@ -173,53 +183,22 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
* @return Promise resolved when done.
*/
async fetchEvent(sync = false, showErrors = false): Promise<void> {
let deleted = false;
this.isOnline = CoreApp.isOnline();
if (sync) {
// Try to synchronize offline events.
try {
const result = await AddonCalendarSync.syncEvents();
if (result.warnings && result.warnings.length) {
CoreDomUtils.showErrorModal(result.warnings[0]);
}
const deleted = await this.syncEvents(showErrors);
if (result.deleted && result.deleted.indexOf(this.eventId) != -1) {
// This event was deleted during the sync.
deleted = true;
}
if (result.updated) {
// Trigger a manual sync event.
result.source = 'event';
CoreEvents.trigger(
AddonCalendarSyncProvider.MANUAL_SYNCED,
result,
this.currentSiteId,
);
}
} catch (error) {
if (showErrors) {
CoreDomUtils.showErrorModalDefault(error, 'core.errorsync', true);
}
if (deleted) {
return;
}
}
if (deleted) {
return;
}
try {
// Get the event data.
const event = await AddonCalendar.getEventById(this.eventId);
const formattedEvent = await AddonCalendarHelper.formatEventData(event);
this.event = formattedEvent;
// Load reminders, and re-schedule them if needed (maybe the event time has changed).
this.loadReminders();
AddonCalendar.scheduleEventsNotifications([this.event]);
if (this.eventId >= 0) {
const event = await AddonCalendar.getEventById(this.eventId);
this.event = await AddonCalendarHelper.formatEventData(event);
}
try {
const offlineEvent = AddonCalendarHelper.formatOfflineEventData(
@ -228,13 +207,28 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// There is offline data, apply it.
this.hasOffline = true;
this.event = Object.assign(this.event, offlineEvent);
this.event = Object.assign(this.event || {}, offlineEvent);
} catch {
// No offline data.
this.hasOffline = false;
if (this.eventId < 0) {
// It's an offline event, but it wasn't found. Shouldn't happen.
CoreDomUtils.showErrorModal('Event not found.');
CoreNavigator.back();
return;
}
}
if (!this.event) {
return; // At this point we should always have the event, adding this check to avoid TS errors.
}
// Load reminders, and re-schedule them if needed (maybe the event time has changed).
this.loadReminders();
AddonCalendar.scheduleEventsNotifications([this.event]);
// Reset some of the calculated data.
this.categoryPath = '';
this.courseName = '';
@ -253,6 +247,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
}
const promises: Promise<void>[] = [];
const event = this.event;
const courseId = this.event.courseid;
if (courseId != this.siteHomeId) {
@ -266,16 +261,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// If it's a group event, get the name of the group.
if (courseId && this.event.groupid) {
promises.push(CoreGroups.getUserGroupsInCourse(courseId).then((groups) => {
const group = groups.find((group) => group.id == formattedEvent.groupid);
this.groupName = group ? group.name : '';
return;
}).catch(() => {
// Error getting groups, just don't show the group name.
this.groupName = '';
}));
promises.push(this.loadGroupName(this.event, courseId));
}
if (this.event.iscategoryevent && this.event.category) {
@ -290,14 +276,14 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// Check if event was deleted in offine.
promises.push(AddonCalendarOffline.isEventDeleted(this.eventId).then((deleted) => {
formattedEvent.deleted = deleted;
event.deleted = deleted;
return;
}));
// Re-calculate the formatted time so it uses the device date.
promises.push(AddonCalendar.getCalendarTimeFormat().then(async (timeFormat) => {
formattedEvent.formattedtime = await AddonCalendar.formatEventTime(formattedEvent, timeFormat);
event.formattedtime = await AddonCalendar.formatEventTime(event, timeFormat);
return;
}));
@ -311,6 +297,69 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
this.syncIcon = CoreConstants.ICON_SYNC;
}
/**
* Sync offline events.
*
* @param showErrors Whether to show sync errors to the user.
* @return Promise resolved with boolean: whether event was deleted on sync.
*/
protected async syncEvents(showErrors = false): Promise<boolean> {
let deleted = false;
// Try to synchronize offline events.
try {
const result = await AddonCalendarSync.syncEvents();
if (result.warnings && result.warnings.length) {
CoreDomUtils.showErrorModal(result.warnings[0]);
}
if (result.deleted && result.deleted.indexOf(this.eventId) != -1) {
// This event was deleted during the sync.
deleted = true;
} else if (this.eventId < 0 && result.offlineIdMap[this.eventId]) {
// Event was created, use the online ID.
this.eventId = result.offlineIdMap[this.eventId];
}
if (result.updated) {
// Trigger a manual sync event.
result.source = 'event';
CoreEvents.trigger(
AddonCalendarSyncProvider.MANUAL_SYNCED,
result,
this.currentSiteId,
);
}
} catch (error) {
if (showErrors) {
CoreDomUtils.showErrorModalDefault(error, 'core.errorsync', true);
}
}
return deleted;
}
/**
* Load group name.
*
* @param event Event.
* @param courseId Course ID.
* @return Promise resolved when done.
*/
protected async loadGroupName(event: AddonCalendarEventToDisplay, courseId: number): Promise<void> {
try {
const groups = await CoreGroups.getUserGroupsInCourse(courseId);
const group = groups.find((group) => group.id == event.groupid);
this.groupName = group ? group.name : '';
} catch {
// Error getting groups, just don't show the group name.
this.groupName = '';
}
}
/**
* Add a reminder for this event.
*/
@ -392,7 +441,9 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
const promises: Promise<void>[] = [];
promises.push(AddonCalendar.invalidateEvent(this.eventId));
if (this.eventId > 0) {
promises.push(AddonCalendar.invalidateEvent(this.eventId));
}
promises.push(AddonCalendar.invalidateTimeFormat());
await CoreUtils.allPromisesIgnoringErrors(promises);
@ -459,9 +510,14 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
const modal = await CoreDomUtils.showModalLoading('core.sending', true);
try {
const sent = await AddonCalendar.deleteEvent(this.event.id, this.event.name, deleteAll);
let onlineEventDeleted = false;
if (this.event.id < 0) {
await AddonCalendarOffline.deleteEvent(this.event.id);
} else {
onlineEventDeleted = await AddonCalendar.deleteEvent(this.event.id, this.event.name, deleteAll);
}
if (sent) {
if (onlineEventDeleted) {
// Event deleted, invalidate right days & months.
try {
await AddonCalendarHelper.refreshAfterChangeEvent(this.event, deleteAll ? this.event.eventcount : 1);
@ -471,12 +527,16 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
}
// Trigger an event.
CoreEvents.trigger(AddonCalendarProvider.DELETED_EVENT_EVENT, {
eventId: this.eventId,
sent: sent,
}, CoreSites.getCurrentSiteId());
if (this.event.id < 0) {
CoreEvents.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, CoreSites.getCurrentSiteId());
} else {
CoreEvents.trigger(AddonCalendarProvider.DELETED_EVENT_EVENT, {
eventId: this.eventId,
sent: onlineEventDeleted,
}, CoreSites.getCurrentSiteId());
}
if (sent) {
if (onlineEventDeleted || this.event.id < 0) {
CoreDomUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000);
// Event deleted, close the view.
@ -537,11 +597,21 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
// Event was deleted, close the view.
CoreNavigator.back();
} else if (data.events && (!isManual || data.source != 'event')) {
const event = data.events.find((ev) => ev.id == this.eventId);
if (this.eventId < 0) {
if (data.offlineIdMap[this.eventId]) {
// Event was created, use the online ID.
this.eventId = data.offlineIdMap[this.eventId];
if (event) {
this.eventLoaded = false;
this.refreshEvent();
this.eventLoaded = false;
this.refreshEvent();
}
} else {
const event = data.events.find((ev) => ev.id == this.eventId);
if (event) {
this.eventLoaded = false;
this.refreshEvent();
}
}
}
}
@ -550,10 +620,11 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
* Page destroyed.
*/
ngOnDestroy(): void {
this.editEventObserver?.off();
this.syncObserver?.off();
this.manualSyncObserver?.off();
this.onlineObserver?.unsubscribe();
this.editEventObserver.off();
this.syncObserver.off();
this.manualSyncObserver.off();
this.onlineObserver.unsubscribe();
this.newEventObserver.off();
clearInterval(this.updateCurrentTime);
}

View File

@ -301,12 +301,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy {
* @param 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 {
CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`);
}
CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`);
}
/**

View File

@ -245,6 +245,8 @@ export class AddonCalendarHelperProvider {
format: 1,
visible: 1,
offline: true,
canedit: event.id < 0,
candelete: event.id < 0,
timeduration: 0,
};

View File

@ -124,6 +124,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider<AddonCalenda
const result: AddonCalendarSyncEvents = {
warnings: [],
events: [],
offlineIdMap: {},
deleted: [],
toinvalidate: [],
updated: false,
@ -256,10 +257,13 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider<AddonCalenda
); // Clone the object because it will be modified in the submit function.
try {
const newEvent = await AddonCalendar.submitEventOnline(eventId > 0 ? eventId : 0, data, siteId);
const newEvent = await AddonCalendar.submitEventOnline(eventId, data, siteId);
result.updated = true;
result.events.push(newEvent);
if (eventId < 0) {
result.offlineIdMap[eventId] = newEvent.id;
}
// Add data to invalidate.
const numberOfRepetitions = data.repeat ? data.repeats :
@ -298,6 +302,7 @@ export const AddonCalendarSync = makeSingleton(AddonCalendarSyncProvider);
export type AddonCalendarSyncEvents = {
warnings: string[];
events: AddonCalendarEvent[];
offlineIdMap: Record<number, number>; // Map offline ID with online ID for created events.
deleted: number[];
toinvalidate: AddonCalendarSyncInvalidateEvent[];
updated: boolean;

View File

@ -1687,7 +1687,7 @@ export class AddonCalendarProvider {
/**
* Submit an event, either to create it or to edit it. It will fail if offline or cannot connect.
*
* @param eventId ID of the event. If undefined/null, create a new event.
* @param eventId ID of the event. If undefined/null or negative number, create a new event.
* @param formData Form data.
* @param siteId Site ID. If not provided, current site.
* @return Promise resolved when done.
@ -1700,7 +1700,7 @@ export class AddonCalendarProvider {
const site = await CoreSites.getSite(siteId);
// Add data that is "hidden" in web.
formData.id = eventId;
formData.id = eventId > 0 ? eventId : 0;
formData.userid = site.getUserId();
formData.visible = 1;
formData.instance = 0;
@ -1724,6 +1724,13 @@ export class AddonCalendarProvider {
});
}
if (eventId < 0) {
// Offline event has been sent. Change reminders eventid if any.
await CoreUtils.ignoreErrors(
site.getDb().updateRecords(REMINDERS_TABLE, { eventid: result.event.id }, { eventid: eventId }),
);
}
return result.event;
}
@ -2240,6 +2247,7 @@ export type AddonCalendarEventToDisplay = Partial<AddonCalendarCalendarEvent> &
*/
export type AddonCalendarUpdatedEventEvent = {
eventId: number;
oldEventId?: number; // Old event ID. Used when an offline event is sent.
sent?: boolean;
};