Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Editor/InvitationResponseButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default {
// TODO: What about recurring events? Add new buttons like "Accept this and all future"?
// Currently, this will only accept a single occurrence.
await this.calendarObjectInstanceStore.saveCalendarObjectInstance({
thisAndAllFuture: false,
mode: 'all',
calendarId: this.calendarId,
})
} catch (error) {
Expand Down
33 changes: 25 additions & 8 deletions src/components/Editor/SaveButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,49 @@
</template>
{{ $t('calendar', 'Update') }}
</NcButton>
<NcButton
v-if="showUpdateOnlyThisButton && !showUpdateThisAndFutureButton"
variant="primary"
:disabled="disabled"
@click="saveThisOnly">
{{ $t('calendar', 'Update this occurrence') }}
</NcButton>
<NcButton
v-if="showUpdateThisAndFutureButton && !showUpdateOnlyThisButton"
:type="primary"
:disabled="disabled"
@click="saveThisAndAllFuture">
{{ $t('calendar', 'Update this and all future') }}
{{ $t('calendar', 'Update this and future occurrences') }}
</NcButton>
<NcButton
v-if="showUpdateOnlyThisButton && !showUpdateThisAndFutureButton"
variant="primary"
v-if="showUpdateThisAndFutureButton && !showUpdateOnlyThisButton"
:type="primary"
:disabled="disabled"
@click="saveThisOnly">
{{ $t('calendar', 'Update this occurrence') }}
@click="saveSeries">
{{ $t('calendar', 'Update all occurrences') }}
</NcButton>

<NcActions v-if="showUpdateThisAndFutureButton && showUpdateOnlyThisButton" :primary="true" :menu-name="t('calendar', 'Update')">
<template #icon>
<CheckIcon :size="20" />
</template>
<NcActionButton @click="saveThisOnly">
<template #icon>
<CheckIcon :size="20" />
</template>
{{ $t('calendar', 'Update this occurrence') }}
</NcActionButton>
<NcActionButton @click="saveThisAndAllFuture">
<template #icon>
<CheckIcon :size="20" />
</template>
{{ $t('calendar', 'Update this and all future') }}
{{ $t('calendar', 'Update this and future occurrences') }}
</NcActionButton>
<NcActionButton @click="saveThisOnly">
<NcActionButton @click="saveSeries">
<template #icon>
<CheckIcon :size="20" />
</template>
{{ $t('calendar', 'Update this occurrence') }}
{{ $t('calendar', 'Update all occurrences') }}
</NcActionButton>
</NcActions>

Expand Down Expand Up @@ -147,6 +160,10 @@ export default {
this.$emit('save-this-and-all-future')
},

saveSeries() {
this.$emit('save-series')
},

showMore() {
this.$emit('show-more')
},
Expand Down
30 changes: 15 additions & 15 deletions src/mixins/EditorMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,12 @@ export default {
},
keyboardSaveEvent(event) {
if (event.key === 'Enter' && event.ctrlKey === true && !this.isReadOnly && !this.canCreateRecurrenceException) {
this.saveAndLeave(false)
this.saveAndLeave('single')
}
},
keyboardDeleteEvent(event) {
if (event.key === 'Delete' && event.ctrlKey === true && this.canDelete && !this.canCreateRecurrenceException) {
this.deleteAndLeave(false)
this.deleteAndLeave('single')
}
},
keyboardDuplicateEvent(event) {
Expand All @@ -496,10 +496,10 @@ export default {
/**
* Saves a calendar-object
*
* @param {boolean} thisAndAllFuture Whether to modify only this or this and all future occurrences
* @param {string} mode Modification mode: 'all', 'future', or 'single'
* @return {Promise<void>}
*/
async save(thisAndAllFuture = false) {
async save(mode = 'single') {
if (!this.calendarObject) {
logger.error('Calendar-object not found')
return
Expand All @@ -508,14 +508,14 @@ export default {
return
}
if (this.forceThisAndAllFuture) {
thisAndAllFuture = true
mode = 'future'
}

this.isLoading = true
this.isSaving = true
try {
await this.calendarObjectInstanceStore.saveCalendarObjectInstance({
thisAndAllFuture,
mode,
calendarId: this.calendarId,
})
} catch (error) {
Expand All @@ -534,11 +534,11 @@ export default {
/**
* Saves a calendar-object and closes the editor
*
* @param {boolean} thisAndAllFuture Whether to modify only this or this and all future occurrences
* @param {string} mode Modification mode: 'all', 'future', or 'single'
* @return {Promise<void>}
*/
async saveAndLeave(thisAndAllFuture = false) {
await this.save(thisAndAllFuture)
async saveAndLeave(mode = 'single') {
await this.save(mode)
this.requiresActionOnRouteLeave = false
this.closeEditor()
},
Expand All @@ -555,10 +555,10 @@ export default {
/**
* Deletes a calendar-object
*
* @param {boolean} thisAndAllFuture Whether to delete only this or this and all future occurrences
* @param {string} mode Deletion mode: 'all', 'future', or 'single'
* @return {Promise<void>}
*/
async delete(thisAndAllFuture = false) {
async delete(mode = 'single') {
if (!this.calendarObject) {
logger.error('Calendar-object not found')
return
Expand All @@ -568,17 +568,17 @@ export default {
}

this.isLoading = true
await this.calendarObjectInstanceStore.deleteCalendarObjectInstance({ thisAndAllFuture })
await this.calendarObjectInstanceStore.deleteCalendarObjectInstance({ mode })
this.isLoading = false
},
/**
* Deletes a calendar-object and closes the editor
*
* @param {boolean} thisAndAllFuture Whether to delete only this or this and all future occurrences
* @param {string} mode Deletion mode: 'all', 'future', or 'single'
* @return {Promise<void>}
*/
async deleteAndLeave(thisAndAllFuture = false) {
await this.delete(thisAndAllFuture)
async deleteAndLeave(mode = 'single') {
await this.delete(mode)
this.requiresActionOnRouteLeave = false
this.closeEditor()
},
Expand Down
66 changes: 54 additions & 12 deletions src/store/calendarObjectInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -1456,32 +1456,69 @@
* Saves changes made to a single calendar-object-instance
*
* @param {object} data The destructuring object
* @param {boolean} data.thisAndAllFuture Whether or not to save changes for all future occurrences or just this one
* @param {string} data.mode Modification mode: 'all', 'future', or 'single'
* @param {string} data.calendarId The new calendar-id to store it in
* @return {Promise<void>}
*/
async saveCalendarObjectInstance({
thisAndAllFuture,
mode,
calendarId,
}) {
const calendarObjectsStore = useCalendarObjectsStore()

const eventComponent = this.calendarObjectInstance.eventComponent
const calendarObject = this.calendarObject
const isForkedItem = eventComponent.primaryItem !== null

updateAlarms(eventComponent)
await updateTalkParticipants(eventComponent)

if (eventComponent.isDirty()) {
const isForkedItem = eventComponent.primaryItem !== null
if (eventComponent.isDirty() && eventComponent.isRecurring() && mode === 'all' && isForkedItem) {
// Find the master component (without RECURRENCE-ID)
let masterComponent = null
for (const component of calendarObject.calendarComponent.getComponentIterator()) {
if (component.name === eventComponent.name && !component.hasProperty('RECURRENCE-ID')) {
masterComponent = component
break
}
}

Check failure on line 1485 in src/store/calendarObjectInstance.js

View workflow job for this annotation

GitHub Actions / NPM lint

Trailing spaces not allowed
if (masterComponent) {
// construct list of properties to clone
const propertyNames = []
for (const property of masterComponent.getPropertyIterator()) {
if (property.name === 'UID' || property.name === 'RECURRENCE-ID' || property.name === 'DTSTART' || property.name === 'DTEND') {
continue
}
propertyNames.push(property.name)
masterComponent.deleteAllProperties(property.name)
}
// clone properties from eventComponent
for (const property of eventComponent.getPropertyIterator()) {
if (propertyNames.indexOf(property.name) === -1) {
continue
}
masterComponent.addProperty(property.clone())
}
// clone alarms
masterComponent.deleteAllComponents('VALARM')
for (const alarm of eventComponent.getAlarmIterator()) {
masterComponent.addComponent(alarm.clone())
}
}

Check failure on line 1509 in src/store/calendarObjectInstance.js

View workflow job for this annotation

GitHub Actions / NPM lint

Trailing spaces not allowed
await calendarObjectsStore.updateCalendarObject({ calendarObject })
}

if (eventComponent.isDirty() && mode !== 'all') {
let original = null
let fork = null

// We check if two things apply:
// - primaryItem !== null -> Is this a fork or not?
// - eventComponent.canCreateRecurrenceExceptions() - Can we create a recurrence-exception for this item
if (isForkedItem && eventComponent.canCreateRecurrenceExceptions()) {
[original, fork] = eventComponent.createRecurrenceException(thisAndAllFuture)
[original, fork] = eventComponent.createRecurrenceException(mode === 'future')
}

await calendarObjectsStore.updateCalendarObject({ calendarObject })
Expand Down Expand Up @@ -1534,20 +1571,25 @@
* Deletes a calendar-object-instance
*
* @param {object} data The destructuring object
* @param {boolean} data.thisAndAllFuture Whether or not to delete all future occurrences or just this one
* @param {string} data.mode Deletion mode: 'all', 'future', or 'single'
* @return {Promise<void>}
*/
async deleteCalendarObjectInstance({ thisAndAllFuture }) {
async deleteCalendarObjectInstance({ mode }) {
const calendarObjectsStore = useCalendarObjectsStore()

const eventComponent = this.calendarObjectInstance.eventComponent
const isRecurrenceSetEmpty = eventComponent.removeThisOccurrence(thisAndAllFuture)
const calendarObject = this.calendarObject

// Singleton event or deleting all occurrences - delete the whole calendar-object
if (!eventComponent.isRecurring() || mode === 'all') {
await calendarObjectsStore.deleteCalendarObject({ calendarObject: this.calendarObject })
return
}

// Recurring event - remove this occurrence or this and all future
const isRecurrenceSetEmpty = eventComponent.removeThisOccurrence(mode === 'future')
if (isRecurrenceSetEmpty) {
await calendarObjectsStore.deleteCalendarObject({ calendarObject })
await calendarObjectsStore.deleteCalendarObject({ calendarObject: this.calendarObject })
} else {
await calendarObjectsStore.updateCalendarObject({ calendarObject })
await calendarObjectsStore.updateCalendarObject({ calendarObject: this.calendarObject })
}
},

Expand Down
36 changes: 22 additions & 14 deletions src/views/EditFull.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@
:is-new="isNew"
:is-read-only="isReadOnly"
:force-this-and-all-future="forceThisAndAllFuture"
@save-this-only="prepareAccessForAttachments(false)"
@save-this-and-all-future="prepareAccessForAttachments(true)" />
@save-this-only="prepareAccessForAttachments('single')"
@save-this-and-all-future="prepareAccessForAttachments('future')"
@save-series="prepareAccessForAttachments('all')"

Check failure on line 59 in src/views/EditFull.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Expected no line breaks before closing bracket, but 1 line break found
/>
<div class="app-full__actions__inner" :class="[{ 'app-full__actions__inner__readonly': isReadOnly }]">
<NcActions>
<NcActionLink v-if="!hideEventExport && hasDownloadURL && !isNew" :href="downloadURL">
Expand All @@ -70,23 +72,29 @@
</template>
{{ $t('calendar', 'Duplicate') }}
</NcActionButton>
<NcActionButton v-if="canDelete && !canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave(false)">
<NcActionButton v-if="canDelete && !canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave('single')">
<template #icon>
<Delete :size="20" decorative />
</template>
{{ $t('calendar', 'Delete') }}
</NcActionButton>
<NcActionButton v-if="canDelete && canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave(false)">
<NcActionButton v-if="canDelete && canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave('single')">
<template #icon>
<Delete :size="20" decorative />
</template>
{{ $t('calendar', 'Delete this occurrence') }}
</NcActionButton>
<NcActionButton v-if="canDelete && canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave(true)">
<NcActionButton v-if="canDelete && canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave('future')">
<template #icon>
<Delete :size="20" decorative />
</template>
{{ $t('calendar', 'Delete this and all future') }}
{{ $t('calendar', 'Delete this and future occurrences') }}
</NcActionButton>
<NcActionButton v-if="canDelete && canCreateRecurrenceException && !isNew" type="tertiary" @click="deleteAndLeave('all')">
<template #icon>
<Delete :size="20" decorative />
</template>
{{ $t('calendar', 'Delete this and future occurrences') }}
</NcActionButton>
</NcActions>
</div>
Expand Down Expand Up @@ -291,7 +299,7 @@
<NcButton
variant="primary"
:disabled="showPreloader"
@click="acceptAttachmentsModal(thisAndAllFuture)">
@click="acceptAttachmentsModal()">
{{ t('calendar', 'Invite') }}
</NcButton>
</div>
Expand Down Expand Up @@ -421,7 +429,7 @@

data() {
return {
thisAndAllFuture: false,
saveMode: 'single',
doNotShare: false,
showModal: false,
showModalNewAttachments: [],
Expand Down Expand Up @@ -707,7 +715,7 @@
this.showModal = false
this.showModalNewAttachments = []
this.showModalUsers = []
this.saveEvent(this.thisAndAllFuture)
this.saveEvent(this.saveMode)
}, 500)
// trigger save event after make each attachment access
// 1) if !isPrivate get attachments NOT SHARED and SharedType is empry -> API ADD SHARE
Expand All @@ -731,8 +739,8 @@
return name.split('/').pop()
},

prepareAccessForAttachments(thisAndAllFuture = false) {
this.thisAndAllFuture = thisAndAllFuture
prepareAccessForAttachments(mode = false) {
this.saveMode = mode
const newAttachments = this.calendarObjectInstance.attachments.filter((attachment) => {
// get only new attachments
// TODO get NOT only new attachments =) Maybe we should filter all attachments without share-type, 'cause event can be private and AFTER save owner could add new participant
Expand All @@ -752,14 +760,14 @@
return false
})
} else {
this.saveEvent(thisAndAllFuture)
this.saveEvent(this.saveMode)
}
},

saveEvent(thisAndAllFuture = false) {
saveEvent(mode = 'single') {
// if there is new attachments and !private, then make modal with users and files/
// maybe check shared access before add file
this.saveAndLeave(thisAndAllFuture)
this.saveAndLeave(mode)
this.calendarObjectInstance.attachments = this.calendarObjectInstance.attachments.map((attachment) => {
if (attachment.isNew) {
delete attachment.isNew
Expand Down
Loading
Loading