Loading src/mixins/EditorMixin.js +216 −80 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import logger from '../utils/logger.js' import { mapEventComponentToCalendarObjectInstanceObject } from '../models/calendarObjectInstance.js' import { getIllustrationForTitle } from '../utils/illustration.js' import { getPrefixedRoute } from '../utils/router.js' import { dateFactory } from '../utils/date.js' import { uidToHexColor } from '../utils/color.js' /** * This is a mixin for the editor. It contains common Vue stuff, that is Loading Loading @@ -58,29 +60,153 @@ export default { } }, computed: { /** * Returns the events title or an empty string if the event is still loading * * @returns {string} */ title() { if (!this.eventComponent) { return '' } return this.calendarObjectInstance.title || '' }, /** * Returns the location or null if the event is still loading * * @returns {string|null} */ location() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.location }, /** * Returns the description or null if the event is still loading * * @returns {string|null} */ description() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.description }, /** * Returns the start-date (without timezone) or null if the event is still loading * * @returns {Date|null} */ startDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startDate }, /** * Returns the timezone of the event's start-date or null if the event is still loading * * @returns {string|null} */ startTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startTimezoneId }, /** * Returns the end-date (without timezone) or null if the event is still loading * * @returns {Date|null} */ endDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endDate }, /** * Returns the timezone of the event's end-date or null if the event is still loading * * @returns {string|null} */ endTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endTimezoneId }, /** * Returns whether or not the event is all-day or null if the event is still loading * * @returns {boolean|null} */ isAllDay() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.isAllDay }, /** * Returns whether or not the user is allowed to modify the all-day setting * * @returns {boolean} */ canModifyAllDay() { if (!this.calendarObjectInstance) { return false } return this.calendarObjectInstance.canModifyAllDay }, /** * Returns an illustration matching this event's title * * @returns {string} */ backgroundImage() { return getIllustrationForTitle(this.title) }, /** * Returns the color of the calendar selected by the user * This is used to color illustration * * @returns {string|*} */ selectedCalendarColor() { if (!this.selectedCalendar) { return '' const calendars = this.$store.getters.sortedCalendars if (calendars.length > 0) { return calendars[0].color } return this.selectedCalendar.color }, title() { if (!this.eventComponent) { return '' return uidToHexColor('') } return this.calendarObjectInstance.title || '' return this.selectedCalendar.color }, // Did the event load without errors? /** * Returns whether or not to display event details * * @returns {boolean} */ displayDetails() { return !this.isLoading && !this.error }, // Is the event read-only or read-write /** * Returns whether or not to allow editing the event * * @returns {boolean} */ isReadOnly() { if (!this.calendarObject) { return true Loading @@ -93,7 +219,11 @@ export default { return calendar.readOnly }, // List of all selectable calendars /** * Returns all calendars selectable by the user * * @returns {Object[]} */ calendars() { if (this.isReadOnly && this.calendarObject) { return [ Loading @@ -103,15 +233,28 @@ export default { return this.$store.getters.sortedCalendars }, // Get the selected calendar as calendar-object /** * Returns the object of the selected calendar * * @returns {Object} */ selectedCalendar() { return this.$store.getters.getCalendarById(this.calendarId) }, // Current timezone of the user /** * Returns the preferred timezone of the user. * If the timezone is set to automatic, it returns the detected one * * @returns {string} */ currentUserTimezone() { return this.$store.getters.getResolvedTimezone }, // Can you delete this event? /** * Returns whether or not the user is allowed to delete this event * * @returns {boolean} */ canDelete() { if (!this.calendarObject) { return false Loading @@ -125,7 +268,11 @@ export default { return this.calendarObject.existsOnServer() }, // Can you create recurrence-exceptions for this event? /** * Returns whether or not the user is allowed to create recurrence exceptions for this event * * @returns {boolean} */ canCreateRecurrenceException() { if (!this.eventComponent) { return false Loading @@ -133,11 +280,20 @@ export default { return this.eventComponent.canCreateRecurrenceExceptions() }, // List of all RFC props we can display /** * Returns a an object with properties from RFCs including * their displayName, a description, options, etc. * * @returns {{geo, color, timeTransparency, description, resources, location, categories, accessClass, priority, status}} */ rfcProps() { return rfcProps }, // Download related properties /** * Returns whether or not this event can be downloaded from the server * * @returns {boolean} */ hasDownloadURL() { if (!this.calendarObject) { return false Loading @@ -148,6 +304,11 @@ export default { return this.calendarObject.existsOnServer() }, /** * Returns the download url as a string or null if event is loading or does not exist on the server (yet) * * @returns {string|null} */ downloadURL() { if (!this.calendarObject) { return null Loading @@ -159,6 +320,11 @@ export default { return this.calendarObject.dav.url + '?export' }, /** * Returns whether or not this is a new event * * @returns {boolean} */ isNew() { if (!this.calendarObject) { return true Loading @@ -170,62 +336,6 @@ export default { return false }, location() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.location }, description() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.description }, startDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startDate }, startTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startTimezoneId }, endDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endDate }, endTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endTimezoneId }, isAllDay() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.isAllDay }, canModifyAllDay() { if (!this.calendarObjectInstance) { return false } return this.calendarObjectInstance.canModifyAllDay }, }, methods: { /** Loading Loading @@ -429,7 +539,6 @@ export default { * @param {Date} startDate New start date */ updateStartDate(startDate) { console.debug('updating start date ...', startDate) this.$store.commit('changeStartDate', { calendarObjectInstance: this.calendarObjectInstance, startDate, Loading @@ -456,7 +565,6 @@ export default { * @param {Date} endDate New end date */ updateEndDate(endDate) { console.debug('updating end date ...', endDate) this.$store.commit('changeEndDate', { calendarObjectInstance: this.calendarObjectInstance, endDate, Loading Loading @@ -528,18 +636,32 @@ export default { const objectId = to.params.object const recurrenceId = to.params.recurrenceId const recurrenceIdDate = new Date(recurrenceId * 1000) vm.$store.dispatch('getEventByObjectId', { objectId }) .then(() => { vm.calendarObject = vm.$store.getters.getCalendarObjectById(objectId) vm.calendarId = vm.calendarObject.calendarId if (recurrenceId === 'next') { const recurrenceIdDate = dateFactory() vm.eventComponent = vm.calendarObject.getClosestRecurrence(recurrenceIdDate) } else { const recurrenceIdDate = new Date(recurrenceId * 1000) vm.eventComponent = vm.calendarObject.getObjectAtRecurrenceId(recurrenceIdDate) } vm.calendarObjectInstance = mapEventComponentToCalendarObjectInstanceObject(vm.eventComponent) vm.isEditingMasterItem = vm.eventComponent.isMasterItem() vm.isRecurrenceException = vm.eventComponent.isRecurrenceException() vm.isLoading = false if (recurrenceId === 'next') { const params = Object.assign({}, vm.$route.params, { recurrenceId: vm.eventComponent.getReferenceRecurrenceId().unixTime, }) vm.$router.replace({ name: vm.$route.name, params }) } }) }) } Loading @@ -554,7 +676,6 @@ export default { * @param {Function} next Function to be called when ready to load the next view */ beforeRouteUpdate(to, from, next) { console.debug('I\'m beforeRouteUpdate inside mixin') // If we are in the New Event dialog, we want to update the selected time if (to.name === 'NewSidebarView' || to.name === 'NewPopoverView') { // If allDay, dtstart and dtend are the same there is no need to update. Loading Loading @@ -605,12 +726,27 @@ export default { .then(() => { this.calendarObject = this.$store.getters.getCalendarObjectById(objectId) this.calendarId = this.calendarObject.calendarId this.eventComponent = this.calendarObject.getObjectAtRecurrenceId(new Date(recurrenceId * 1000)) if (recurrenceId === 'next') { const recurrenceIdDate = dateFactory() this.eventComponent = this.calendarObject.getClosestRecurrence(recurrenceIdDate) } else { const recurrenceIdDate = new Date(recurrenceId * 1000) this.eventComponent = this.calendarObject.getObjectAtRecurrenceId(recurrenceIdDate) } this.calendarObjectInstance = mapEventComponentToCalendarObjectInstanceObject(this.eventComponent) this.isEditingMasterItem = this.eventComponent.isMasterItem() this.isRecurrenceException = this.eventComponent.isRecurrenceException() this.isLoading = false if (recurrenceId === 'next') { const params = Object.assign({}, this.$route.params, { recurrenceId: this.eventComponent.getReferenceRecurrenceId().unixTime, }) this.$router.replace({ name: this.$route.name, params }) } }) next() }).catch(() => { Loading src/models/calendarObject.js +22 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ import { getParserManager } from 'calendar-js' import DateTimeValue from 'calendar-js/src/values/dateTimeValue' import CalendarComponent from 'calendar-js/src/components/calendarComponent' /** * This model represents exactly */ export default class CalendarObject { /** Loading Loading @@ -163,6 +166,25 @@ export default class CalendarObject { return firstVObject.recurrenceManager.getAllOccurrencesBetween(s, e) } /** * Get recurrence-item closest to the given recurrence-Id * This is either the one next in the future or if none exist, * the one closest in the past * * @param {Date} closeTo The time to get the * @returns {AbstractRecurringComponent|null} */ getClosestRecurrence(closeTo) { const iterator = this.vcalendar.getVObjectIterator() const firstVObject = iterator.next().value if (!firstVObject) { return null } const d = DateTimeValue.fromJSDate(closeTo, true) return firstVObject.recurrenceManager.getClosestOccurrence(d) } /** * Get recurrence-item at exactly a given recurrence-Id * Loading src/router.js +2 −2 Original line number Diff line number Diff line Loading @@ -94,11 +94,11 @@ const router = new Router({ }, { path: '/edit/:object', redirect: '/${getInitialView()}/now/edit/sidebar/:object/next' redirect: `/${getInitialView()}/now/edit/sidebar/:object/next`, }, { path: '/edit/:object/:recurrenceId', redirect: '/${getInitialView()}/now/edit/sidebar/:object/:recurrenceId' redirect: `/${getInitialView()}/now/edit/sidebar/:object/:recurrenceId`, }, /** * This is the main route that contains the current view and viewed day Loading Loading
src/mixins/EditorMixin.js +216 −80 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import logger from '../utils/logger.js' import { mapEventComponentToCalendarObjectInstanceObject } from '../models/calendarObjectInstance.js' import { getIllustrationForTitle } from '../utils/illustration.js' import { getPrefixedRoute } from '../utils/router.js' import { dateFactory } from '../utils/date.js' import { uidToHexColor } from '../utils/color.js' /** * This is a mixin for the editor. It contains common Vue stuff, that is Loading Loading @@ -58,29 +60,153 @@ export default { } }, computed: { /** * Returns the events title or an empty string if the event is still loading * * @returns {string} */ title() { if (!this.eventComponent) { return '' } return this.calendarObjectInstance.title || '' }, /** * Returns the location or null if the event is still loading * * @returns {string|null} */ location() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.location }, /** * Returns the description or null if the event is still loading * * @returns {string|null} */ description() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.description }, /** * Returns the start-date (without timezone) or null if the event is still loading * * @returns {Date|null} */ startDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startDate }, /** * Returns the timezone of the event's start-date or null if the event is still loading * * @returns {string|null} */ startTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startTimezoneId }, /** * Returns the end-date (without timezone) or null if the event is still loading * * @returns {Date|null} */ endDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endDate }, /** * Returns the timezone of the event's end-date or null if the event is still loading * * @returns {string|null} */ endTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endTimezoneId }, /** * Returns whether or not the event is all-day or null if the event is still loading * * @returns {boolean|null} */ isAllDay() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.isAllDay }, /** * Returns whether or not the user is allowed to modify the all-day setting * * @returns {boolean} */ canModifyAllDay() { if (!this.calendarObjectInstance) { return false } return this.calendarObjectInstance.canModifyAllDay }, /** * Returns an illustration matching this event's title * * @returns {string} */ backgroundImage() { return getIllustrationForTitle(this.title) }, /** * Returns the color of the calendar selected by the user * This is used to color illustration * * @returns {string|*} */ selectedCalendarColor() { if (!this.selectedCalendar) { return '' const calendars = this.$store.getters.sortedCalendars if (calendars.length > 0) { return calendars[0].color } return this.selectedCalendar.color }, title() { if (!this.eventComponent) { return '' return uidToHexColor('') } return this.calendarObjectInstance.title || '' return this.selectedCalendar.color }, // Did the event load without errors? /** * Returns whether or not to display event details * * @returns {boolean} */ displayDetails() { return !this.isLoading && !this.error }, // Is the event read-only or read-write /** * Returns whether or not to allow editing the event * * @returns {boolean} */ isReadOnly() { if (!this.calendarObject) { return true Loading @@ -93,7 +219,11 @@ export default { return calendar.readOnly }, // List of all selectable calendars /** * Returns all calendars selectable by the user * * @returns {Object[]} */ calendars() { if (this.isReadOnly && this.calendarObject) { return [ Loading @@ -103,15 +233,28 @@ export default { return this.$store.getters.sortedCalendars }, // Get the selected calendar as calendar-object /** * Returns the object of the selected calendar * * @returns {Object} */ selectedCalendar() { return this.$store.getters.getCalendarById(this.calendarId) }, // Current timezone of the user /** * Returns the preferred timezone of the user. * If the timezone is set to automatic, it returns the detected one * * @returns {string} */ currentUserTimezone() { return this.$store.getters.getResolvedTimezone }, // Can you delete this event? /** * Returns whether or not the user is allowed to delete this event * * @returns {boolean} */ canDelete() { if (!this.calendarObject) { return false Loading @@ -125,7 +268,11 @@ export default { return this.calendarObject.existsOnServer() }, // Can you create recurrence-exceptions for this event? /** * Returns whether or not the user is allowed to create recurrence exceptions for this event * * @returns {boolean} */ canCreateRecurrenceException() { if (!this.eventComponent) { return false Loading @@ -133,11 +280,20 @@ export default { return this.eventComponent.canCreateRecurrenceExceptions() }, // List of all RFC props we can display /** * Returns a an object with properties from RFCs including * their displayName, a description, options, etc. * * @returns {{geo, color, timeTransparency, description, resources, location, categories, accessClass, priority, status}} */ rfcProps() { return rfcProps }, // Download related properties /** * Returns whether or not this event can be downloaded from the server * * @returns {boolean} */ hasDownloadURL() { if (!this.calendarObject) { return false Loading @@ -148,6 +304,11 @@ export default { return this.calendarObject.existsOnServer() }, /** * Returns the download url as a string or null if event is loading or does not exist on the server (yet) * * @returns {string|null} */ downloadURL() { if (!this.calendarObject) { return null Loading @@ -159,6 +320,11 @@ export default { return this.calendarObject.dav.url + '?export' }, /** * Returns whether or not this is a new event * * @returns {boolean} */ isNew() { if (!this.calendarObject) { return true Loading @@ -170,62 +336,6 @@ export default { return false }, location() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.location }, description() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.description }, startDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startDate }, startTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.startTimezoneId }, endDate() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endDate }, endTimezone() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.endTimezoneId }, isAllDay() { if (!this.calendarObjectInstance) { return null } return this.calendarObjectInstance.isAllDay }, canModifyAllDay() { if (!this.calendarObjectInstance) { return false } return this.calendarObjectInstance.canModifyAllDay }, }, methods: { /** Loading Loading @@ -429,7 +539,6 @@ export default { * @param {Date} startDate New start date */ updateStartDate(startDate) { console.debug('updating start date ...', startDate) this.$store.commit('changeStartDate', { calendarObjectInstance: this.calendarObjectInstance, startDate, Loading @@ -456,7 +565,6 @@ export default { * @param {Date} endDate New end date */ updateEndDate(endDate) { console.debug('updating end date ...', endDate) this.$store.commit('changeEndDate', { calendarObjectInstance: this.calendarObjectInstance, endDate, Loading Loading @@ -528,18 +636,32 @@ export default { const objectId = to.params.object const recurrenceId = to.params.recurrenceId const recurrenceIdDate = new Date(recurrenceId * 1000) vm.$store.dispatch('getEventByObjectId', { objectId }) .then(() => { vm.calendarObject = vm.$store.getters.getCalendarObjectById(objectId) vm.calendarId = vm.calendarObject.calendarId if (recurrenceId === 'next') { const recurrenceIdDate = dateFactory() vm.eventComponent = vm.calendarObject.getClosestRecurrence(recurrenceIdDate) } else { const recurrenceIdDate = new Date(recurrenceId * 1000) vm.eventComponent = vm.calendarObject.getObjectAtRecurrenceId(recurrenceIdDate) } vm.calendarObjectInstance = mapEventComponentToCalendarObjectInstanceObject(vm.eventComponent) vm.isEditingMasterItem = vm.eventComponent.isMasterItem() vm.isRecurrenceException = vm.eventComponent.isRecurrenceException() vm.isLoading = false if (recurrenceId === 'next') { const params = Object.assign({}, vm.$route.params, { recurrenceId: vm.eventComponent.getReferenceRecurrenceId().unixTime, }) vm.$router.replace({ name: vm.$route.name, params }) } }) }) } Loading @@ -554,7 +676,6 @@ export default { * @param {Function} next Function to be called when ready to load the next view */ beforeRouteUpdate(to, from, next) { console.debug('I\'m beforeRouteUpdate inside mixin') // If we are in the New Event dialog, we want to update the selected time if (to.name === 'NewSidebarView' || to.name === 'NewPopoverView') { // If allDay, dtstart and dtend are the same there is no need to update. Loading Loading @@ -605,12 +726,27 @@ export default { .then(() => { this.calendarObject = this.$store.getters.getCalendarObjectById(objectId) this.calendarId = this.calendarObject.calendarId this.eventComponent = this.calendarObject.getObjectAtRecurrenceId(new Date(recurrenceId * 1000)) if (recurrenceId === 'next') { const recurrenceIdDate = dateFactory() this.eventComponent = this.calendarObject.getClosestRecurrence(recurrenceIdDate) } else { const recurrenceIdDate = new Date(recurrenceId * 1000) this.eventComponent = this.calendarObject.getObjectAtRecurrenceId(recurrenceIdDate) } this.calendarObjectInstance = mapEventComponentToCalendarObjectInstanceObject(this.eventComponent) this.isEditingMasterItem = this.eventComponent.isMasterItem() this.isRecurrenceException = this.eventComponent.isRecurrenceException() this.isLoading = false if (recurrenceId === 'next') { const params = Object.assign({}, this.$route.params, { recurrenceId: this.eventComponent.getReferenceRecurrenceId().unixTime, }) this.$router.replace({ name: this.$route.name, params }) } }) next() }).catch(() => { Loading
src/models/calendarObject.js +22 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ import { getParserManager } from 'calendar-js' import DateTimeValue from 'calendar-js/src/values/dateTimeValue' import CalendarComponent from 'calendar-js/src/components/calendarComponent' /** * This model represents exactly */ export default class CalendarObject { /** Loading Loading @@ -163,6 +166,25 @@ export default class CalendarObject { return firstVObject.recurrenceManager.getAllOccurrencesBetween(s, e) } /** * Get recurrence-item closest to the given recurrence-Id * This is either the one next in the future or if none exist, * the one closest in the past * * @param {Date} closeTo The time to get the * @returns {AbstractRecurringComponent|null} */ getClosestRecurrence(closeTo) { const iterator = this.vcalendar.getVObjectIterator() const firstVObject = iterator.next().value if (!firstVObject) { return null } const d = DateTimeValue.fromJSDate(closeTo, true) return firstVObject.recurrenceManager.getClosestOccurrence(d) } /** * Get recurrence-item at exactly a given recurrence-Id * Loading
src/router.js +2 −2 Original line number Diff line number Diff line Loading @@ -94,11 +94,11 @@ const router = new Router({ }, { path: '/edit/:object', redirect: '/${getInitialView()}/now/edit/sidebar/:object/next' redirect: `/${getInitialView()}/now/edit/sidebar/:object/next`, }, { path: '/edit/:object/:recurrenceId', redirect: '/${getInitialView()}/now/edit/sidebar/:object/:recurrenceId' redirect: `/${getInitialView()}/now/edit/sidebar/:object/:recurrenceId`, }, /** * This is the main route that contains the current view and viewed day Loading