Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/calendar.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2022-08-02 13:22:32 +0300
committerRichard Steinmetz <richard@steinmetz.cloud>2022-11-04 18:29:19 +0300
commit1632e8edbbd5fd7d8a6cd35a4d9ddc374343d088 (patch)
tree4b48b731e20729ec83cf0fdf8a84853b5fbe4fee
parent871b3ef04576067bd6c0a307858ccbb0a79f83f5 (diff)
Redesign calendar sharingenh/4124/redesign-calendar-sharing
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
-rw-r--r--src/components/AppNavigation/CalendarList/CalendarListItem.vue314
-rw-r--r--src/components/AppNavigation/EditCalendarModal.vue301
-rw-r--r--src/components/AppNavigation/EditCalendarModal/PublishCalendar.vue (renamed from src/components/AppNavigation/CalendarList/CalendarListItemSharingPublishItem.vue)262
-rw-r--r--src/components/AppNavigation/EditCalendarModal/ShareItem.vue (renamed from src/components/AppNavigation/CalendarList/CalendarListItemSharingShareItem.vue)88
-rw-r--r--src/components/AppNavigation/EditCalendarModal/SharingSearch.vue (renamed from src/components/AppNavigation/CalendarList/CalendarListItemSharingSearch.vue)25
-rw-r--r--src/store/calendars.js13
-rw-r--r--src/views/Calendar.vue3
7 files changed, 599 insertions, 407 deletions
diff --git a/src/components/AppNavigation/CalendarList/CalendarListItem.vue b/src/components/AppNavigation/CalendarList/CalendarListItem.vue
index 06c66246..178cf297 100644
--- a/src/components/AppNavigation/CalendarList/CalendarListItem.vue
+++ b/src/components/AppNavigation/CalendarList/CalendarListItem.vue
@@ -1,6 +1,8 @@
<!--
- @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ -
- @author Georg Ehrke <oc.list@georgehrke.com>
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license AGPL-3.0-or-later
-
@@ -20,13 +22,12 @@
-->
<template>
- <AppNavigationItem v-click-outside="closeShareMenu"
- :loading="calendar.loading"
+ <AppNavigationItem :loading="calendar.loading"
:aria-description="descriptionAppNavigationItem"
:title="calendar.displayName || $t('calendar', 'Untitled calendar')"
- :class="{deleted: !!deleteTimeout, disabled: !calendar.enabled, 'open-sharing': shareMenuOpen}"
+ :class="{deleted: !!deleteTimeout, disabled: !calendar.enabled}"
@click.prevent.stop="toggleEnabled">
- <template slot="icon">
+ <template #icon>
<CheckboxBlankCircle v-if="calendar.enabled"
:size="20"
:fill-color="calendar.color" />
@@ -35,133 +36,74 @@
:fill-color="calendar.color" />
</template>
- <template v-if="!deleteTimeout" slot="counter">
- <Actions v-if="showSharingIcon" class="sharing">
- <ActionButton @click="toggleShareMenu">
- <template #icon>
- <LinkVariant v-if="isPublished" :size="20" decorative />
- <ShareVariant v-else
- :size="20"
- decorative
- :class="{share: !isShared}" />
- </template>
- </ActionButton>
- </Actions>
- <Avatar v-if="isSharedWithMe && loadedOwnerPrincipal" :user="ownerUserId" :display-name="ownerDisplayname" />
+ <template #counter>
+ <NcAvatar v-if="isSharedWithMe && loadedOwnerPrincipal" :user="ownerUserId" :display-name="ownerDisplayname" />
<div v-if="isSharedWithMe && !loadedOwnerPrincipal" class="icon icon-loading" />
</template>
- <template v-if="!deleteTimeout" slot="actions">
- <ActionButton v-if="showRenameLabel"
- @click.prevent.stop="openRenameInput">
- <template #icon>
- <Pencil :size="20" decorative />
- </template>
- {{ $t('calendar', 'Edit name') }}
- </ActionButton>
- <ActionInput v-if="showRenameInput"
- :value="calendar.displayName"
- @submit.prevent.stop="saveRenameInput">
- <template #icon>
- <Pencil :size="20" decorative />
- </template>
- </ActionInput>
- <ActionText v-if="showRenameSaving"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Saving name …') }}
- </ActionText>
- <ActionButton v-if="showColorLabel"
- @click.prevent.stop="openColorInput">
- <template #icon>
- <Pencil :size="20" decorative />
- </template>
- {{ $t('calendar', 'Edit color') }}
- </ActionButton>
- <ActionInput v-if="showColorInput"
- :value="calendar.color"
- type="color"
- @submit.prevent.stop="saveColorInput">
- <template #icon>
- <Pencil :size="20" decorative />
- </template>
- </ActionInput>
- <ActionText v-if="showColorSaving"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Saving color …') }}
- </ActionText>
- <ActionButton @click.stop.prevent="copyLink">
- <template #icon>
- <LinkVariant :size="20" decorative />
- </template>
- {{ $t('calendar', 'Copy private link') }}
- </ActionButton>
- <ActionLink target="_blank"
- :href="downloadUrl">
- <template #icon>
- <Download :size="20" decorative />
- </template>
- {{ $t('calendar', 'Export') }}
- </ActionLink>
- <ActionButton v-if="calendar.isSharedWithMe"
- @click.prevent.stop="deleteCalendar">
- <template #icon>
- <Close :size="20" decorative />
- </template>
- {{ $t('calendar', 'Unshare from me') }}
- </ActionButton>
- <ActionButton v-if="!calendar.isSharedWithMe"
- @click.prevent.stop="deleteCalendar">
- <template #icon>
- <Delete :size="20" decorative />
- </template>
- {{ $t('calendar', 'Delete') }}
- </ActionButton>
- </template>
-
- <template v-if="!!deleteTimeout" slot="actions">
- <ActionButton v-if="calendar.isSharedWithMe"
- @click.prevent.stop="cancelDeleteCalendar">
- <template #icon>
- <Undo :size="20" decorative />
- </template>
- {{ $n('calendar', 'Unsharing the calendar in {countdown} second', 'Unsharing the calendar in {countdown} seconds', countdown, { countdown }) }}
- </ActionButton>
- <ActionButton v-else
- @click.prevent.stop="cancelDeleteCalendar">
- <template #icon>
- <Undo :size="20" decorative />
- </template>
- {{ $n('calendar', 'Deleting the calendar in {countdown} second', 'Deleting the calendar in {countdown} seconds', countdown, { countdown }) }}
- </ActionButton>
- </template>
+ <template #actions>
+ <template v-if="!deleteTimeout">
+ <ActionButton @click.prevent.stop="showEditModal">
+ <template #icon>
+ <Pencil :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Edit and share calendar') }}
+ </ActionButton>
+ <ActionButton @click.stop.prevent="copyLink">
+ <template #icon>
+ <LinkVariant :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Copy private link') }}
+ </ActionButton>
+ <ActionLink target="_blank"
+ :href="downloadUrl">
+ <template #icon>
+ <Download :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Export') }}
+ </ActionLink>
+ <ActionButton v-if="calendar.isSharedWithMe"
+ @click.prevent.stop="deleteCalendar">
+ <template #icon>
+ <Close :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Unshare from me') }}
+ </ActionButton>
+ <ActionButton v-if="!calendar.isSharedWithMe"
+ @click.prevent.stop="deleteCalendar">
+ <template #icon>
+ <Delete :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Delete') }}
+ </ActionButton>
+ </template>
- <template v-if="!deleteTimeout">
- <div v-show="shareMenuOpen" class="sharing-section">
- <CalendarListItemSharingSearch v-if="calendar.canBeShared" :calendar="calendar" />
- <CalendarListItemSharingPublishItem v-if="calendar.canBePublished" :calendar="calendar" />
- <CalendarListItemSharingShareItem v-for="sharee in calendar.shares"
- v-show="shareMenuOpen"
- :key="sharee.uri"
- :sharee="sharee"
- :calendar="calendar" />
- </div>
+ <template v-if="deleteTimeout">
+ <ActionButton v-if="calendar.isSharedWithMe"
+ @click.prevent.stop="cancelDeleteCalendar">
+ <template #icon>
+ <Undo :size="20" decorative />
+ </template>
+ {{ $n('calendar', 'Unsharing the calendar in {countdown} second', 'Unsharing the calendar in {countdown} seconds', countdown, { countdown }) }}
+ </ActionButton>
+ <ActionButton v-else
+ @click.prevent.stop="cancelDeleteCalendar">
+ <template #icon>
+ <Undo :size="20" decorative />
+ </template>
+ {{ $n('calendar', 'Deleting the calendar in {countdown} second', 'Deleting the calendar in {countdown} seconds', countdown, { countdown }) }}
+ </ActionButton>
+ </template>
</template>
</AppNavigationItem>
</template>
<script>
-import Avatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import Actions from '@nextcloud/vue/dist/Components/NcActions.js'
+import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import ActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
-import ActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import ActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
-import ActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import AppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
-import ClickOutside from 'vue-click-outside'
import {
- showInfo,
showSuccess,
showError,
} from '@nextcloud/dialogs'
@@ -169,9 +111,6 @@ import {
generateRemoteUrl,
} from '@nextcloud/router'
-import CalendarListItemSharingSearch from './CalendarListItemSharingSearch.vue'
-import CalendarListItemSharingPublishItem from './CalendarListItemSharingPublishItem.vue'
-import CalendarListItemSharingShareItem from './CalendarListItemSharingShareItem.vue'
import CheckboxBlankCircle from 'vue-material-design-icons/CheckboxBlankCircle.vue'
import CheckboxBlankCircleOutline from 'vue-material-design-icons/CheckboxBlankCircleOutline.vue'
import Close from 'vue-material-design-icons/Close.vue'
@@ -179,22 +118,15 @@ import Delete from 'vue-material-design-icons/Delete.vue'
import Download from 'vue-material-design-icons/Download.vue'
import LinkVariant from 'vue-material-design-icons/LinkVariant.vue'
import Pencil from 'vue-material-design-icons/Pencil.vue'
-import ShareVariant from 'vue-material-design-icons/ShareVariant.vue'
import Undo from 'vue-material-design-icons/Undo.vue'
export default {
name: 'CalendarListItem',
components: {
- Avatar,
- Actions,
+ NcAvatar,
ActionButton,
- ActionInput,
ActionLink,
- ActionText,
AppNavigationItem,
- CalendarListItemSharingSearch,
- CalendarListItemSharingPublishItem,
- CalendarListItemSharingShareItem,
CheckboxBlankCircle,
CheckboxBlankCircleOutline,
Close,
@@ -202,12 +134,8 @@ export default {
Download,
LinkVariant,
Pencil,
- ShareVariant,
Undo,
},
- directives: {
- ClickOutside,
- },
props: {
calendar: {
type: Object,
@@ -216,14 +144,6 @@ export default {
},
data() {
return {
- // Rename action
- showRenameLabel: true,
- showRenameInput: false,
- showRenameSaving: false,
- // Color action
- showColorLabel: true,
- showColorInput: false,
- showColorSaving: false,
// share menu
shareMenuOpen: false,
// Deleting
@@ -348,6 +268,7 @@ export default {
console.error(error)
})
},
+
/**
* Deletes or unshares the calendar
*/
@@ -373,6 +294,7 @@ export default {
}
}, 7000)
},
+
/**
* Cancels the deletion of a calendar
*/
@@ -383,36 +305,7 @@ export default {
this.deleteInterval = null
this.countdown = 7
},
- /**
- * Closes the share menu
- * This is used with v-click-outside
- *
- * @param {Event} event The javascript click event
- */
- closeShareMenu(event) {
- if (!event.isTrusted) {
- return
- }
-
- if (this.$el.contains(event.target)) {
- this.shareMenuOpen = true
- return
- }
- if (event.composedPath && event.composedPath().includes(this.$el)) {
- this.shareMenuOpen = true
- return
- }
-
- this.shareMenuOpen = false
- },
- /**
- * Toggles the visibility of the share menu
- */
- toggleShareMenu() {
- this.shareMenuOpen = !this.shareMenuOpen
- console.debug('toggled share menu')
- },
/**
* Copies the private calendar link
* to be used with clients like Thunderbird
@@ -431,85 +324,14 @@ export default {
showError(this.$t('calendar', 'Calendar link could not be copied to clipboard.'))
}
},
- /**
- * Opens the input-field to rename the calendar
- */
- openRenameInput() {
- // Hide label and show input
- this.showRenameLabel = false
- this.showRenameInput = true
- this.showRenameSaving = false
- // Reset color input if necessary
- this.showColorLabel = true
- this.showColorInput = false
- this.showColorSaving = false
- },
- /**
- * Saves the modified name of a calendar
- *
- * @param {Event} event The submit event
- */
- async saveRenameInput(event) {
- this.showRenameInput = false
- this.showRenameSaving = true
- const newName = event.target.querySelector('input[type=text]').value
- try {
- await this.$store.dispatch('renameCalendar', {
- calendar: this.calendar,
- newName,
- })
- this.showRenameLabel = true
- this.showRenameInput = false
- this.showRenameSaving = false
- } catch (error) {
- showInfo(this.$t('calendar', 'An error occurred, unable to rename the calendar.'))
- console.error(error)
-
- this.showRenameLabel = false
- this.showRenameInput = true
- this.showRenameSaving = false
- }
- },
- /**
- * Opens the color-picker
- */
- openColorInput() {
- // Hide label and show input
- this.showColorLabel = false
- this.showColorInput = true
- this.showColorSaving = false
- // Reset rename input if necessary
- this.showRenameLabel = true
- this.showRenameInput = false
- this.showRenameSaving = false
- },
/**
- * Saves the modified color of a calendar
- *
- * @param {Event} event The submit event
+ * Open the calendar modal for this calendar item.
*/
- async saveColorInput(event) {
- this.showColorInput = false
- this.showColorSaving = true
-
- const newColor = event.target.querySelector('input[type=color]').value
- try {
- await this.$store.dispatch('changeCalendarColor', {
- calendar: this.calendar,
- newColor,
- })
- this.showColorLabel = true
- this.showColorInput = false
- this.showColorSaving = false
- } catch (error) {
- showInfo(this.$t('calendar', 'An error occurred, unable to change the calendar\'s color.'))
- console.error(error)
-
- this.showColorLabel = false
- this.showColorInput = true
- this.showColorSaving = false
- }
+ showEditModal() {
+ this.$store.commit('showEditCalendarModal', {
+ calendarId: this.calendar.id,
+ })
},
},
}
diff --git a/src/components/AppNavigation/EditCalendarModal.vue b/src/components/AppNavigation/EditCalendarModal.vue
new file mode 100644
index 00000000..7005f475
--- /dev/null
+++ b/src/components/AppNavigation/EditCalendarModal.vue
@@ -0,0 +1,301 @@
+<!--
+ - @copyright Copyright (c) 2022 Richard Steinmetz <richard@steinmetz.cloud>
+ -
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
+ -
+ - @license AGPL-3.0-or-later
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <NcModal v-if="!!editCalendarModal && calendar" size="normal" @close="saveAndClose">
+ <div class="edit-calendar-modal">
+ <h2>{{ $t('calendar', 'Edit calendar') }}</h2>
+
+ <div class="edit-calendar-modal__name-and-color">
+ <div class="edit-calendar-modal__name-and-color__color">
+ <div v-if="loading"
+ class="edit-calendar-modal__name-and-color__color__dot"
+ :style="{'background-color': calendarColor}" />
+ <NcColorPicker v-else v-model="calendarColor">
+ <div class="edit-calendar-modal__name-and-color__color__dot"
+ :style="{'background-color': calendarColor}" />
+ </NcColorPicker>
+ </div>
+
+ <input v-model="calendarName"
+ class="edit-calendar-modal__name-and-color__name"
+ type="text"
+ :disabled="loading">
+ </div>
+
+ <!--<h2>Additional actions</h2>-->
+ <div class="edit-calendar-modal__additional-actions">
+ <NcButton type="primary" @click="copyPrivateLink">
+ <template #icon>
+ <LinkVariantIcon :size="20" />
+ </template>
+ {{ $t('calendar', 'Copy private link') }}
+ </NcButton>
+
+ <NcButton type="primary" @click="exportCalendar">
+ <template #icon>
+ <DownloadIcon :size="20" />
+ </template>
+ {{ $t('calendar', 'Export') }}
+ </NcButton>
+
+ <NcButton type="error" @click="deleteCalendar">
+ <template #icon>
+ <DeleteIcon :size="20" />
+ </template>
+ {{ $t('calendar', 'Delete') }}
+ </NcButton>
+ </div>
+
+ <h2 class="edit-calendar-modal__sharing-header">
+ {{ $t('calendar', 'Share calendar') }}
+ </h2>
+
+ <div class="edit-calendar-modal__sharing">
+ <SharingSearch :calendar="calendar" />
+ <PublishCalendar :calendar="calendar" />
+ <ShareItem v-for="sharee in calendar.shares"
+ :key="sharee.uri"
+ :sharee="sharee"
+ :calendar="calendar" />
+ </div>
+
+ <!--
+ <div class="edit-calendar-modal__actions">
+ <NcButton type="primary" @click="saveAndClose">
+ {{ $t('calendar', 'Save') }}
+ </NcButton>
+ </div>
+ -->
+ </div>
+ </NcModal>
+</template>
+
+<script>
+import NcModal from '@nextcloud/vue/dist/Components/NcModal'
+import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton'
+import PublishCalendar from './EditCalendarModal/PublishCalendar'
+import SharingSearch from './EditCalendarModal/SharingSearch'
+import ShareItem from './EditCalendarModal/ShareItem'
+import { mapGetters } from 'vuex'
+import logger from '../../utils/logger'
+import debounce from 'debounce'
+import LinkVariantIcon from 'vue-material-design-icons/LinkVariant.vue'
+import DownloadIcon from 'vue-material-design-icons/Download.vue'
+import DeleteIcon from 'vue-material-design-icons/Delete.vue'
+
+export default {
+ name: 'EditCalendarModal',
+ components: {
+ NcModal,
+ NcColorPicker,
+ NcButton,
+ PublishCalendar,
+ SharingSearch,
+ ShareItem,
+ LinkVariantIcon,
+ DownloadIcon,
+ DeleteIcon,
+ },
+ props: {
+ },
+ data() {
+ return {
+ loading: false,
+ calendarColor: undefined,
+ calendarColorChanged: false,
+ calendarName: undefined,
+ calendarNameChanged: false,
+ }
+ },
+ computed: {
+ ...mapGetters(['editCalendarModal']),
+ calendar() {
+ const id = this.editCalendarModal?.calendarId
+ if (!id) {
+ return undefined
+ }
+
+ return this.$store.getters.getCalendarById(id)
+ },
+ },
+ watch: {
+ async calendarColor() {
+ await this.saveColor()
+ },
+ async calendarName() {
+ await this.saveName()
+ },
+ editCalendarModal(value) {
+ if (!value) {
+ return
+ }
+
+ this.calendarName = this.calendar.displayName
+ this.calendarColor = this.calendar.color
+ },
+ },
+ methods: {
+ /**
+ * Close the modal (without saving).
+ */
+ closeModal() {
+ this.$store.commit('hideEditCalendarModal')
+ },
+
+ /**
+ * Save the calendar color.
+ */
+ async saveColor() {
+ this.loading = true
+
+ try {
+ await this.$store.dispatch('changeCalendarColor', {
+ calendar: this.calendar,
+ newColor: this.calendarColor,
+ })
+ } catch (error) {
+ logger.error('Failed to save calendar color', {
+ calendar: this.calendar,
+ newColor: this.calendarColor,
+ })
+ } finally {
+ this.loading = false
+ }
+ },
+ saveColorDebounced: debounce(() => this.saveColor(), 1000),
+
+ /**
+ * Save the calendar name.
+ */
+ async saveName() {
+ this.loading = true
+
+ try {
+ await this.$store.dispatch('renameCalendar', {
+ calendar: this.calendar,
+ newName: this.calendarName,
+ })
+ } catch (error) {
+ logger.error('Failed to save calendar name', {
+ calendar: this.calendar,
+ newName: this.calendarName,
+ })
+ } finally {
+ this.loading = false
+ }
+ },
+ saveNameDebounced: debounce(() => this.saveName(), 1000),
+
+ /**
+ * Save unsaved changes and close the modal.
+ *
+ * @return {Promise<void>}
+ */
+ async saveAndClose() {
+ await this.saveColor.flush()
+ await this.saveName.flush()
+ this.closeModal()
+ },
+
+ /**
+ * Copy the internal/private link of this calendar to the clipboard.
+ *
+ * @return {Promise<void>}
+ */
+ async copyPrivateLink() {
+ // TODO
+ },
+
+ /**
+ * Export the calendar as a download.
+ *
+ * @return {Promise<void>}
+ */
+ async exportCalendar() {
+ // TODO
+ },
+
+ /**
+ * Delete the calendar.
+ *
+ * @return {Promise<void>}
+ */
+ async deleteCalendar() {
+ // TODO
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.edit-calendar-modal {
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+
+ &__name-and-color {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 10px;
+
+ &__color {
+ ::v-deep &__dot {
+ width: 24px;
+ height: 24px;
+ border-radius: 12px;
+ }
+ }
+
+ &__name {
+ flex: 1 auto;
+ }
+ }
+
+ &__additional-actions {
+ display: flex;
+ gap: 10px;
+
+ button {
+ flex: 1 auto;
+ }
+ }
+
+ &__sharing-header {
+ margin-top: 25px;
+ }
+
+ &__sharing {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ }
+
+ &__actions {
+ display: flex;
+ justify-content: end;
+ margin-top: 15px;
+ }
+}
+</style>
diff --git a/src/components/AppNavigation/CalendarList/CalendarListItemSharingPublishItem.vue b/src/components/AppNavigation/EditCalendarModal/PublishCalendar.vue
index 7768a29b..3e1c1e8e 100644
--- a/src/components/AppNavigation/CalendarList/CalendarListItemSharingPublishItem.vue
+++ b/src/components/AppNavigation/EditCalendarModal/PublishCalendar.vue
@@ -1,6 +1,8 @@
<!--
- @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ -
- @author Georg Ehrke <oc.list@georgehrke.com>
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license AGPL-3.0-or-later
-
@@ -20,135 +22,127 @@
-->
<template>
- <AppNavigationItem :title="$t('calendar', 'Share link')"
- :menu-open.sync="menuOpen">
- <template slot="icon">
- <LinkVariant :class="{published: isPublished}"
- :size="18"
- decorative
- class="avatar" />
- </template>
+ <div class="publish-calendar">
+ <div class="publish-calendar__icon">
+ <LinkVariant :size="20" />
+ </div>
- <template v-if="!isPublished" slot="actions">
- <ActionButton v-if="!publishingCalendar"
- key="publish"
- @click.prevent.stop="publishCalendar">
- <template #icon>
- <Plus :size="20" decorative />
- </template>
- {{ $t('calendar', 'Publish calendar') }}
- </ActionButton>
- <ActionButton v-else
- key="publishing"
- icon="icon-loading-small"
- :disabled="true">
- {{ $t('calendar', 'Publishing calendar') }}
- </ActionButton>
- </template>
+ <p class="publish-calendar__label">
+ {{ $t('calendar', 'Share link') }}
+ </p>
- <template v-if="isPublished" slot="counter">
- <Actions>
- <ActionButton @click.prevent.stop="copyPublicLink">
+ <template v-if="isPublished">
+ <NcActions>
+ <NcActionButton @click.prevent.stop="copyPublicLink">
<template #icon>
<ClipboardArrowLeftOutline :size="20" decorative />
</template>
{{ $t('calendar', 'Copy public link') }}
- </ActionButton>
- </Actions>
- </template>
- <template v-if="isPublished" slot="actions">
- <ActionButton v-if="showEMailLabel"
- @click.prevent.stop="openEMailLinkInput">
- <template #icon>
- <Email :size="20" decorative />
- </template>
- {{ $t('calendar', 'Send link to calendar via email') }}
- </ActionButton>
- <ActionInput v-if="showEMailInput"
- @submit.prevent.stop="sendLinkViaEMail">
- <template #icon>
- <Email :size="20" decorative />
- </template>
- {{ $t('calendar', 'Enter one address') }}
- </ActionInput>
- <ActionText v-if="showEMailSending"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Sending email …') }}
- </ActionText>
+ </NcActionButton>
+ </NcActions>
- <ActionButton v-if="showCopySubscriptionLinkLabel"
- @click.prevent.stop="copySubscriptionLink">
- <template #icon>
- <CalendarBlank :size="20" decorative />
- </template>
- {{ $t('calendar', 'Copy subscription link') }}
- </ActionButton>
- <ActionText v-if="showCopySubscriptionLinkSpinner"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Copying link …') }}
- </ActionText>
- <ActionText v-if="showCopySubscriptionLinkSuccess">
- <template #icon>
- <CalendarBlank :size="20" decorative />
- </template>
- {{ $t('calendar', 'Copied link') }}
- </ActionText>
- <ActionText v-if="showCopySubscriptionLinkError">
- <template #icon>
- <CalendarBlank :size="20" decorative />
- </template>
- {{ $t('calendar', 'Could not copy link') }}
- </ActionText>
+ <NcActions>
+ <NcActionButton v-if="showEMailLabel"
+ @click.prevent.stop="openEMailLinkInput">
+ <template #icon>
+ <Email :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Send link to calendar via email') }}
+ </NcActionButton>
+ <NcActionInput v-if="showEMailInput"
+ @submit.prevent.stop="sendLinkViaEMail">
+ <template #icon>
+ <Email :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Enter one address') }}
+ </NcActionInput>
+ <NcActionText v-if="showEMailSending"
+ icon="icon-loading-small">
+ <!-- eslint-disable-next-line no-irregular-whitespace -->
+ {{ $t('calendar', 'Sending email …') }}
+ </NcActionText>
- <ActionButton v-if="showCopyEmbedCodeLinkLabel"
- @click.prevent.stop="copyEmbedCode">
- <template #icon>
- <CodeBrackets :size="20" decorative />
- </template>
- {{ $t('calendar', 'Copy embedding code') }}
- </ActionButton>
- <ActionText v-if="showCopyEmbedCodeLinkSpinner"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Copying code …') }}
- </ActionText>
- <ActionText v-if="showCopyEmbedCodeLinkSuccess">
- <template #icon>
- <CodeBrackets :size="20" decorative />
- </template>
- {{ $t('calendar', 'Copied code') }}
- </ActionText>
- <ActionText v-if="showCopyEmbedCodeLinkError">
- <template #icon>
- <CodeBrackets :size="20" decorative />
- </template>
- {{ $t('calendar', 'Could not copy code') }}
- </ActionText>
+ <NcActionButton v-if="showCopySubscriptionLinkLabel"
+ @click.prevent.stop="copySubscriptionLink">
+ <template #icon>
+ <CalendarBlank :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Copy subscription link') }}
+ </NcActionButton>
+ <NcActionText v-if="showCopySubscriptionLinkSpinner"
+ icon="icon-loading-small">
+ <!-- eslint-disable-next-line no-irregular-whitespace -->
+ {{ $t('calendar', 'Copying link …') }}
+ </NcActionText>
+ <NcActionText v-if="showCopySubscriptionLinkSuccess">
+ <template #icon>
+ <CalendarBlank :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Copied link') }}
+ </NcActionText>
+ <NcActionText v-if="showCopySubscriptionLinkError">
+ <template #icon>
+ <CalendarBlank :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Could not copy link') }}
+ </NcActionText>
+
+ <NcActionButton v-if="showCopyEmbedCodeLinkLabel"
+ @click.prevent.stop="copyEmbedCode">
+ <template #icon>
+ <CodeBrackets :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Copy embedding code') }}
+ </NcActionButton>
+ <NcActionText v-if="showCopyEmbedCodeLinkSpinner"
+ icon="icon-loading-small">
+ <!-- eslint-disable-next-line no-irregular-whitespace -->
+ {{ $t('calendar', 'Copying code …') }}
+ </NcActionText>
+ <NcActionText v-if="showCopyEmbedCodeLinkSuccess">
+ <template #icon>
+ <CodeBrackets :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Copied code') }}
+ </NcActionText>
+ <NcActionText v-if="showCopyEmbedCodeLinkError">
+ <template #icon>
+ <CodeBrackets :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Could not copy code') }}
+ </NcActionText>
- <ActionButton v-if="!unpublishingCalendar"
- @click.prevent.stop="unpublishCalendar">
+ <NcActionButton v-if="!unpublishingCalendar"
+ @click.prevent.stop="unpublishCalendar">
+ <template #icon>
+ <Delete :size="20" decorative />
+ </template>
+ {{ $t('calendar', 'Delete share link') }}
+ </NcActionButton>
+ <NcActionText v-if="unpublishingCalendar"
+ icon="icon-loading-small">
+ <!-- eslint-disable-next-line no-irregular-whitespace -->
+ {{ $t('calendar', 'Deleting share link …') }}
+ </NcActionText>
+ </NcActions>
+ </template>
+ <NcActions v-else>
+ <NcActionButton aria-label="$t('calendar', 'Share link')"
+ :disabled="publishingCalendar"
+ @click.prevent.stop="publishCalendar">
<template #icon>
- <Delete :size="20" decorative />
+ <PlusIcon :size="20" />
</template>
- {{ $t('calendar', 'Delete share link') }}
- </ActionButton>
- <ActionText v-if="unpublishingCalendar"
- icon="icon-loading-small">
- <!-- eslint-disable-next-line no-irregular-whitespace -->
- {{ $t('calendar', 'Deleting share link …') }}
- </ActionText>
- </template>
- </AppNavigationItem>
+ </NcActionButton>
+ </NcActions>
+ </div>
</template>
<script>
-import Actions from '@nextcloud/vue/dist/Components/NcActions.js'
-import ActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
-import ActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
-import ActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
-import AppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
+import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
+import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
+import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
import ClickOutside from 'vue-click-outside'
import {
generateRemoteUrl,
@@ -167,23 +161,22 @@ import CodeBrackets from 'vue-material-design-icons/CodeBrackets.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
import Email from 'vue-material-design-icons/Email.vue'
import LinkVariant from 'vue-material-design-icons/LinkVariant.vue'
-import Plus from 'vue-material-design-icons/Plus.vue'
+import PlusIcon from 'vue-material-design-icons/Plus.vue'
export default {
- name: 'CalendarListItemSharingPublishItem',
+ name: 'PublishCalendar',
components: {
- Actions,
- ActionButton,
- ActionInput,
- ActionText,
- AppNavigationItem,
+ NcActions,
+ NcActionButton,
+ NcActionInput,
+ NcActionText,
CalendarBlank,
ClipboardArrowLeftOutline,
CodeBrackets,
Delete,
Email,
LinkVariant,
- Plus,
+ PlusIcon,
},
directives: {
ClickOutside,
@@ -385,3 +378,26 @@ export default {
},
}
</script>
+
+<style lang="scss" scoped>
+.publish-calendar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ &__icon {
+ display: flex;
+ width: 32px;
+ height: 32px;
+ border-radius: 16px;
+ color: white;
+ background-color: var(--color-primary);
+ align-items: center;
+ justify-content: center;
+ }
+
+ &__label {
+ flex: 1 auto;
+ }
+}
+</style>
diff --git a/src/components/AppNavigation/CalendarList/CalendarListItemSharingShareItem.vue b/src/components/AppNavigation/EditCalendarModal/ShareItem.vue
index 8f1f79e3..d6419def 100644
--- a/src/components/AppNavigation/CalendarList/CalendarListItemSharingShareItem.vue
+++ b/src/components/AppNavigation/EditCalendarModal/ShareItem.vue
@@ -1,6 +1,8 @@
<!--
- @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ -
- @author Georg Ehrke <oc.list@georgehrke.com>
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license AGPL-3.0-or-later
-
@@ -20,56 +22,53 @@
-->
<template>
- <AppNavigationItem :title="sharee.displayName">
- <template slot="icon">
- <AccountMultiple v-if="sharee.isGroup"
- :size="18"
- decorative
- class="avatar" />
- <IconCircle v-else-if="sharee.isCircle" />
- <Avatar v-else :user="sharee.id" :display-name="sharee.displayName" />
- </template>
+ <div class="share-item">
+ <AccountMultiple v-if="sharee.isGroup" :size="20" class="share-item__group-icon" />
+ <IconCircle v-else-if="sharee.isCircle" />
+ <NcAvatar v-else :user="sharee.id" :display-name="sharee.displayName" />
+
+ <p class="share-item__label">
+ {{ sharee.displayName }}
+ </p>
- <template slot="counter">
- <ActionCheckbox :disabled="updatingSharee"
- :checked="sharee.writeable"
- @update:checked="updatePermission">
- {{ $t('calendar', 'can edit') }}
- </ActionCheckbox>
- </template>
+ <input :id="`${id}-can-edit`"
+ :disabled="updatingSharee"
+ :checked="sharee.writeable"
+ type="checkbox"
+ class="checkbox"
+ @update:checked="updatePermission">
+ <label :for="`${id}-can-edit`">{{ $t('calendar', 'can edit') }}</label>
- <template slot="actions">
- <ActionButton :disabled="updatingSharee"
+ <NcActions>
+ <NcActionButton :disabled="updatingSharee"
@click.prevent.stop="unshare">
<template #icon>
<Delete :size="20" decorative />
</template>
{{ $t('calendar', 'Unshare with {displayName}', { displayName: sharee.displayName }) }}
- </ActionButton>
- </template>
- </AppNavigationItem>
+ </NcActionButton>
+ </NcActions>
+ </div>
</template>
<script>
-import ActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
-import ActionCheckbox from '@nextcloud/vue/dist/Components/NcActionCheckbox.js'
-import AppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
-import Avatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
-import {
- showInfo,
-} from '@nextcloud/dialogs'
-
+import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
+import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
+import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import AccountMultiple from 'vue-material-design-icons/AccountMultiple.vue'
import IconCircle from '../../Icons/IconCircles.vue'
import Delete from 'vue-material-design-icons/Delete.vue'
+import {
+ showInfo,
+} from '@nextcloud/dialogs'
+import { randomId } from '../../../utils/randomId'
export default {
- name: 'CalendarListItemSharingShareItem',
+ name: 'ShareItem',
components: {
- ActionButton,
- ActionCheckbox,
- AppNavigationItem,
- Avatar,
+ NcActions,
+ NcActionButton,
+ NcAvatar,
IconCircle,
AccountMultiple,
Delete,
@@ -86,6 +85,7 @@ export default {
},
data() {
return {
+ id: randomId(),
updatingSharee: false,
}
},
@@ -138,3 +138,23 @@ export default {
},
}
</script>
+
+<style lang="scss" scoped>
+.share-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ &__group-icon {
+ width: 32px;
+ height: 32px;
+ border-radius: 16px;
+ color: white;
+ background-color: var(--color-text-maxcontrast);
+ }
+
+ &__label {
+ flex: 1 auto;
+ }
+}
+</style>
diff --git a/src/components/AppNavigation/CalendarList/CalendarListItemSharingSearch.vue b/src/components/AppNavigation/EditCalendarModal/SharingSearch.vue
index cfae4c5d..d8c26a60 100644
--- a/src/components/AppNavigation/CalendarList/CalendarListItemSharingSearch.vue
+++ b/src/components/AppNavigation/EditCalendarModal/SharingSearch.vue
@@ -4,6 +4,7 @@
-
- @author Georg Ehrke <oc.list@georgehrke.com>
- @author Jakob Röhrl <jakob.roehrl@web.de>
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
-
- @license AGPL-3.0-or-later
-
@@ -23,23 +24,24 @@
-->
<template>
- <li class="app-navigation-entry__multiselect">
+ <div class="sharing-search">
<Multiselect :options="usersOrGroups"
:searchable="true"
:internal-search="false"
:max-height="600"
:show-no-results="true"
:placeholder="$t('calendar', 'Share with users or groups')"
+ class="sharing-search__select"
:class="{ 'showContent': inputGiven, 'icon-loading': isLoading }"
:user-select="true"
- open-direction="bottom"
+ open-direction="above"
track-by="user"
label="displayName"
@search-change="findSharee"
@change="shareCalendar">
<span slot="noResult">{{ $t('calendar', 'No users or groups') }}</span>
</Multiselect>
- </li>
+ </div>
</template>
<script>
@@ -51,7 +53,7 @@ import { generateOcsUrl } from '@nextcloud/router'
import { urldecode } from '../../../utils/url.js'
export default {
- name: 'CalendarListItemSharingSearch',
+ name: 'SharingSearch',
components: {
Multiselect,
},
@@ -232,3 +234,18 @@ export default {
},
}
</script>
+
+<style lang="scss" scoped>
+.sharing-search {
+ display: flex;
+
+ &__select {
+ flex: 1 auto;
+
+ // Fix weird height of multiselect
+ ::v-deep .multiselect__tags {
+ box-sizing: border-box;
+ }
+ }
+}
+</style>
diff --git a/src/store/calendars.js b/src/store/calendars.js
index bbb5f272..e0b06d9a 100644
--- a/src/store/calendars.js
+++ b/src/store/calendars.js
@@ -57,6 +57,7 @@ const state = {
deletedCalendarObjects: [],
calendarsById: {},
initialCalendarsLoaded: false,
+ editCalendarModal: undefined,
}
const mutations = {
@@ -378,6 +379,16 @@ const mutations = {
markCalendarAsNotLoading(state, { calendar }) {
state.calendarsById[calendar.id].loading = false
},
+
+ showEditCalendarModal(state, { calendarId }) {
+ state.editCalendarModal = {
+ calendarId,
+ }
+ },
+
+ hideEditCalendarModal(state) {
+ state.editCalendarModal = undefined
+ }
}
const getters = {
@@ -555,6 +566,8 @@ const getters = {
return true
})
},
+
+ editCalendarModal: (state) => state.editCalendarModal,
}
const actions = {
diff --git a/src/views/Calendar.vue b/src/views/Calendar.vue
index 1805cde7..b7ff0734 100644
--- a/src/views/Calendar.vue
+++ b/src/views/Calendar.vue
@@ -33,6 +33,7 @@
:loading-calendars="loadingCalendars" />
<CalendarListNew v-if="!loadingCalendars && isAuthenticatedUser"
:disabled="loadingCalendars" />
+ <EditCalendarModal />
<!-- Appointment Configuration List -->
<template v-if="isAuthenticatedUser">
@@ -73,6 +74,7 @@ import CalendarListNew from '../components/AppNavigation/CalendarList/CalendarLi
import EmbedTopNavigation from '../components/AppNavigation/EmbedTopNavigation.vue'
import EmptyCalendar from '../components/EmptyCalendar.vue'
import CalendarGrid from '../components/CalendarGrid.vue'
+import EditCalendarModal from '../components/AppNavigation/EditCalendarModal.vue'
// Import CalDAV related methods
import {
@@ -118,6 +120,7 @@ export default {
AppNavigationSpacer,
CalendarListNew,
Trashbin,
+ EditCalendarModal,
},
data() {
return {