Skip to content

Commit

Permalink
add logic for splitting recurring events
Browse files Browse the repository at this point in the history
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
  • Loading branch information
georgehrke committed Mar 11, 2018
1 parent 4fac851 commit 7e56a3c
Show file tree
Hide file tree
Showing 6 changed files with 469 additions and 41 deletions.
45 changes: 32 additions & 13 deletions js/app/controllers/calcontroller.js
Expand Up @@ -78,6 +78,22 @@ app.controller('CalController', ['$scope', 'Calendar', 'CalendarService', 'VEven
});
}

function updateAndRenderEvent(newCalendar, oldCalendar, vevent, fcEvent, view) {
// was the event moved to another calendar?
if (newCalendar === oldCalendar) {
VEventService.update(vevent).then(function() {
fc.elm.fullCalendar('removeEvents', fcEvent.id);

if (newCalendar.enabled) {
fc.elm.fullCalendar('refetchEventSources', newCalendar.fcEventSource);
}
});
} else {
deleteAndRemoveEvent(vevent, fcEvent);
createAndRenderEvent(newCalendar, vevent.data, view.start, view.end, $scope.defaulttimezone);
}
}

$scope.$watchCollection('calendars', function(newCalendars, oldCalendars) {
newCalendars.filter(function(calendar) {
return oldCalendars.indexOf(calendar) === -1;
Expand Down Expand Up @@ -205,7 +221,12 @@ app.controller('CalController', ['$scope', 'Calendar', 'CalendarService', 'VEven
}
});
}).then(function(result) {
createAndRenderEvent(result.calendar, result.vevent.data, view.start, view.end, $scope.defaulttimezone);
result.create = result.create || [];

result.create.forEach(eventToCreate => {
createAndRenderEvent(eventToCreate.calendar, eventToCreate.vevent.data,
view.start, view.end, $scope.defaulttimezone);
});
}).catch(function(reason) {
//fcEvent is removed by unlock callback
//no need to anything
Expand All @@ -227,19 +248,17 @@ app.controller('CalController', ['$scope', 'Calendar', 'CalendarService', 'VEven
fcEvt.editable = fcEvent.calendar.writable;
fc.elm.fullCalendar('updateEvent', fcEvt);
}).then(function(result) {
// was the event moved to another calendar?
if (result.calendar === oldCalendar) {
VEventService.update(vevent).then(function() {
fc.elm.fullCalendar('removeEvents', fcEvent.id);
result.create = result.create || [];
result.update = result.update || [];

if (result.calendar.enabled) {
fc.elm.fullCalendar('refetchEventSources', result.calendar.fcEventSource);
}
});
} else {
deleteAndRemoveEvent(vevent, fcEvent);
createAndRenderEvent(result.calendar, result.vevent.data, view.start, view.end, $scope.defaulttimezone);
}
result.create.forEach(eventToCreate => {
createAndRenderEvent(eventToCreate.calendar, eventToCreate.vevent.data,
view.start, view.end, $scope.defaulttimezone);
});
result.update.forEach(eventToUpdate => {
updateAndRenderEvent(eventToUpdate.calendar, oldCalendar, eventToUpdate.vevent,
fcEvent, view);
});
}).catch(function(reason) {
if (reason === 'delete') {
deleteAndRemoveEvent(vevent, fcEvent);
Expand Down
4 changes: 2 additions & 2 deletions js/app/controllers/editorRecurrenceQuestionController.js
Expand Up @@ -34,9 +34,9 @@ app.controller('EditorRecurrenceQuestionController', function($scope, $uibModalI
/**
* edit only this event
*/
$scope.onlyThis = () => {
$scope.thisOnly = () => {
$uibModalInstance.close({
action: 'onlyThis'
action: 'thisOnly'
});
};

Expand Down
172 changes: 170 additions & 2 deletions js/app/models/fcEventModel.js
Expand Up @@ -29,11 +29,16 @@ app.factory('FcEvent', function(SimpleEvent) {
* @param {ICAL.Component} event
* @param {ICAL.Time} start
* @param {ICAL.Time} end
* @param {object} recurrenceDetails
*/
function FcEvent(vevent, event, start, end) {
function FcEvent(vevent, event, start, end, recurrenceDetails) {
const context = {vevent, event};
context.iCalEvent = new ICAL.Event(event);

context.previousEvent = null;
context.peviousVEvent = null;
context.previousRRule = null;

let id = context.vevent.uri;
if (event.hasProperty('recurrence-id')) {
id += context.event.getFirstPropertyValue('recurrence-id').toICALString();
Expand All @@ -54,7 +59,8 @@ app.factory('FcEvent', function(SimpleEvent) {
backgroundColor: vevent.calendar.color,
borderColor: vevent.calendar.color,
textColor: vevent.calendar.textColor,
title: event.getFirstPropertyValue('summary')
title: event.getFirstPropertyValue('summary'),
recurrenceDetails: recurrenceDetails
};

Object.defineProperties(iface, {
Expand Down Expand Up @@ -191,6 +197,168 @@ app.factory('FcEvent', function(SimpleEvent) {
context.vevent.touch();
};

/**
*
*/
iface.createRecurrenceException = function() {
context.previousEvent = context.event;
const ics = context.event.toString();
const newComp = new ICAL.Component(ICAL.parse(ics));

newComp.addPropertyWithValue('recurrence-id',
iface.recurrenceDetails.recurrenceId);

if (newComp.hasProperty('dtend')) {
const diff = newComp.getFirstPropertyValue('dtend').subtractDateTz(
newComp.getFirstPropertyValue('dtstart'));
const dtend = iface.recurrenceDetails.recurrenceId.clone();
dtend.addDuration(diff);

newComp.updatePropertyWithValue('dtend', dtend);
}

newComp.updatePropertyWithValue('dtstart',
iface.recurrenceDetails.recurrenceId.clone());

newComp.removeAllProperties('rrule');
newComp.removeAllProperties('exrule');
newComp.removeAllProperties('rdate');
newComp.removeAllProperties('exdate');

context.vevent.comp.addSubcomponent(newComp);
context.event = newComp;

return newComp;
};

/**
* revert changes previously made by
* createRecurrenceException
*/
iface.revertCreateRecurrenceException = function() {
if (context.previousEvent) {
context.vevent.comp.removeSubcomponent(context.event);
context.event = context.previousEvent;
}
};

/**
* create a fork for modifying this and all future events
*/
iface.createFork = function() {
const oldRRule = context.event.getFirstPropertyValue('rrule');
if (!oldRRule) {
return;
}

context.previousEvent = context.event;
context.previousVEvent = context.vevent;
context.previousRRule = oldRRule.clone();

const ics = context.event.toString();
const newComp = new ICAL.Component(ICAL.parse(ics));
const fork = context.vevent.fork(newComp);

const newRRule = newComp.getFirstPropertyValue('rrule');
if (oldRRule.count) {
newRRule.count = oldRRule.count - iface.recurrenceDetails.count + 1;
oldRRule.count = iface.recurrenceDetails.count - 1;
} else {
oldRRule.until = iface.recurrenceDetails.recurrenceId.clone();
console.log(oldRRule.until.toString());
oldRRule.until.addDuration(ICAL.Duration.fromSeconds(-1 * 60 * 60 * 24));
console.log(oldRRule.until.toString());
}

context.previousEvent.updatePropertyWithValue('rrule', oldRRule);
newComp.updatePropertyWithValue('rrule', newRRule);

if (newComp.hasProperty('dtend')) {
const diff = newComp.getFirstPropertyValue('dtend').subtractDateTz(
newComp.getFirstPropertyValue('dtstart'));
const dtend = iface.recurrenceDetails.recurrenceId.clone();
dtend.addDuration(diff);

newComp.updatePropertyWithValue('dtend', dtend);
}

newComp.updatePropertyWithValue('dtstart',
iface.recurrenceDetails.recurrenceId.clone());

context.vevent = fork;
context.event = newComp;

console.log('successfully forked event');
console.log('fork:', fork, context.vevent, iface.vevent);
console.log('event:', newComp, context.event, iface.event);

return iface;
};

/**
* revert changes previously made by
* createFork
*/
iface.revertCreateFork = function() {
if (context.previousRRule) {
context.vevent = context.previousVEvent;
context.event = context.previousEvent;
context.event.updatePropertyWithValue('rrule', context.previousRRule);
}

context.previousEvent = null;
context.previousVEvent = null;
context.previousRRule = null;
};

iface.isForked = function() {
return context.previousRRule;
};

iface.revertCreateForkKeepingRRule = function() {
if (context.previousRRule) {
context.vevent = context.previousVEvent;
context.event = context.previousEvent;
}

context.previousEvent = null;
context.previousVEvent = null;
context.previousRRule = null;
};

iface.getPreviousVEvent = () => {
return context.previousVEvent;
};

/**
* Does this event have multiple recurrence rule sets?
* @return {boolean}
*/
iface.hasMultipleRRules = function() {
// getAllProperties always returns an array, so its safe to use length directly
return context.event.getAllProperties('rrule').length > 1;
};

/**
* removes VEvent that this fcEvent is based on from
* VEvent object and returns number of vEvents left
* @return {number}
*/
iface.removeFromVEvent = function() {
if (context.event.hasProperty('recurrence-id')) {
const recurrenceId = context.event.getFirstPropertyValue('recurrence-id');
const rootElement = context.vevent.findComponentByRecurrenceId(null);

if (rootElement) {
rootElement.addPropertyWithValue('exdate', recurrenceId.clone());
}
}

context.vevent.comp.removeSubcomponent(context.event);

return context.vevent.comp.getAllSubcomponents('vevent').length;
};

/**
* lock fc event for editing
*/
Expand Down

0 comments on commit 7e56a3c

Please sign in to comment.