101101 :calendar =" calendar " />
102102 </template >
103103
104+ <template v-if =" ! isPublic && isDelegationSupported && sortedCalendars .delegated .length " >
105+ <template v-for =" group in delegatedGroups " :key =" group .delegatorUrl " >
106+ <NcAppNavigationCaption
107+ :name =" group .readOnly
108+ ? $t (' calendar' , ' Delegated by {name} (read-only)' , { name: group .displayname })
109+ : $t (' calendar' , ' Delegated by {name}' , { name: group .displayname }) " />
110+ <CalendarListItem
111+ v-for =" calendar in group .calendars "
112+ :key =" calendar .id "
113+ :calendar =" calendar " />
114+ </template >
115+ </template >
116+
104117 <NcAppNavigationSpacer />
105118
106119 <!-- The header slot must be placed here, otherwise vuedraggable adds undefined as item to the array -->
@@ -122,6 +135,9 @@ import CalendarListItemLoadingPlaceholder from './CalendarList/CalendarListItemL
122135import CalendarListNew from ' ./CalendarList/CalendarListNew.vue'
123136import PublicCalendarListItem from ' ./CalendarList/PublicCalendarListItem.vue'
124137import useCalendarsStore from ' ../../store/calendars.js'
138+ import useDelegationStore from ' ../../store/delegation.ts'
139+ import usePrincipalsStore from ' ../../store/principals.js'
140+ import { isAfterVersion } from ' ../../utils/nextcloudVersion.ts'
125141
126142const limit = pLimit (1 )
127143
@@ -153,13 +169,14 @@ export default {
153169 return {
154170 calendars: [],
155171 /**
156- * Calendars sorted by personal, shared, deck, and tasks
172+ * Calendars sorted by personal, shared, deck, and delegated
157173 */
158174 sortedCalendars: {
159175 personal: [],
160176 shared: [],
161177 deck: [],
162178 tasks: [],
179+ delegated: [],
163180 },
164181
165182 disableDragging: false ,
@@ -168,14 +185,43 @@ export default {
168185 },
169186
170187 computed: {
171- ... mapStores (useCalendarsStore),
188+ ... mapStores (useCalendarsStore, useDelegationStore, usePrincipalsStore ),
172189 ... mapState (useCalendarsStore, {
173190 serverCalendars: ' sortedCalendarsSubscriptions' ,
174191 }),
175192
176193 loadingKeyCalendars () {
177194 return this ._uid + ' -loading-placeholder-calendars'
178195 },
196+
197+ isDelegationSupported () {
198+ return isAfterVersion (34 )
199+ },
200+
201+ /**
202+ * Delegated calendars grouped by the delegator (the user who granted
203+ * proxy access), which may differ from each calendar's owner when the
204+ * delegator only has access via a regular share.
205+ *
206+ * @return {Array<{delegatorUrl: string, displayname: string, calendars: object[]}>}
207+ */
208+ delegatedGroups () {
209+ const groups = new Map ()
210+ for (const calendar of this .sortedCalendars .delegated ) {
211+ const delegatorUrl = calendar .delegatorUrl || calendar .owner || ' '
212+ if (! groups .has (delegatorUrl)) {
213+ const principal = this .principalsStore .getPrincipalByUrl (delegatorUrl)
214+ groups .set (delegatorUrl, {
215+ delegatorUrl,
216+ displayname: principal? .displayname || principal? .userId || ' ' ,
217+ readOnly: !! calendar .readOnly ,
218+ calendars: [],
219+ })
220+ }
221+ groups .get (delegatorUrl).calendars .push (calendar)
222+ }
223+ return Array .from (groups .values ())
224+ },
179225 },
180226
181227 watch: {
@@ -208,9 +254,15 @@ export default {
208254 shared: [],
209255 deck: [],
210256 tasks: [],
257+ delegated: [],
211258 }
212259
213260 this .calendars .forEach ((calendar ) => {
261+ if (calendar .isDelegated ) {
262+ this .sortedCalendars .delegated .push (calendar)
263+ return
264+ }
265+
214266 if (calendar .isSharedWithMe ) {
215267 this .sortedCalendars .shared .push (calendar)
216268 return
0 commit comments