Skip to content

Commit

Permalink
[wip] feat(calendar): add dialog for previewing upcoming events
Browse files Browse the repository at this point in the history
[skip ci]

Signed-off-by: Maksim Sukharev <[email protected]>
  • Loading branch information
Antreesy committed Dec 18, 2024
1 parent d8a7f77 commit 92dfb73
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 19 deletions.
156 changes: 156 additions & 0 deletions src/components/CalendarEventsDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import { computed, onBeforeMount, ref } from 'vue'

import IconCalendarBlank from 'vue-material-design-icons/CalendarBlank.vue'
import IconCalendarRefresh from 'vue-material-design-icons/CalendarRefresh.vue'

import { t } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'

import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import NcPopover from '@nextcloud/vue/dist/Components/NcPopover.js'
import usernameToColor from '@nextcloud/vue/dist/Functions/usernameToColor.js'

import { useGroupwareStore } from '../stores/groupware.ts'
import { UpcomingEvent } from '../types/index.ts'

type UpcomingEventFormatted = UpcomingEvent & {
startTimeLocalized: string,
style: string,
}

const props = defineProps<{
token: string,
container?: string,
}>()
const emit = defineEmits<{
(event: 'close'): void,
}>()

const groupwareStore = useGroupwareStore()

const loading = ref(groupwareStore.calendars.length === 0)

const calendars = computed(() => groupwareStore.calendars)
const upcomingEvents = computed<Record<string, UpcomingEventFormatted[]>>(() => {
const events = {}

groupwareStore.getAllEvents(props.token)
?.sort((a, b) => a.start - b.start)
.map(event => ({
...event,
startTimeLocalized: moment(event.start * 1000).format('LT'),
dateLocalized: moment(event.start * 1000).format('LL, dddd'),
style: `background-color: ${calendars.value[event.calendarUri]?.color ?? usernameToColor(event.calendarUri).color}`,
}))
.forEach(event => {
if (events[event.dateLocalized]) {
events[event.dateLocalized].push(event)
} else {
events[event.dateLocalized] = [event]
}
})

return events
})

onBeforeMount(() => {
getCalendars()
})

/**
* Get user's calendars to identify belonging of known and future events
*/
async function getCalendars() {
await groupwareStore.getPersonalCalendars()
loading.value = false
}
</script>

<template>
<NcPopover :container="container" :focus-trap="false">
<template #trigger>
<slot name="trigger" />
</template>
<template #default>
<ul v-if="!loading && Object.keys(upcomingEvents).length" class="calendar-events__wrapper">
<template v-for="(day, key) in upcomingEvents">
<li :key="key" class="calendar-events__date">
{{ key }}
</li>
<li v-for="event in day" :key="event.uri">
<a class="calendar-events__item"
:href="event.calendarAppUrl"
:title="t('spreed', 'Open Calendar')"
target="_blank">
<IconCalendarRefresh v-if="event.recurrenceId" :size="20" />
<IconCalendarBlank v-else :size="20" />
<span class="calendar-events__time">{{ event.startTimeLocalized }}</span>
<span class="calendar-events__badge" :style="event.style" />
<span>{{ event.summary }}</span>
</a>
</li>
</template>
</ul>
<NcEmptyContent v-else class="calendar-events__wrapper">
<template #icon>
<NcLoadingIcon v-if="loading" />
<IconCalendarBlank v-else />
</template>

<template #description>
<p>{{ loading ? t('spreed', 'Loading …') : t('spreed', 'No upcoming events') }}</p>
</template>
</NcEmptyContent>
</template>
</NcPopover>
</template>

<style lang="scss" scoped>
.calendar-events {
&__wrapper {
width: 400px;
padding: calc(var(--default-grid-baseline) * 2);
}

&__date {
padding: var(--default-grid-baseline);
border-radius: var(--border-radius);
background-color: var(--color-background-dark);
}

&__item {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--default-grid-baseline);
padding: var(--default-grid-baseline);
height: 100%;
border-radius: var(--border-radius);

&:not(:last-child) {
border-bottom: 1px solid var(--color-background-dark);
}
}

&__time {
flex-shrink: 0;
width: 8ch;
}

&__badge {
display: inline-block;
flex-shrink: 0;
width: calc(0.6 * var(--default-font-size));
height: calc(0.6 * var(--default-font-size));
border-radius: 50%;
background-color: var(--primary-color);
}
}
</style>
47 changes: 28 additions & 19 deletions src/components/TopBar/TopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,24 @@

<TasksCounter v-if="conversation.type === CONVERSATION.TYPE.NOTE_TO_SELF" />

<!-- Upcoming event -->
<a v-if="showUpcomingEvent"
class="upcoming-event"
:href="nextEvent.calendarAppUrl"
:title="t('spreed', 'Open Calendar')"
target="_blank">
<div class="icon">
<IconCalendarBlank :size="20" />
</div>
<div class="event-info">
<p class="event-info__header">
{{ t('spreed', 'Next call') }}
</p>
<p> {{ eventInfo }} </p>
</div>
</a>
<!-- Upcoming events dialog -->
<CalendarEventsDialog v-show="showCalendarEvents" :token="token">
<template #trigger>
<a class="upcoming-event"
role="button"
:title="t('spreed', 'Open Calendar')">
<span class="icon">
<IconCalendarBlank :size="20" />
</span>
<span v-if="showUpcomingEvent" class="event-info">
<span class="event-info__header">
{{ t('spreed', 'Next call') }}
</span>
<span>{{ eventInfo }}</span>
</span>
</a>
</template>
</CalendarEventsDialog>

<!-- Call time -->
<CallTime v-if="isInCall"
Expand Down Expand Up @@ -139,6 +141,7 @@ import TasksCounter from './TasksCounter.vue'
import TopBarMediaControls from './TopBarMediaControls.vue'
import TopBarMenu from './TopBarMenu.vue'
import BreakoutRoomsEditor from '../BreakoutRoomsEditor/BreakoutRoomsEditor.vue'
import CalendarEventsDialog from '../CalendarEventsDialog.vue'
import ConversationIcon from '../ConversationIcon.vue'

import { useGetParticipants } from '../../composables/useGetParticipants.js'
Expand All @@ -155,6 +158,7 @@ export default {
components: {
// Components
BreakoutRoomsEditor,
CalendarEventsDialog,
CallButton,
CallTime,
ConversationIcon,
Expand Down Expand Up @@ -300,9 +304,12 @@ export default {
return moment(this.nextEvent.start * 1000).calendar()
},

showCalendarEvents() {
return !this.isInCall && !this.isSidebar && this.conversation.type !== CONVERSATION.TYPE.NOTE_TO_SELF
},

showUpcomingEvent() {
return this.nextEvent && !this.isInCall && !this.isSidebar && !this.isMobile
&& this.conversation.type !== CONVERSATION.TYPE.NOTE_TO_SELF
return this.showCalendarEvents && this.nextEvent && !this.isMobile
},

getUserId() {
Expand Down Expand Up @@ -453,8 +460,10 @@ export default {
flex-direction: row;
gap: calc(var(--default-grid-baseline) * 2);
padding: 0 calc(var(--default-grid-baseline) * 2);
background-color: rgba(var(--color-info-rgb), 0.1);
background-color: var(--color-primary-element-light);
height: 100%;
min-height: var(--default-clickable-area);
min-width: var(--default-clickable-area);
border-radius: var(--border-radius);
}

Expand Down

0 comments on commit 92dfb73

Please sign in to comment.