mirror of https://github.com/nextcloud/calendar
feat: add a setting for the default calendar url
Co-authored-by: Lukas Boersma <mail@lukas-boersma.com> Co-authored-by: szaimen <szaimen@e.mail.de> Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
This commit is contained in:
parent
fd85b52787
commit
7a307bbcf0
|
@ -73,8 +73,9 @@
|
|||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&--timezone {
|
||||
|
||||
&--timezone,
|
||||
&--default-calendar {
|
||||
width: 100%;
|
||||
|
||||
.multiselect {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<AppNavigationSettings exclude-click-outside-classes="import-modal"
|
||||
<AppNavigationSettings :exclude-click-outside-selectors="['.vs__dropdown-menu', '.modal-wrapper']"
|
||||
:name="settingsTitle">
|
||||
<ul class="settings-fieldset-interior">
|
||||
<SettingsImportSection :is-disabled="loadingCalendars" />
|
||||
|
@ -71,6 +71,18 @@
|
|||
label="label"
|
||||
@option:selected="changeSlotDuration" />
|
||||
</li>
|
||||
<!-- TODO: remove version check once Nextcloud 28 is not supported anymore -->
|
||||
<li v-if="currentUserPrincipal && defaultCalendarOptions.length > 1 && nextcloudVersion >= 29"
|
||||
class="settings-fieldset-interior-item settings-fieldset-interior-item--default-calendar">
|
||||
<label :for="defaultCalendarPickerId">
|
||||
{{ $t('calendar', 'Default calendar for invitations and new events') }}
|
||||
</label>
|
||||
<CalendarPicker :value="defaultCalendar"
|
||||
:calendars="defaultCalendarOptions"
|
||||
:disabled="savingDefaultCalendarId"
|
||||
:input-id="defaultCalendarPickerId"
|
||||
@select-calendar="changeDefaultCalendar" />
|
||||
</li>
|
||||
<li class="settings-fieldset-interior-item settings-fieldset-interior-item--defaultReminder">
|
||||
<label for="defaultReminder">{{ $t('calendar', 'Default reminder') }}</label>
|
||||
<NcSelect :id="defaultReminder"
|
||||
|
@ -124,6 +136,8 @@ import {
|
|||
NcAppNavigationSettings as AppNavigationSettings,
|
||||
NcSelect,
|
||||
} from '@nextcloud/vue'
|
||||
import CalendarPicker from '../Shared/CalendarPicker.vue'
|
||||
|
||||
import {
|
||||
generateRemoteUrl,
|
||||
generateUrl,
|
||||
|
@ -156,6 +170,9 @@ import ClipboardArrowLeftOutline from 'vue-material-design-icons/ClipboardArrowL
|
|||
import InformationVariant from 'vue-material-design-icons/InformationVariant.vue'
|
||||
import OpenInNewIcon from 'vue-material-design-icons/OpenInNew.vue'
|
||||
|
||||
import logger from '../../utils/logger.js'
|
||||
import { randomId } from '../../utils/randomId.js'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
|
@ -171,6 +188,7 @@ export default {
|
|||
ClipboardArrowLeftOutline,
|
||||
InformationVariant,
|
||||
OpenInNewIcon,
|
||||
CalendarPicker,
|
||||
},
|
||||
props: {
|
||||
loadingCalendars: {
|
||||
|
@ -186,14 +204,18 @@ export default {
|
|||
savingPopover: false,
|
||||
savingSlotDuration: false,
|
||||
savingDefaultReminder: false,
|
||||
savingDefaultCalendarId: false,
|
||||
savingWeekend: false,
|
||||
savingWeekNumber: false,
|
||||
savingDefaultCalendar: false,
|
||||
displayKeyboardShortcuts: false,
|
||||
defaultCalendarPickerId: randomId(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
birthdayCalendar: 'hasBirthdayCalendar',
|
||||
currentUserPrincipal: 'getCurrentUserPrincipal',
|
||||
}),
|
||||
...mapState({
|
||||
eventLimit: state => state.settings.eventLimit,
|
||||
|
@ -271,6 +293,28 @@ export default {
|
|||
nextcloudVersion() {
|
||||
return parseInt(OC.config.version.split('.')[0])
|
||||
},
|
||||
defaultCalendarOptions() {
|
||||
return this.$store.state.calendars.calendars
|
||||
.filter(calendar => !calendar.readOnly && !calendar.isSharedWithMe)
|
||||
},
|
||||
/**
|
||||
* The default calendar for new events and inivitations
|
||||
*
|
||||
* @return {object|undefined} The default calendar or undefined if none is available
|
||||
*/
|
||||
defaultCalendar() {
|
||||
const defaultCalendarUrl = this.currentUserPrincipal.scheduleDefaultCalendarUrl
|
||||
const calendar = this.defaultCalendarOptions
|
||||
.find(calendar => calendar.url === defaultCalendarUrl)
|
||||
|
||||
// If the default calendar is not or no longer available,
|
||||
// pick the first calendar in the list of available calendars.
|
||||
if (!calendar) {
|
||||
return this.defaultCalendarOptions[0]
|
||||
}
|
||||
|
||||
return calendar
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async toggleBirthdayEnabled() {
|
||||
|
@ -396,6 +440,34 @@ export default {
|
|||
this.savingDefaultReminder = false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Changes the default calendar for new events
|
||||
*
|
||||
* @param {object} selectedCalendar The new selected default calendar
|
||||
*/
|
||||
async changeDefaultCalendar(selectedCalendar) {
|
||||
if (!selectedCalendar) {
|
||||
return
|
||||
}
|
||||
|
||||
this.savingDefaultCalendar = true
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('changePrincipalScheduleDefaultCalendarUrl', {
|
||||
principal: this.currentUserPrincipal,
|
||||
scheduleDefaultCalendarUrl: selectedCalendar.url,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error while changing default calendar', {
|
||||
error,
|
||||
calendarUrl: selectedCalendar.url,
|
||||
selectedCalendar,
|
||||
})
|
||||
showError(this.$t('calendar', 'Failed to save default calendar'))
|
||||
} finally {
|
||||
this.savingDefaultCalendar = false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copies the primary CalDAV url to the user's clipboard.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<NcSelect label="id"
|
||||
input-id="url"
|
||||
:input-id="inputId"
|
||||
:disabled="isDisabled"
|
||||
:options="options"
|
||||
:value="valueIds"
|
||||
|
@ -25,6 +25,7 @@
|
|||
<script>
|
||||
import { NcSelect } from '@nextcloud/vue'
|
||||
import CalendarPickerOption from './CalendarPickerOption.vue'
|
||||
import { randomId } from '../../utils/randomId.js'
|
||||
|
||||
export default {
|
||||
name: 'CalendarPicker',
|
||||
|
@ -49,12 +50,20 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inputId: {
|
||||
type: String,
|
||||
default: () => randomId(),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isDisabled() {
|
||||
// for pickers where multiple can be selected (zero or more) we don't want to disable the picker
|
||||
// for calendars where only one calendar can be selected, disable if there are < 2
|
||||
return this.multiple ? this.calendars.length < 1 : this.calendars.length < 2
|
||||
return this.disabled || (this.multiple ? this.calendars.length < 1 : this.calendars.length < 2)
|
||||
},
|
||||
valueIds() {
|
||||
if (Array.isArray(this.value)) {
|
||||
|
|
|
@ -65,6 +65,8 @@ const getDefaultPrincipalObject = (props) => Object.assign({}, {
|
|||
isCalendarRoom: false,
|
||||
// The id of the principal without prefix. e.g. userId / groupId / etc.
|
||||
principalId: null,
|
||||
// The url of the default calendar for invitations
|
||||
scheduleDefaultCalendarUrl: null,
|
||||
}, props)
|
||||
|
||||
/**
|
||||
|
@ -80,6 +82,7 @@ const mapDavToPrincipal = (dav) => {
|
|||
const emailAddress = dav.email
|
||||
|
||||
const displayname = dav.displayname
|
||||
const scheduleDefaultCalendarUrl = dav.scheduleDefaultCalendarUrl
|
||||
|
||||
const isUser = dav.principalScheme.startsWith(PRINCIPAL_PREFIX_USER)
|
||||
const isGroup = dav.principalScheme.startsWith(PRINCIPAL_PREFIX_GROUP)
|
||||
|
@ -118,6 +121,7 @@ const mapDavToPrincipal = (dav) => {
|
|||
isCalendarRoom,
|
||||
principalId,
|
||||
userId,
|
||||
scheduleDefaultCalendarUrl,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -352,8 +352,13 @@ const actions = {
|
|||
vObject.undirtify()
|
||||
}
|
||||
|
||||
const firstCalendar = context.getters.sortedCalendars[0].id
|
||||
return Promise.resolve(mapCalendarJsToCalendarObject(calendar, firstCalendar))
|
||||
const defaultCalendarUrl = context.getters.getCurrentUserPrincipal.scheduleDefaultCalendarUrl
|
||||
const defaultCalendar = context.getters.getCalendarByUrl(defaultCalendarUrl)
|
||||
|
||||
return Promise.resolve(mapCalendarJsToCalendarObject(
|
||||
calendar,
|
||||
defaultCalendar?.id ?? context.getters.sortedCalendars[0].id,
|
||||
))
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -522,6 +522,14 @@ const getters = {
|
|||
*/
|
||||
getCalendarById: (state) => (calendarId) => state.calendarsById[calendarId],
|
||||
|
||||
/**
|
||||
* Gets a calendar by its url
|
||||
*
|
||||
* @param {object} state the store data
|
||||
* @return {function({String}): {Object}}
|
||||
*/
|
||||
getCalendarByUrl: (state) => (url) => state.calendars.find((calendar) => calendar.url === url),
|
||||
|
||||
/**
|
||||
* Gets the contact's birthday calendar or null
|
||||
*
|
||||
|
|
|
@ -66,6 +66,22 @@ const mutations = {
|
|||
setCurrentUserPrincipal(state, { principalId }) {
|
||||
state.currentUserPrincipal = principalId
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the schedule-default-calendar-URL of a principal
|
||||
*
|
||||
* @param {object} state The vuex state
|
||||
* @param {object} data The destructuring object
|
||||
* @param {object} data.principal The principal to modify
|
||||
* @param {string} data.scheduleDefaultCalendarUrl The new schedule-default-calendar-URL
|
||||
*/
|
||||
changePrincipalScheduleDefaultCalendarUrl(state, { principal, scheduleDefaultCalendarUrl }) {
|
||||
Vue.set(
|
||||
state.principalsById[principal.id],
|
||||
'scheduleDefaultCalendarUrl',
|
||||
scheduleDefaultCalendarUrl,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
const getters = {
|
||||
|
@ -147,6 +163,25 @@ const actions = {
|
|||
context.commit('setCurrentUserPrincipal', { principalId: principal.id })
|
||||
logger.debug(`Current user principal is ${principal.url}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* Change a principal's schedule-default-calendar-URL
|
||||
*
|
||||
* @param {object} context The vuex context
|
||||
* @param {object} data The destructuring object
|
||||
* @param {object} data.principal The principal to modify
|
||||
* @param {string} data.scheduleDefaultCalendarUrl The new schedule-default-calendar-URL
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async changePrincipalScheduleDefaultCalendarUrl(context, { principal, scheduleDefaultCalendarUrl }) {
|
||||
principal.dav.scheduleDefaultCalendarUrl = scheduleDefaultCalendarUrl
|
||||
|
||||
await principal.dav.update()
|
||||
context.commit('changePrincipalScheduleDefaultCalendarUrl', {
|
||||
principal,
|
||||
scheduleDefaultCalendarUrl,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export default { state, mutations, getters, actions }
|
||||
|
|
|
@ -40,6 +40,7 @@ describe('Test suite: Principal model (models/principal.js)', () => {
|
|||
isCalendarResource: false,
|
||||
isCalendarRoom: false,
|
||||
principalId: null,
|
||||
scheduleDefaultCalendarUrl: null,
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -63,6 +64,7 @@ describe('Test suite: Principal model (models/principal.js)', () => {
|
|||
isCalendarRoom: false,
|
||||
principalId: 'bar',
|
||||
otherProp: 'foo',
|
||||
scheduleDefaultCalendarUrl: null,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue