diff options
Diffstat (limited to 'apps/files_sharing/src/components/NewFileRequestDialog/NewFileRequestDialogFinish.vue')
-rw-r--r-- | apps/files_sharing/src/components/NewFileRequestDialog/NewFileRequestDialogFinish.vue | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/apps/files_sharing/src/components/NewFileRequestDialog/NewFileRequestDialogFinish.vue b/apps/files_sharing/src/components/NewFileRequestDialog/NewFileRequestDialogFinish.vue new file mode 100644 index 00000000000..423565f4d48 --- /dev/null +++ b/apps/files_sharing/src/components/NewFileRequestDialog/NewFileRequestDialogFinish.vue @@ -0,0 +1,224 @@ +<!-- + - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <div> + <!-- Request note --> + <NcNoteCard type="success"> + {{ t('files_sharing', 'Once created, you can share the link below to allow people to upload files to your directory.') }} + </NcNoteCard> + + <!-- Copy share link --> + <NcInputField ref="clipboard" + :value="shareLink" + :label="t('files_sharing', 'Share link')" + :readonly="true" + :show-trailing-button="true" + :trailing-button-label="t('files_sharing', 'Copy to clipboard')" + @click="copyShareLink" + @click-trailing-button="copyShareLink"> + <template #trailing-button-icon> + <IconCheck v-if="isCopied" :size="20" @click="isCopied = false" /> + <IconClipboard v-else :size="20" @click="copyShareLink" /> + </template> + </NcInputField> + + <template v-if="isShareByMailEnabled"> + <!-- Email share--> + <NcTextField :value.sync="email" + :label="t('files_sharing', 'Send link via email')" + :placeholder="t('files_sharing', 'Enter an email address or paste a list')" + type="email" + @keypress.enter.stop="addNewEmail" + @paste.stop.prevent="onPasteEmails" /> + + <!-- Email list --> + <div v-if="emails.length > 0" class="file-request-dialog__emails"> + <NcChip v-for="mail in emails" + :key="mail" + :aria-label-close="t('files_sharing', 'Remove email')" + :text="mail" + @close="$emit('remove-email', mail)"> + <template #icon> + <NcAvatar :disable-menu="true" + :disable-tooltip="true" + :is-guest="true" + :size="24" + :user="mail" /> + </template> + </NcChip> + </div> + </template> + </div> +</template> + +<script lang="ts"> +import type { PropType } from 'vue' +import Share from '../../models/Share' + +import { defineComponent } from 'vue' +import { generateUrl } from '@nextcloud/router' +import { showError, showSuccess } from '@nextcloud/dialogs' +import { translate, translatePlural } from '@nextcloud/l10n' + +import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' +import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js' +import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js' +import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' +import NcChip from '@nextcloud/vue/dist/Components/NcChip.js' + +import IconCheck from 'vue-material-design-icons/Check.vue' +import IconClipboard from 'vue-material-design-icons/Clipboard.vue' + +export default defineComponent({ + name: 'FileRequestFinish', + + components: { + IconCheck, + IconClipboard, + NcAvatar, + NcInputField, + NcNoteCard, + NcTextField, + NcChip, + }, + + props: { + share: { + type: Object as PropType<Share>, + required: true, + }, + emails: { + type: Array as PropType<string[]>, + required: true, + }, + isShareByMailEnabled: { + type: Boolean, + required: true, + }, + }, + + emits: ['add-email', 'remove-email'], + + setup() { + return { + n: translatePlural, + t: translate, + } + }, + + data() { + return { + isCopied: false, + email: '', + } + }, + + computed: { + shareLink() { + return window.location.protocol + '//' + window.location.host + generateUrl('/s/') + this.share.token + }, + }, + + methods: { + async copyShareLink(event: MouseEvent) { + if (this.isCopied) { + this.isCopied = false + return + } + + if (!navigator.clipboard) { + // Clipboard API not available + window.prompt(this.t('files_sharing', 'Automatically copying failed, please copy the share link manually'), this.shareLink) + return + } + + await navigator.clipboard.writeText(this.shareLink) + + showSuccess(this.t('files_sharing', 'Link copied to clipboard')) + this.isCopied = true + event.target?.select?.() + + setTimeout(() => { + this.isCopied = false + }, 3000) + }, + + addNewEmail(e: KeyboardEvent) { + if (e.target instanceof HTMLInputElement) { + if (e.target.checkValidity() === false) { + e.target.reportValidity() + return + } + + // The email is already in the list + if (this.emails.includes(this.email.trim())) { + e.target.setCustomValidity(this.t('files_sharing', 'Email already added')) + e.target.reportValidity() + return + } + + if (!this.isValidEmail(this.email.trim())) { + e.target.setCustomValidity(this.t('files_sharing', 'Invalid email address')) + e.target.reportValidity() + return + } + + this.$emit('add-email', this.email.trim()) + this.email = '' + } + }, + + // Handle dumping a list of emails + onPasteEmails(e: ClipboardEvent) { + const clipboardData = e.clipboardData + if (!clipboardData) { + return + } + + const pastedText = clipboardData.getData('text') + const emails = pastedText.split(/[\s,;]+/).filter(Boolean).map((email) => email.trim()) + + const duplicateEmails = emails.filter((email) => this.emails.includes(email)) + const validEmails = emails.filter((email) => this.isValidEmail(email) && !duplicateEmails.includes(email)) + const invalidEmails = emails.filter((email) => !this.isValidEmail(email)) + validEmails.forEach((email) => this.$emit('add-email', email)) + + // Warn about invalid emails + if (invalidEmails.length > 0) { + showError(this.n('files_sharing', 'The following email address is not valid: {emails}', 'The following email addresses are not valid: {emails}', invalidEmails.length, { emails: invalidEmails.join(', ') })) + } + + // Warn about duplicate emails + if (duplicateEmails.length > 0) { + showError(this.n('files_sharing', '1 email address already added', '{count} email addresses already added', duplicateEmails.length, { count: duplicateEmails.length })) + } + + if (validEmails.length > 0) { + showSuccess(this.n('files_sharing', '1 email address added', '{count} email addresses added', validEmails.length, { count: validEmails.length })) + } + + this.email = '' + }, + + isValidEmail(email) { + const regExpEmail = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return regExpEmail.test(email) + }, + }, +}) +</script> +<style scoped> +.input-field, +.file-request-dialog__emails { + margin-top: var(--secondary-margin); +} + +.file-request-dialog__emails { + display: flex; + gap: var(--default-grid-baseline); + flex-wrap: wrap; +} +</style> |