Skip to content

Commit

Permalink
Merge pull request #3864 from nextcloud/enh/144/inline-invitation-res…
Browse files Browse the repository at this point in the history
…ponse

Accept and decline invitations
  • Loading branch information
st3iny authored Jan 28, 2022
2 parents d771339 + 58c4df7 commit 4134f5c
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
180 changes: 180 additions & 0 deletions src/components/Editor/InvitationResponseButtons.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<!--
- @copyright Copyright (c) 2021 Richard Steinmetz <richard@steinmetz.cloud>
-
- @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->

<template>
<div
class="invitation-response-buttons"
:class="{ 'invitation-response-buttons--narrow': narrow }">
<button
v-if="!isAccepted"
class="invitation-response-buttons__button primary"
:disabled="loading"
@click="accept">
{{ t('calendar', 'Accept') }}
</button>
<button
v-if="!isDeclined"
class="invitation-response-buttons__button error"
:disabled="loading"
@click="decline">
{{ t('calendar', 'Decline') }}
</button>
<template v-if="!isTentative">
<button
v-if="!narrow"
class="invitation-response-buttons__button"
:disabled="loading"
@click="tentative">
{{ t('calendar', 'Tentative') }}
</button>
<Actions v-else>
<ActionButton
:disabled="loading"
@click="tentative">
<template #icon>
<CalendarQuestionIcon :size="20" />
</template>
{{ t('calendar', 'Tentative') }}
</ActionButton>
</Actions>
</template>
</div>
</template>

<script>
import Actions from '@nextcloud/vue/dist/Components/Actions'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import CalendarQuestionIcon from 'vue-material-design-icons/CalendarQuestion.vue'
import { showError, showSuccess } from '@nextcloud/dialogs'
import logger from '../../utils/logger'
export default {
name: 'InvitationResponseButtons',
components: {
Actions,
ActionButton,
CalendarQuestionIcon,
},
props: {
attendee: {
type: Object,
required: true,
},
calendarId: {
type: String,
required: true,
},
narrow: {
type: Boolean,
default: false,
},
},
data() {
return {
loading: false,
}
},
computed: {
isAccepted() {
return this.attendee.participationStatus === 'ACCEPTED'
},
isDeclined() {
return this.attendee.participationStatus === 'DECLINED'
},
isTentative() {
return this.attendee.participationStatus === 'TENTATIVE'
},
},
methods: {
async accept() {
try {
await this.setParticipationStatus('ACCEPTED')
showSuccess(this.t('calendar', 'The invitation has been accepted successfully.'))
this.$emit('close')
} catch (e) {
showError(this.t('calendar', 'Failed to accept the invitation.'))
}
},
async decline() {
try {
await this.setParticipationStatus('DECLINED')
showSuccess(this.t('calendar', 'The invitation has been declined successfully.'))
this.$emit('close')
} catch (e) {
showError(this.t('calendar', 'Failed to decline the invitation.'))
}
},
async tentative() {
try {
await this.setParticipationStatus('TENTATIVE')
showSuccess(this.t('calendar', 'Your participation has been marked as tentative.'))
this.$emit('close')
} catch (e) {
showError(this.t('calendar', 'Failed to set the participation status to tentative.'))
}
},
/**
* Set the participation status and save the event
*
* @param {string} participationStatus The new participation status
* @return {Promise<void>}
*/
async setParticipationStatus(participationStatus) {
this.loading = true
try {
this.$store.commit('changeAttendeesParticipationStatus', {
attendee: this.attendee,
participationStatus,
})
// TODO: What about recurring events? Add new buttons like "Accept this and all future"?
// Currently, this will only accept a single occurrence.
await this.$store.dispatch('saveCalendarObjectInstance', {
thisAndAllFuture: false,
calendarId: this.calendarId,
})
} catch (error) {
logger.error('Failed to set participation status', { error, participationStatus })
throw error
} finally {
this.loading = false
}
},
},
}
</script>

<style lang="scss" scoped>
.invitation-response-buttons {
display: flex;
width: 100%;
&__button {
flex: 1 auto;
width: 100%;
}
// Fix alignment of buttons on simple editor
&--narrow > button + button {
margin-left: 5px;
}
}
</style>
38 changes: 38 additions & 0 deletions src/mixins/EditorMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @copyright Copyright (c) 2019 Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license AGPL-3.0-or-later
*
Expand All @@ -19,6 +20,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

import { getRFCProperties } from '../models/rfcProps'
import logger from '../utils/logger.js'
import { getIllustrationForTitle } from '../utils/illustration.js'
Expand All @@ -30,6 +32,7 @@ import {
mapState,
} from 'vuex'
import { translate as t } from '@nextcloud/l10n'
import { removeMailtoPrefix } from '../utils/attendee'

/**
* This is a mixin for the editor. It contains common Vue stuff, that is
Expand Down Expand Up @@ -210,6 +213,33 @@ export default {

return calendar.readOnly
},
/**
* Returns whether the user is an attendee of the event
*
* @return {boolean}
*/
isViewedByAttendee() {
return this.userAsAttendee !== null
},
/**
* Returns the attendee property corresponding to the current user
*
* @return {?object}
*/
userAsAttendee() {
if (!this.calendarObjectInstance.organizer) {
return null
}

const principal = removeMailtoPrefix(this.$store.getters.getCurrentUserPrincipalEmail)
for (const attendee of this.calendarObjectInstance.attendees) {
if (removeMailtoPrefix(attendee.uri) === principal) {
return attendee
}
}

return null
},
/**
* Returns all calendars selectable by the user
*
Expand Down Expand Up @@ -370,6 +400,14 @@ export default {
})
this.$store.commit('resetCalendarObjectInstanceObjectIdAndRecurrenceId')
},
/**
* Closes the editor and returns to normal calendar-view without running any action.
* This is useful if the calendar-object-instance has already been saved.
*/
closeEditorAndSkipAction() {
this.requiresActionOnRouteLeave = false
this.closeEditor()
},
/**
* Resets the calendar-object back to it's original state and closes the editor
*/
Expand Down
9 changes: 9 additions & 0 deletions src/views/EditSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@
@update-end-date="updateEndDate"
@update-end-timezone="updateEndTimezone"
@toggle-all-day="toggleAllDay" />

<InvitationResponseButtons
v-if="isViewedByAttendee && userAsAttendee"
:attendee="userAsAttendee"
:calendar-id="calendarId"
:narrow="true"
@close="closeEditorAndSkipAction" />
</template>

<AppSidebarTab
Expand Down Expand Up @@ -261,6 +268,7 @@ import SaveButtons from '../components/Editor/SaveButtons.vue'
import PropertySelectMultiple from '../components/Editor/Properties/PropertySelectMultiple.vue'
import PropertyColor from '../components/Editor/Properties/PropertyColor.vue'
import ResourceList from '../components/Editor/Resources/ResourceList'
import InvitationResponseButtons from '../components/Editor/InvitationResponseButtons'
import AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'
import CalendarBlank from 'vue-material-design-icons/CalendarBlank.vue'
Expand Down Expand Up @@ -295,6 +303,7 @@ export default {
Download,
InformationOutline,
MapMarker,
InvitationResponseButtons,
},
mixins: [
EditorMixin,
Expand Down
8 changes: 8 additions & 0 deletions src/views/EditSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@
:value="description"
@update:value="updateDescription" />

<InvitationResponseButtons
v-if="isViewedByAttendee && userAsAttendee"
:attendee="userAsAttendee"
:calendar-id="calendarId"
@close="closeEditorAndSkipAction" />

<SaveButtons
v-if="!isReadOnly"
class="event-popover__buttons"
Expand Down Expand Up @@ -176,6 +182,7 @@ import PropertyText from '../components/Editor/Properties/PropertyText.vue'
import SaveButtons from '../components/Editor/SaveButtons.vue'
import PopoverLoadingIndicator from '../components/Popover/PopoverLoadingIndicator.vue'
import { getPrefixedRoute } from '../utils/router.js'
import InvitationResponseButtons from '../components/Editor/InvitationResponseButtons'
import ArrowExpand from 'vue-material-design-icons/ArrowExpand.vue'
import CalendarBlank from 'vue-material-design-icons/CalendarBlank.vue'
Expand Down Expand Up @@ -203,6 +210,7 @@ export default {
Close,
Download,
Delete,
InvitationResponseButtons,
},
mixins: [
EditorMixin,
Expand Down

0 comments on commit 4134f5c

Please sign in to comment.