diff --git a/src/Pages/Calendar.vue b/src/Pages/Calendar.vue index 14cbb59fa5f134ddd52adf68aeb8d0a069785c31..9fc7defe27cd806a24091ff5640f1842fea81315 100644 --- a/src/Pages/Calendar.vue +++ b/src/Pages/Calendar.vue @@ -95,7 +95,7 @@ import { useErrorList } from '@rokoli/bnb/drf' import { computedAsync, createTemplatePromise } from '@vueuse/core' import { addDays, endOfDay, parseISO, startOfDay } from 'date-fns' -import { ref, watchEffect } from 'vue' +import { computed, ref, watchEffect } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useI18n } from '@/i18n' @@ -152,81 +152,87 @@ const conflictResponse = ref<ScheduleConflictResponse | undefined>() const { program, reload: reloadProgram, - isLoading: isUpdatingData, + isLoading: isLoadingProgram, } = useProgramSlots({ start: () => startOfDay(currentStart.value), end: () => endOfDay(currentEnd.value), }) const error = ref<Error | null>(null) const errorList = useErrorList(error) +const isUpdatingData = computed(() => isLoadingProgram.value || isGeneratingEvents.value) const calendarEventsCache = new Map() -const calendarEvents = computedAsync(async () => { - const result = [] +const isGeneratingEvents = ref(false) +const calendarEvents = computedAsync( + async () => { + const result = [] - // fetch relevant objects upfront - await Promise.allSettled([ - showStore.retrieveMultiple( - program.value.map((e) => e.showId), - { useCached: true }, - ), - timeslotStore.retrieveMultiple( - program.value.map((e) => e.timeslotId).filter((id) => id !== null) as number[], - { useCached: true }, - ), - playlistStore.retrieveMultiple( - program.value.map((e) => e.playlistId).filter((id) => id !== null) as number[], - { useCached: true }, - ), - ]) + // fetch relevant objects upfront + await Promise.allSettled([ + showStore.retrieveMultiple( + program.value.map((e) => e.showId), + { useCached: true }, + ), + timeslotStore.retrieveMultiple( + program.value.map((e) => e.timeslotId).filter((id) => id !== null) as number[], + { useCached: true }, + ), + playlistStore.retrieveMultiple( + program.value.map((e) => e.playlistId).filter((id) => id !== null) as number[], + { useCached: true }, + ), + ]) - for (const entry of program.value) { - const cacheKey = [entry.id, entry.timeslotId, entry.playlistId].join(':') - const cachedSlot = calendarEventsCache.get(cacheKey) + for (const entry of program.value) { + const cacheKey = [entry.id, entry.timeslotId, entry.playlistId].join(':') + const cachedSlot = calendarEventsCache.get(cacheKey) - if (cachedSlot) { - result.push(cachedSlot) - continue - } + if (cachedSlot) { + result.push(cachedSlot) + continue + } - const isEmpty = entry.playlistId === null - const emptyText = isEmpty ? `: ${t('calendar.empty')} âš ` : '' - const ts = entry.timeslotId - ? await timeslotStore.retrieve(entry.timeslotId, { useCached: true }) - : null - const show = await showStore.retrieve(entry.showId, { useCached: true }) - const durationMinutes = - calculateDurationSeconds(ts?.start ?? entry.start, ts?.end ?? entry.end, false) / 60 - const isOwner = - show?.ownerIds?.includes?.(authStore?.steeringUser?.id as SteeringUser['id']) ?? false - const className = ['calendar-event', isOwner ? 'is-mine' : 'is-not-mine'] - if (durationMinutes < 0) className.push('is-invalid') - if (entry.timeslotId) className.push('is-scheduled') - else className.push('is-generated') - if (entry.timeslotId === null || radioSettings.value?.program.fallback.showId === show?.id) - className.push('is-fallback') - else className.push('is-normal') + const isEmpty = entry.playlistId === null + const emptyText = isEmpty ? `: ${t('calendar.empty')} âš ` : '' + const ts = entry.timeslotId + ? await timeslotStore.retrieve(entry.timeslotId, { useCached: true }) + : null + const show = await showStore.retrieve(entry.showId, { useCached: true }) + const durationMinutes = + calculateDurationSeconds(ts?.start ?? entry.start, ts?.end ?? entry.end, false) / 60 + const isOwner = + show?.ownerIds?.includes?.(authStore?.steeringUser?.id as SteeringUser['id']) ?? false + const className = ['calendar-event', isOwner ? 'is-mine' : 'is-not-mine'] + if (durationMinutes < 0) className.push('is-invalid') + if (entry.timeslotId) className.push('is-scheduled') + else className.push('is-generated') + if (entry.timeslotId === null || radioSettings.value?.program.fallback.showId === show?.id) + className.push('is-fallback') + else className.push('is-normal') - const title = sanitizeHTML(show?.name ?? '') + emptyText - const slot = { - id: entry.id, - start: entry.start, - end: entry.end, - className: className.join(' '), - title, - display: entry.timeslotId === null ? 'block' : 'auto', - extendedProps: { + const title = sanitizeHTML(show?.name ?? '') + emptyText + const slot = { + id: entry.id, + start: entry.start, + end: entry.end, + className: className.join(' '), title, - id: entry.timeslotId, - durationMinutes: Math.abs(durationMinutes), - }, + display: entry.timeslotId === null ? 'block' : 'auto', + extendedProps: { + title, + id: entry.timeslotId, + durationMinutes: Math.abs(durationMinutes), + }, + } + calendarEventsCache.set(cacheKey, slot) + result.push(slot) } - calendarEventsCache.set(cacheKey, slot) - result.push(slot) - } - return result -}, []) + return result + }, + [], + { evaluating: isGeneratingEvents }, +) function syncDateRange({ start, end }: { start: Date; end: Date }) { currentStart.value = start