diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2021-11-19 18:45:30 +0300 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2022-02-17 11:38:42 +0300 |
commit | daf1b5f6a3b35279b4a23046ba51690920f62fec (patch) | |
tree | f8920304ef3f4b6486334ab0f791fe668e9c0397 /apps/dav/src | |
parent | 5ee0fb3acb918d16c78addaa54d3a581f9e4f982 (diff) |
Replace CalDAV availability component with component lib
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'apps/dav/src')
-rw-r--r-- | apps/dav/src/service/CalendarService.js | 117 | ||||
-rw-r--r-- | apps/dav/src/views/Availability.vue | 140 |
2 files changed, 37 insertions, 220 deletions
diff --git a/apps/dav/src/service/CalendarService.js b/apps/dav/src/service/CalendarService.js index 10d3e252465..2b416d6b670 100644 --- a/apps/dav/src/service/CalendarService.js +++ b/apps/dav/src/service/CalendarService.js @@ -19,11 +19,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import { getClient } from '../dav/client' -import ICAL from 'ical.js' import logger from './logger' import { parseXML } from 'webdav/dist/node/tools/dav' -import { getZoneString } from 'icalzone' -import { v4 as uuidv4 } from 'uuid' + +import { + slotsToVavailability, + vavailabilityToSlots, +} from '@nextcloud/calendar-availability-vue' /** * @@ -67,44 +69,7 @@ export async function findScheduleInboxAvailability() { return undefined } - const parsedIcal = ICAL.parse(availability) - - const vcalendarComp = new ICAL.Component(parsedIcal) - const vavailabilityComp = vcalendarComp.getFirstSubcomponent('vavailability') - - let timezoneId - const timezoneComp = vcalendarComp.getFirstSubcomponent('vtimezone') - if (timezoneComp) { - timezoneId = timezoneComp.getFirstProperty('tzid').getFirstValue() - } - - const availableComps = vavailabilityComp.getAllSubcomponents('available') - // Combine all AVAILABLE blocks into a week of slots - const slots = getEmptySlots() - availableComps.forEach((availableComp) => { - const start = availableComp.getFirstProperty('dtstart').getFirstValue().toJSDate() - const end = availableComp.getFirstProperty('dtend').getFirstValue().toJSDate() - const rrule = availableComp.getFirstProperty('rrule') - - if (rrule.getFirstValue().freq !== 'WEEKLY') { - logger.warn('rrule not supported', { - rrule: rrule.toICALString(), - }) - return - } - - rrule.getFirstValue().getComponent('BYDAY').forEach(day => { - slots[day].push({ - start, - end, - }) - }) - }) - - return { - slots, - timezoneId, - } + return vavailabilityToSlots(availability) } /** @@ -117,74 +82,10 @@ export async function saveScheduleInboxAvailability(slots, timezoneId) { day: dayId, })))] - const vcalendarComp = new ICAL.Component('vcalendar') - vcalendarComp.addPropertyWithValue('prodid', 'Nextcloud DAV app') - - // Store time zone info - // If possible we use the info from a time zone database - const predefinedTimezoneIcal = getZoneString(timezoneId) - if (predefinedTimezoneIcal) { - const timezoneComp = new ICAL.Component(ICAL.parse(predefinedTimezoneIcal)) - vcalendarComp.addSubcomponent(timezoneComp) - } else { - // Fall back to a simple markup - const timezoneComp = new ICAL.Component('vtimezone') - timezoneComp.addPropertyWithValue('tzid', timezoneId) - vcalendarComp.addSubcomponent(timezoneComp) - } - - // Store availability info - const vavailabilityComp = new ICAL.Component('vavailability') - - // Deduplicate by start and end time - const deduplicated = all.reduce((acc, slot) => { - const key = [ - slot.start.getHours(), - slot.start.getMinutes(), - slot.end.getHours(), - slot.end.getMinutes(), - ].join('-') - - return { - ...acc, - [key]: [...(acc[key] ?? []), slot], - } - }, {}) - - // Create an AVAILABILITY component for every recurring slot - Object.keys(deduplicated).map(key => { - const slots = deduplicated[key] - const start = slots[0].start - const end = slots[0].end - // Combine days but make them also unique - const days = slots.map(slot => slot.day).filter((day, index, self) => self.indexOf(day) === index) - - const availableComp = new ICAL.Component('available') - - // Define DTSTART and DTEND - const startTimeProp = availableComp.addPropertyWithValue('dtstart', ICAL.Time.fromJSDate(start, false)) - startTimeProp.setParameter('tzid', timezoneId) - const endTimeProp = availableComp.addPropertyWithValue('dtend', ICAL.Time.fromJSDate(end, false)) - endTimeProp.setParameter('tzid', timezoneId) - - // Add mandatory UID - availableComp.addPropertyWithValue('uid', uuidv4()) - - // TODO: add optional summary - - // Define RRULE - availableComp.addPropertyWithValue('rrule', { - freq: 'WEEKLY', - byday: days, - }) - - return availableComp - }).map(vavailabilityComp.addSubcomponent.bind(vavailabilityComp)) + const vavailability = slotsToVavailability(all, timezoneId) - vcalendarComp.addSubcomponent(vavailabilityComp) logger.debug('New availability ical created', { - asObject: vcalendarComp, - asString: vcalendarComp.toString(), + vavailability, }) const client = getClient('calendars') @@ -194,7 +95,7 @@ export async function saveScheduleInboxAvailability(slots, timezoneId) { <x0:propertyupdate xmlns:x0="DAV:"> <x0:set> <x0:prop> - <x1:calendar-availability xmlns:x1="urn:ietf:params:xml:ns:caldav">${vcalendarComp.toString()}</x1:calendar-availability> + <x1:calendar-availability xmlns:x1="urn:ietf:params:xml:ns:caldav">${vavailability}</x1:calendar-availability> </x0:prop> </x0:set> </x0:propertyupdate>`, diff --git a/apps/dav/src/views/Availability.vue b/apps/dav/src/views/Availability.vue index 62915810042..bba35f35b1f 100644 --- a/apps/dav/src/views/Availability.vue +++ b/apps/dav/src/views/Availability.vue @@ -12,45 +12,19 @@ <TimezonePicker v-model="timezone" /> </span> </div> - <div class="grid-table"> - <template v-for="day in daysOfTheWeek"> - <div :key="`day-label-${day.id}`" class="label-weekday"> - {{ day.displayName }} - </div> - <div :key="`day-slots-${day.id}`" class="availability-slots"> - <div class="availability-slot-group"> - <template v-for="(slot, idx) in day.slots"> - <div :key="`slot-${day.id}-${idx}`" class="availability-slot"> - <DatetimePicker v-model="slot.start" - type="time" - class="start-date" - format="H:mm" /> - <span class="to-text"> - {{ $t('dav', 'to') }} - </span> - <DatetimePicker v-model="slot.end" - type="time" - class="end-date" - format="H:mm" /> - <button :key="`slot-${day.id}-${idx}-btn`" - class="icon-delete delete-slot button" - :title="$t('dav', 'Delete slot')" - @click="deleteSlot(day, idx)" /> - </div> - </template> - </div> - <span v-if="day.slots.length === 0" - class="empty-content"> - {{ $t('dav', 'No working hours set') }} - </span> - </div> - <button :key="`add-slot-${day.id}`" - :disabled="loading" - class="icon-add add-another button" - :title="$t('dav', 'Add slot')" - @click="addSlot(day)" /> - </template> - </div> + <CalendarAvailability :slots.sync="slots" + :loading="loading" + :l10n-to="$t('dav', 'to')" + :l10n-delete-slot="$t('dav', 'Delete slot')" + :l10n-empty-day="$t('dav', 'No working hours set')" + :l10n-add-slot="$t('dav', 'Add slot')" + :l10n-monday="$t('dav', 'Monday')" + :l10n-tuesday="$t('dav', 'Tuesday')" + :l10n-wednesday="$t('dav', 'Wednesday')" + :l10n-thursday="$t('dav', 'Thursday')" + :l10n-friday="$t('dav', 'Friday')" + :l10n-saturday="$t('dav', 'Saturday')" + :l10n-sunday="$t('dav', 'Sunday')" /> <button :disabled="loading || saving" class="button primary" @click="save"> @@ -60,19 +34,18 @@ </template> <script> -import DatetimePicker from '@nextcloud/vue/dist/Components/DatetimePicker' +import { CalendarAvailability } from '@nextcloud/calendar-availability-vue' import { findScheduleInboxAvailability, getEmptySlots, saveScheduleInboxAvailability, } from '../service/CalendarService' -import { getFirstDay } from '@nextcloud/l10n' import jstz from 'jstimezonedetect' import TimezonePicker from '@nextcloud/vue/dist/Components/TimezonePicker' export default { name: 'Availability', components: { - DatetimePicker, + CalendarAvailability, TimezonePicker, }, data() { @@ -80,63 +53,27 @@ export default { const defaultTimezone = jstz.determine() const defaultTimezoneId = defaultTimezone ? defaultTimezone.name() : 'UTC' - const moToSa = [ - { - id: 'MO', - displayName: this.$t('dav', 'Monday'), - slots: [], - }, - { - id: 'TU', - displayName: this.$t('dav', 'Tuesday'), - slots: [], - }, - { - id: 'WE', - displayName: this.$t('dav', 'Wednesday'), - slots: [], - }, - { - id: 'TH', - displayName: this.$t('dav', 'Thursday'), - slots: [], - }, - { - id: 'FR', - displayName: this.$t('dav', 'Friday'), - slots: [], - }, - { - id: 'SA', - displayName: this.$t('dav', 'Saturday'), - slots: [], - }, - ] - const sunday = { - id: 'SU', - displayName: this.$t('dav', 'Sunday'), - slots: [], - } - const daysOfTheWeek = getFirstDay() === 1 ? [...moToSa, sunday] : [sunday, ...moToSa] return { loading: true, saving: false, timezone: defaultTimezoneId, - daysOfTheWeek, + slots: getEmptySlots(), } }, async mounted() { try { - const { slots, timezoneId } = await findScheduleInboxAvailability() - if (slots) { - this.daysOfTheWeek.forEach(day => { - day.slots.push(...slots[day.id]) - }) - } - if (timezoneId) { - this.timezone = timezoneId + const slotData = await findScheduleInboxAvailability() + if (!slotData) { + console.info('no availability is set') + this.slots = getEmptySlots() + } else { + const { slots, timezoneId } = slotData + this.slots = slots + if (timezoneId) { + this.timezone = timezoneId + } + console.info('availability loaded', this.slots, this.timezoneId) } - console.info('availability loaded', this.daysOfTheWeek) } catch (e) { console.error('could not load existing availability', e) @@ -146,32 +83,11 @@ export default { } }, methods: { - addSlot(day) { - const start = new Date() - start.setHours(9) - start.setMinutes(0) - start.setSeconds(0) - const end = new Date() - end.setHours(17) - end.setMinutes(0) - end.setSeconds(0) - day.slots.push({ - start, - end, - }) - }, - deleteSlot(day, idx) { - day.slots.splice(idx, 1) - }, async save() { try { this.saving = true - const slots = getEmptySlots() - this.daysOfTheWeek.forEach(day => { - day.slots.forEach(slot => slots[day.id].push(slot)) - }) - await saveScheduleInboxAvailability(slots, this.timezone) + await saveScheduleInboxAvailability(this.slots, this.timezone) // TODO: show a nice toast } catch (e) { |