diff --git a/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue b/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue index 2db52ae5ef86ecf4534aee70ffd7d9939dffe453..a9dc64fdb9bfd5713d68d2ebf926b78ad255ea51 100644 --- a/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue +++ b/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue @@ -6,7 +6,7 @@ offset-x > <v-card min-width="350px" flat> - <v-toolbar :color="selectedEvent.color" dark dense> + <v-toolbar :color="color || selectedEvent.color" dark dense> <v-toolbar-title> <slot name="title" :selected-event="selectedEvent">{{ selectedEvent.name @@ -14,41 +14,42 @@ </v-toolbar-title> <v-spacer></v-spacer> <slot name="badge" :selected-event="selectedEvent"> - <v-chip + <cancelled-calendar-status-chip v-if="selectedEvent.status === 'CANCELLED' && !withoutBadge" - color="error" - label - > - <v-avatar left> - <v-icon>mdi-cancel</v-icon> - </v-avatar> - {{ $t("calendar.cancelled") }} - </v-chip> + /> </slot> </v-toolbar> <slot name="time" :selected-event="selectedEvent"> - <v-card-text v-if="!withoutTime"> - <v-icon left>mdi-calendar-today-outline</v-icon> - <span v-if="selectedEvent.start !== selectedEvent.end"> - {{ $d(selectedEvent.start, "shortDateTime") }} – - {{ $d(selectedEvent.end, "shortDateTime") }} - </span> - <span v-else> {{ $d(selectedEvent.start, "shortDateTime") }}</span> - </v-card-text> + <v-list-item v-if="!withoutTime"> + <v-list-item-icon> + <v-icon color="primary">mdi-calendar-today-outline</v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title> + <span v-if="selectedEvent.start !== selectedEvent.end"> + {{ $d(selectedEvent.start, "shortDateTime") }} – + {{ $d(selectedEvent.end, "shortDateTime") }} + </span> + <span v-else> + {{ $d(selectedEvent.start, "shortDateTime") }}</span + > + </v-list-item-title> + </v-list-item-content> + </v-list-item> </slot> <slot name="description" :selected-event="selectedEvent"> - <v-divider v-if="selectedEvent.description && !withoutDescription" /> - <v-card-text - class="d-flex" + <v-divider + inset v-if="selectedEvent.description && !withoutDescription" - > - <div> - <v-icon left>mdi-card-text-outline</v-icon> - </div> - <div style="white-space: pre-line"> + /> + <v-list-item v-if="selectedEvent.description && !withoutDescription"> + <v-list-item-icon> + <v-icon color="primary">mdi-card-text-outline</v-icon> + </v-list-item-icon> + <v-list-item-content style="white-space: pre-line"> {{ selectedEvent.description }} - </div> - </v-card-text> + </v-list-item-content> + </v-list-item> </slot> </v-card> </v-menu> @@ -56,9 +57,11 @@ <script> import calendarFeedDetailsMixin from "../../mixins/calendarFeedDetails.js"; +import CancelledCalendarStatusChip from "./CancelledCalendarStatusChip.vue"; export default { name: "BaseCalendarFeedDetails", + components: { CancelledCalendarStatusChip }, mixins: [calendarFeedDetailsMixin], }; </script> diff --git a/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue b/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue index 510e2af17160f6d2cbcacbf3d640cb18d1f9df5c..205cbb13a1c150555bec1e222244c2b361d31053 100644 --- a/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue +++ b/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue @@ -1,9 +1,8 @@ <template> <div - class="mx-1 text-truncate" - :style=" - event.status === 'CANCELLED' ? 'text-decoration: line-through;' : '' - " + class="text-truncate" + :class="{ 'text-decoration-line-through': event.status === 'CANCELLED', 'mx-1': withPadding }" + :style="{ height: '100%' }" > <slot name="time" v-bind="$props"> <span @@ -32,5 +31,22 @@ import calendarFeedEventBarMixin from "../../mixins/calendarFeedEventBar.js"; export default { name: "BaseCalendarFeedEventBar", mixins: [calendarFeedEventBarMixin], + props: { + withPadding: { + required: false, + type: Boolean, + default: true, + }, + icon: { + required: false, + type: String, + default: "", + }, + withoutTime: { + required: false, + type: Boolean, + default: false, + }, + } }; </script> diff --git a/aleksis/core/frontend/components/calendar/CalendarOverview.vue b/aleksis/core/frontend/components/calendar/CalendarOverview.vue index 575475db559c39a24d8ffebf645b17d666594e91..db9d10a0ab8dfc30bcb68862646b1875a050f9a0 100644 --- a/aleksis/core/frontend/components/calendar/CalendarOverview.vue +++ b/aleksis/core/frontend/components/calendar/CalendarOverview.vue @@ -195,6 +195,7 @@ export default { end: new Date(event.end), color: event.color ? event.color : cf.color, timed: !event.allDay, + meta: JSON.parse(event.meta), })) ); }, diff --git a/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue b/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue new file mode 100644 index 0000000000000000000000000000000000000000..679749135dd12e302f18a797af4d8fb169a44470 --- /dev/null +++ b/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue @@ -0,0 +1,24 @@ +<script> +export default { + name: "CalendarStatusChip", + props: { + color: { + type: String, + required: true, + }, + icon: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <v-chip :color="color" label> + <v-avatar left> + <v-icon>{{ icon }}</v-icon> + </v-avatar> + <slot></slot> + </v-chip> +</template> diff --git a/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue b/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue new file mode 100644 index 0000000000000000000000000000000000000000..00b816943654453690f32da1db95e14f57b7dce7 --- /dev/null +++ b/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue @@ -0,0 +1,14 @@ +<script> +import CalendarStatusChip from "./CalendarStatusChip.vue"; + +export default { + name: "CancelledCalendarStatusChip", + components: { CalendarStatusChip }, +}; +</script> + +<template> + <calendar-status-chip icon="mdi-cancel" color="error"> + {{ $t("calendar.cancelled") }} + </calendar-status-chip> +</template> diff --git a/aleksis/core/frontend/components/calendar/calendarOverview.graphql b/aleksis/core/frontend/components/calendar/calendarOverview.graphql index 061ab3872572d3c8e98ac1e75bc236f664f62000..777d2f5253ab24e7c07378eaf57e033f5bee874d 100644 --- a/aleksis/core/frontend/components/calendar/calendarOverview.graphql +++ b/aleksis/core/frontend/components/calendar/calendarOverview.graphql @@ -15,6 +15,7 @@ query ($start: Date, $end: Date) { uid allDay status + meta } } } diff --git a/aleksis/core/frontend/mixins/calendarFeedDetails.js b/aleksis/core/frontend/mixins/calendarFeedDetails.js index ef874b5c5600d487472ca73b957b4ed3fad801bc..4d3e9beda841af3b84eadbcb8fab910525ca518c 100644 --- a/aleksis/core/frontend/mixins/calendarFeedDetails.js +++ b/aleksis/core/frontend/mixins/calendarFeedDetails.js @@ -13,20 +13,25 @@ const calendarFeedDetailsMixin = { }, value: { type: Boolean, required: true }, withoutTime: { - required: true, + required: false, type: Boolean, default: false, }, withoutDescription: { - required: true, + required: false, type: Boolean, default: false, }, withoutBadge: { - required: true, + required: false, type: Boolean, default: false, }, + color: { + required: false, + type: String, + default: null, + }, }, computed: { model: { diff --git a/aleksis/core/frontend/mixins/calendarFeedEventBar.js b/aleksis/core/frontend/mixins/calendarFeedEventBar.js index 5a0367c2b7590dce12f6eb5d85097e23bd1b1ced..3b6590b0f68e75090b200bd0aa0fcc747cfdef51 100644 --- a/aleksis/core/frontend/mixins/calendarFeedEventBar.js +++ b/aleksis/core/frontend/mixins/calendarFeedEventBar.js @@ -15,16 +15,6 @@ const calendarFeedEventBarMixin = { required: true, type: String, }, - icon: { - required: false, - type: String, - default: "", - }, - withoutTime: { - required: true, - type: Boolean, - default: false, - }, }, }; diff --git a/aleksis/core/schema/calendar.py b/aleksis/core/schema/calendar.py index 4414249bc16259468c67256ee356732028cdaf7e..9aa2ad53227b1a3dca1f9b6ab0f0c4a629d21fe8 100644 --- a/aleksis/core/schema/calendar.py +++ b/aleksis/core/schema/calendar.py @@ -15,6 +15,7 @@ class CalendarEventType(ObjectType): uid = graphene.String() all_day = graphene.Boolean() status = graphene.String() + meta = graphene.String() def resolve_name(root, info, **kwargs): return root["SUMMARY"] @@ -40,6 +41,9 @@ class CalendarEventType(ObjectType): def resolve_status(root, info, **kwargs): return root.get("STATUS", "") + def resolve_meta(root, info, **kwargs): + return root.get("X-META", {}) + class CalendarFeedType(ObjectType): events = graphene.List( diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index 65576a76ce333b870e8eaeba2656cd92ae1c60ea..26bcd0b83521c76581be52ca1272b672c0f01696 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -1,3 +1,4 @@ +import json import os from datetime import datetime, timedelta from importlib import import_module, metadata @@ -498,6 +499,7 @@ feedgenerator.FEED_FIELD_MAP = feedgenerator.FEED_FIELD_MAP + (("color", "color" feedgenerator.ITEM_ELEMENT_FIELD_MAP = feedgenerator.ITEM_ELEMENT_FIELD_MAP + ( ("color", "color"), ("recurrence_id", "recurrence-id"), + ("meta", "x-meta"), ) @@ -507,7 +509,7 @@ class ExtendedICal20Feed(feedgenerator.ICal20Feed): Adds a method to return the actual calendar object. """ - def get_calendar_object(self): + def get_calendar_object(self, with_meta=True): cal = Calendar() cal.add("version", "2.0") cal.add("calscale", "GREGORIAN") @@ -517,28 +519,19 @@ class ExtendedICal20Feed(feedgenerator.ICal20Feed): if val is not None: cal.add(efield, val) - self.write_items(cal) + self.write_items(cal, with_meta=with_meta) return cal def write(self, outfile, encoding): - cal = Calendar() - cal.add("version", "2.0") - cal.add("calscale", "GREGORIAN") - - for ifield, efield in feedgenerator.ITEM_ELEMENT_FIELD_MAP: - val = self.feed.get(ifield) - if val is not None: - cal.add(efield, val) - - self.write_items(cal) + cal = self.get_calendar_object(with_meta=False) to_ical = getattr(cal, "as_string", None) if not to_ical: to_ical = cal.to_ical outfile.write(to_ical()) - def write_items(self, calendar): + def write_items(self, calendar, with_meta=True): for item in self.items: component_type = item.get("component_type") if component_type == "todo": @@ -554,6 +547,9 @@ class ExtendedICal20Feed(feedgenerator.ICal20Feed): elif ifield == "valarm": for list_item in val: element.add_component(list_item) + elif ifield == "meta": + if with_meta: + element.add(efield, json.dumps(val)) else: element.add(efield, val) calendar.add_component(element) diff --git a/aleksis/core/vite.config.js b/aleksis/core/vite.config.js index f1402e7702877f33967158820fda5ecf4f4141db..bc408ab344bd9d1d22da7b562a97c02a07fdf91e 100644 --- a/aleksis/core/vite.config.js +++ b/aleksis/core/vite.config.js @@ -122,7 +122,7 @@ function generateAppImporter(appDetails) { // Include calendar feed event bar components from all apps code += generateComponentsImportCode( appMeta.assetDir, - "/calendar_feeds/event_bas/", + "/calendar_feeds/event_bar/", appMeta.name, "calendarFeedEventBarComponents" );