diff options
author | nfebe <fenn25.fn@gmail.com> | 2025-04-01 19:55:56 +0100 |
---|---|---|
committer | nfebe <fenn25.fn@gmail.com> | 2025-06-24 01:34:50 +0100 |
commit | 28bc4887ff7626190c2293fe307101132bfafa0f (patch) | |
tree | 05ad0c34f5c5e322ef02c662a568a80bbfbd1f62 | |
parent | 16b98e74931db1ced578030752ef88b276baebcd (diff) | |
download | nextcloud-server-enh/share-sidebar.tar.gz nextcloud-server-enh/share-sidebar.zip |
refactor: Move pending actions to own componentenh/share-sidebar
Moved the logic to create link shares out of the the `SharingEntryLink`
component into `PendingActionsMixin.js` which is not used in the `SharingTab`
and `SharingEntryLink` as well.
-rw-r--r-- | apps/files_sharing/src/components/SharingEntryLink.vue | 143 | ||||
-rw-r--r-- | apps/files_sharing/src/logger.ts | 11 | ||||
-rw-r--r-- | apps/files_sharing/src/mixins/PendingActionsHandlersMixin.js (renamed from apps/files_sharing/src/mixins/PendingActionsHandlersMixin.ts) | 183 | ||||
-rw-r--r-- | apps/files_sharing/src/views/SharingLinkList.vue | 67 | ||||
-rw-r--r-- | apps/files_sharing/src/views/SharingTab.vue | 102 |
5 files changed, 297 insertions, 209 deletions
diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index be53bd6d685..fb32bef395d 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -185,7 +185,8 @@ import PendingActions from './PendingActions.vue' import Share from '../models/Share.ts' import SharesMixin from '../mixins/SharesMixin.js' import ShareDetails from '../mixins/ShareDetails.js' -import logger from '../logger.ts' +import PendingActionsHandlersMixin from '../mixins/PendingActionsHandlersMixin.js' +import logger from '../services/logger.ts' export default { name: 'SharingEntryLink', @@ -210,7 +211,7 @@ export default { ShareExpiryTime, }, - mixins: [SharesMixin, ShareDetails], + mixins: [SharesMixin, ShareDetails, PendingActionsHandlersMixin], props: { canReshare: { @@ -312,135 +313,22 @@ export default { } return null }, - - passwordExpirationTime() { - if (this.share.passwordExpirationTime === null) { - return null - } - - const expirationTime = moment(this.share.passwordExpirationTime) - - if (expirationTime.diff(moment()) < 0) { - return false - } - - return expirationTime.fromNow() - }, - - /** - * Is Talk enabled? - * - * @return {boolean} - */ - isTalkEnabled() { - return OC.appswebroots.spreed !== undefined - }, - /** - * Is it possible to protect the password by Talk? + * Is the current share password protected ? * * @return {boolean} */ - isPasswordProtectedByTalkAvailable() { - return this.isPasswordProtected && this.isTalkEnabled - }, - - /** - * Is the current share password protected by Talk? - * - * @return {boolean} - */ - isPasswordProtectedByTalk: { + isPasswordProtected: { get() { - return this.share.sendPasswordByTalk + return this.config.enforcePasswordForPublicLink + || !!this.share.password }, async set(enabled) { - this.share.sendPasswordByTalk = enabled + // TODO: directly save after generation to make sure the share is always protected + Vue.set(this.share, 'password', enabled ? await GeneratePassword(true) : '') + Vue.set(this.share, 'newPassword', this.share.password) }, }, - - /** - * Is the current share an email share ? - * - * @return {boolean} - */ - isEmailShareType() { - return this.share - ? this.share.type === ShareType.Email - : false - }, - - canTogglePasswordProtectedByTalkAvailable() { - if (!this.isPasswordProtected) { - // Makes no sense - return false - } else if (this.isEmailShareType && !this.hasUnsavedPassword) { - // For email shares we need a new password in order to enable or - // disable - return false - } - - // Anything else should be fine - return true - }, - - /** - * Pending data. - * If the share still doesn't have an id, it is not synced - * Therefore this is still not valid and requires user input - * - * @return {boolean} - */ - pendingDataIsMissing() { - return this.pendingPassword || this.pendingEnforcedPassword || this.pendingDefaultExpirationDate || this.pendingEnforcedExpirationDate - }, - pendingPassword() { - return this.config.enableLinkPasswordByDefault && this.isPendingShare - }, - pendingEnforcedPassword() { - return this.config.enforcePasswordForPublicLink && this.isPendingShare - }, - pendingEnforcedExpirationDate() { - return this.config.isDefaultExpireDateEnforced && this.isPendingShare - }, - pendingDefaultExpirationDate() { - return (this.config.defaultExpirationDate instanceof Date || !isNaN(new Date(this.config.defaultExpirationDate).getTime())) && this.isPendingShare - }, - isPendingShare() { - return !!(this.share && !this.share.id) - }, - sharePolicyHasEnforcedProperties() { - return this.config.enforcePasswordForPublicLink || this.config.isDefaultExpireDateEnforced - }, - - enforcedPropertiesMissing() { - // Ensure share exist and the share policy has required properties - if (!this.sharePolicyHasEnforcedProperties) { - return false - } - - if (!this.share) { - // if no share, we can't tell if properties are missing or not so we assume properties are missing - return true - } - - // If share has ID, then this is an incoming link share created from the existing link share - // Hence assume required properties - if (this.share.id) { - return true - } - // Check if either password or expiration date is missing and enforced - const isPasswordMissing = this.config.enforcePasswordForPublicLink && !this.share.password - const isExpireDateMissing = this.config.isDefaultExpireDateEnforced && !this.share.expireDate - - return isPasswordMissing || isExpireDateMissing - }, - // if newPassword exists, but is empty, it means - // the user deleted the original password - hasUnsavedPassword() { - return this.share.newPassword !== undefined - }, - /** * Return the public share link * @@ -517,6 +405,17 @@ export default { }, methods: { + _handleBeforeAddShare(share, resolve) { + this._handleShareAdded(share, resolve) + }, + _handleShareAdded(share, resolve) { + this.$emit('add:share', share, resolve) + }, + + _handleShareUpdated(share, resolve) { + this.$emit('update:share', share, resolve) + }, + /** * Check if the share requires review * diff --git a/apps/files_sharing/src/logger.ts b/apps/files_sharing/src/logger.ts deleted file mode 100644 index 31490d814e8..00000000000 --- a/apps/files_sharing/src/logger.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -import { getLoggerBuilder } from '@nextcloud/logger' - -export default getLoggerBuilder() - .setApp('files_sharing') - .detectUser() - .build()
\ No newline at end of file diff --git a/apps/files_sharing/src/mixins/PendingActionsHandlersMixin.ts b/apps/files_sharing/src/mixins/PendingActionsHandlersMixin.js index 99c16ca4005..66d715fc874 100644 --- a/apps/files_sharing/src/mixins/PendingActionsHandlersMixin.ts +++ b/apps/files_sharing/src/mixins/PendingActionsHandlersMixin.js @@ -3,37 +3,187 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { emit } from '@nextcloud/event-bus' +import moment from '@nextcloud/moment' import { ShareType } from '@nextcloud/sharing' -import GeneratePassword from '../utils/GeneratePassword' -import Share from '../models/Share' +import GeneratePassword from '../utils/GeneratePassword.ts' +import Share from '../models/Share.ts' import { showError, showSuccess } from '@nextcloud/dialogs' import { t } from '@nextcloud/l10n' -import SharesMixin from '../mixins/SharesMixin.js' +import SharesMixin from './SharesMixin.js' +import logger from '../services/logger.ts' +import Config from '../services/ConfigService.ts' +/** + * @mixin PendingActionsHandlersMixin + * + * This mixin provides the logic for handling the creation of new link shares, + * including showing a pending actions dialog and processing the share creation + * asynchronously. + * + * It follows a "template method" pattern. The main algorithm for creating a share + * is defined in `pushNewLinkShare`, but specific steps are delegated to the + * component that uses this mixin. + * + * IMPORTANT: Any component using this mixin MUST implement the following methods: + * + * - `_handleShareAdded(share, resolve)`: This method is called after a new share + * is successfully created. It is responsible for adding the new share to the + * component's state and updating the UI. + * + * - `_handleShareUpdated(share, resolve)`: This method is called when an existing + * share is updated (e.g., a new link share is created for a file that already + * had one). It should update the share in the component's state. + * + * The `resolve` function passed to both handlers MUST be called with the Vue + * component instance corresponding to the newly added/updated share. This instance + * is expected to have a `copyLink()` method, which will be called by `pushNewLinkShare` + * to copy the new share link to the clipboard. + */ export default { + data() { + return { + open: false, + config: new Config(), + shareCreationComplete: false, + pending: false, + loading: false, + defaultExpirationDateEnabled: false, + errors: {}, + logger, + } + }, computed: { + passwordExpirationTime() { + if (this.share.passwordExpirationTime === null) { + return null + } + + const expirationTime = moment(this.share.passwordExpirationTime) + + if (expirationTime.diff(moment()) < 0) { + return false + } + + return expirationTime.fromNow() + }, + /** - * Whether the share policy has enforced properties. + * Is Talk enabled? + * * @return {boolean} */ - sharePolicyHasEnforcedProperties() { - return this.config.enforcePasswordForPublicLink || this.config.isDefaultExpireDateEnforced + isTalkEnabled() { + return OC.appswebroots.spreed !== undefined }, /** - * Whether required properties are missing. + * Is it possible to protect the password by Talk? + * * @return {boolean} */ + isPasswordProtectedByTalkAvailable() { + return this.isPasswordProtected && this.isTalkEnabled + }, + + /** + * Is the current share password protected by Talk? + * + * @return {boolean} + */ + isPasswordProtectedByTalk: { + get() { + return this.share.sendPasswordByTalk + }, + async set(enabled) { + this.share.sendPasswordByTalk = enabled + }, + }, + + /** + * Is the current share an email share ? + * + * @return {boolean} + */ + isEmailShareType() { + return this.share + ? this.share.type === ShareType.Email + : false + }, + + canTogglePasswordProtectedByTalkAvailable() { + if (!this.isPasswordProtected) { + // Makes no sense + return false + } else if (this.isEmailShareType && !this.hasUnsavedPassword) { + // For email shares we need a new password in order to enable or + // disable + return false + } + + // Anything else should be fine + return true + }, + + /** + * Pending data. + * If the share still doesn't have an id, it is not synced + * Therefore this is still not valid and requires user input + * + * @return {boolean} + */ + pendingDataIsMissing() { + return this.pendingPassword || this.pendingEnforcedPassword || this.pendingDefaultExpirationDate || this.pendingEnforcedExpirationDate + }, + pendingPassword() { + return this.config.enableLinkPasswordByDefault && this.isPendingShare + }, + pendingEnforcedPassword() { + return this.config.enforcePasswordForPublicLink && this.isPendingShare + }, + pendingEnforcedExpirationDate() { + return this.config.isDefaultExpireDateEnforced && this.isPendingShare + }, + pendingDefaultExpirationDate() { + return (this.config.defaultExpirationDate instanceof Date || !isNaN(new Date(this.config.defaultExpirationDate).getTime())) && this.isPendingShare + }, + isPendingShare() { + return !!(this.share && !this.share.id) + }, + sharePolicyHasEnforcedProperties() { + return this.config.enforcePasswordForPublicLink || this.config.isDefaultExpireDateEnforced + }, + enforcedPropertiesMissing() { - if (!this.sharePolicyHasEnforcedProperties) return false - if (!this.share) return true - if (this.share.id) return true + // Ensure share exist and the share policy has required properties + if (!this.sharePolicyHasEnforcedProperties) { + return false + } + + if (!this.share) { + // if no share, we can't tell if properties are missing or not so we assume properties are missing + return true + } + // If share has ID, then this is an incoming link share created from the existing link share + // Hence assume required properties + if (this.share.id) { + return true + } + // Check if either password or expiration date is missing and enforced const isPasswordMissing = this.config.enforcePasswordForPublicLink && !this.share.password const isExpireDateMissing = this.config.isDefaultExpireDateEnforced && !this.share.expireDate return isPasswordMissing || isExpireDateMissing }, + // if newPassword exists, but is empty, it means + // the user deleted the original password + hasUnsavedPassword() { + return this.share.newPassword !== undefined + }, + /** + * Whether the share policy has enforced properties. + * @return {boolean} + */ }, mixins: [SharesMixin], @@ -46,7 +196,6 @@ export default { * @return {boolean} */ shareRequiresReview(shareReviewComplete) { - // If a user clicks 'Create share' it means they have reviewed the share if (shareReviewComplete) { return false } @@ -58,6 +207,7 @@ export default { */ async onNewLinkShare(shareReviewComplete = false) { this.logger.debug('onNewLinkShare called (with this.share)', this.share) + this.logger.debug('onNewLinkShare shareReviewComplete', shareReviewComplete) if (this.loading) return const shareDefaults = { @@ -81,7 +231,7 @@ export default { const share = new Share(shareDefaults) const component = await new Promise((resolve) => { - this.$emit('add:share', share, resolve) + this._handleBeforeAddShare(share, resolve) }) this.open = false @@ -105,7 +255,6 @@ export default { return } } - const share = new Share(shareDefaults) await this.pushNewLinkShare(share) this.shareCreationComplete = true @@ -154,14 +303,14 @@ export default { let component if (update) { component = await new Promise(resolve => { - this.$emit('update:share', newShare, resolve) + this._handleShareUpdated(newShare, resolve) }) } else { // adding new share to the array and copying link to clipboard // using promise so that we can copy link in the same click function // and avoid firefox copy permissions issue component = await new Promise(resolve => { - this.$emit('add:share', newShare, resolve) + this._handleShareAdded(newShare, resolve) }) } @@ -183,7 +332,8 @@ export default { if (!message) { showError(t('files_sharing', 'Error while creating the share')) console.error(data) - return + // throw the original error to be caught by the caller + throw data } if (message.match(/password/i)) { @@ -234,6 +384,7 @@ export default { if (!this.shareCreationComplete) { this.$emit('remove:share', this.share) } + this.open = false }, }, } diff --git a/apps/files_sharing/src/views/SharingLinkList.vue b/apps/files_sharing/src/views/SharingLinkList.vue index 3dd6fdf317b..dd20033b33a 100644 --- a/apps/files_sharing/src/views/SharingLinkList.vue +++ b/apps/files_sharing/src/views/SharingLinkList.vue @@ -9,22 +9,26 @@ class="sharing-link-list"> <!-- If no link shares, show the add link default entry --> <SharingEntryLink v-if="!hasLinkShares && canReshare" + ref="defaultShareEntryRef" :can-reshare="canReshare" :file-info="fileInfo" - @add:share="addShare" /> + @add:share="(share, resolve) => $emit('add:share', share, resolve)" + @update:share="(share, resolve) => $emit('update:share', share, resolve)" + @open-sharing-details="openSharingDetails(share)" /> <!-- Else we display the list --> <template v-if="hasShares"> <!-- using shares[index] to work with .sync --> <SharingEntryLink v-for="(share, index) in shares" + :ref="(el) => { if (el && share && share.id) shareEntryRefs[share.id] = el }" :key="share.id" :index="shares.length > 1 ? index + 1 : null" :can-reshare="canReshare" - :share.sync="shares[index]" + :share="share" :file-info="fileInfo" - @add:share="addShare(...arguments)" - @update:share="awaitForShare(...arguments)" - @remove:share="removeShare" + @add:share="(share, resolve) => $emit('add:share', share, resolve)" + @update:share="(share, resolve) => $emit('update:share', share, resolve)" + @remove:share="(share) => $emit('remove:share', share)" @open-sharing-details="openSharingDetails(share)" /> </template> </ul> @@ -35,7 +39,6 @@ import { getCapabilities } from '@nextcloud/capabilities' import { t } from '@nextcloud/l10n' -import Share from '../models/Share.js' import SharingEntryLink from '../components/SharingEntryLink.vue' import ShareDetails from '../mixins/ShareDetails.js' import { ShareType } from '@nextcloud/sharing' @@ -69,6 +72,7 @@ export default { data() { return { canLinkShare: getCapabilities().files_sharing.public.enabled, + shareEntryRefs: {}, } }, @@ -94,48 +98,19 @@ export default { }, }, + beforeUpdate() { + // Clear refs before each update to ensure they are current + this.shareEntryRefs = {} + }, + methods: { t, - - /** - * Add a new share into the link shares list - * and return the newly created share component - * - * @param {Share} share the share to add to the array - * @param {Function} resolve a function to run after the share is added and its component initialized - */ - addShare(share, resolve) { - // eslint-disable-next-line vue/no-mutating-props - this.shares.push(share) - this.awaitForShare(share, resolve) - }, - - /** - * Await for next tick and render after the list updated - * Then resolve with the matched vue component of the - * provided share object - * - * @param {Share} share newly created share - * @param {Function} resolve a function to execute after - */ - awaitForShare(share, resolve) { - this.$nextTick(() => { - const newShare = this.$children.find(component => component.share === share) - if (newShare) { - resolve(newShare) - } - }) - }, - - /** - * Remove a share from the shares list - * - * @param {Share} share the share to remove - */ - removeShare(share) { - const index = this.shares.findIndex(item => item === share) - // eslint-disable-next-line vue/no-mutating-props - this.shares.splice(index, 1) + getShareEntryComponent(shareId) { + if (shareId) { + return this.shareEntryRefs[shareId] + } + // For the case when a new share is added and it's the first one (default entry was shown) + return this.$refs.defaultShareEntryRef }, }, } diff --git a/apps/files_sharing/src/views/SharingTab.vue b/apps/files_sharing/src/views/SharingTab.vue index d709084e83f..90057885372 100644 --- a/apps/files_sharing/src/views/SharingTab.vue +++ b/apps/files_sharing/src/views/SharingTab.vue @@ -16,7 +16,9 @@ class="sharingTab__content"> <!-- shared with me information --> <ul v-if="isSharedWithMe"> - <SharingEntrySimple v-bind="sharedWithMe" class="sharing-entry__reshare"> + <SharingEntrySimple v-bind="sharedWithMe" + :title="sharedWithMe.name" + class="sharing-entry__reshare"> <template #avatar> <NcAvatar :user="sharedWithMe.user" :display-name="sharedWithMe.displayName" @@ -54,7 +56,7 @@ @open-sharing-details="toggleShareDetailsView" /> <!-- other shares list --> - <SharingList v-if="!loading" + <SharingList v-if="shares.length > 0" ref="shareList" :shares="shares" :file-info="fileInfo" @@ -95,22 +97,48 @@ :shares="shares" @open-sharing-details="toggleShareDetailsView" /> <!-- Non link external shares list --> - <SharingList v-if="!loading" + <SharingList v-if="externalShares.length > 0" :shares="externalShares" :file-info="fileInfo" @open-sharing-details="toggleShareDetailsView" /> <!-- link shares list --> - <SharingLinkList v-if="!loading && isLinkSharingAllowed" + <SharingLinkList v-if="isLinkSharingAllowed" ref="linkShareList" :can-reshare="canReshare" :file-info="fileInfo" :shares="linkShares" - @open-sharing-details="toggleShareDetailsView" /> + @open-sharing-details="toggleShareDetailsView" + @add:share="addShare" + @update:share="updateShare" + @remove:share="removeShare" /> + <!-- pending actions --> + <PendingActions v-if="open" + :open.sync="open" + :share="share" + :config="config" + :errors="errors" + :pending-password="pendingPassword" + :pending-enforced-password="pendingEnforcedPassword" + :pending-default-expiration-date="pendingDefaultExpirationDate" + :pending-enforced-expiration-date="pendingEnforcedExpirationDate" + :default-expiration-date-enabled="defaultExpirationDateEnabled" + :saving="saving" + :is-password-policy-enabled="isPasswordPolicyEnabled" + :date-tomorrow="dateTomorrow" + :max-expiration-date-enforced="maxExpirationDateEnforced" + :actions-tooltip="actionsTooltip" + :is-password-protected="isPasswordProtected" + @new-link-share="onNewLinkShare(true)" + @cancel="onCancel" + @password-disable="onPasswordDisable" + @update:isPasswordProtected="onPasswordProtectedChange" + @update:defaultExpirationDateEnabled="onExpirationDateToggleChange" + @expiration-date-changed="expirationDateChanged" /> <!-- Create new share --> <NcButton v-if="canReshare" class="new-link-share" :disabled="loading" - @click.prevent.stop="onNewLinkShare"> + @click.prevent.stop="onNewLinkShare(false)"> {{ t('files_sharing', 'Create a link share') }} <template #icon> <LoadingIcon v-if="loading" :size="20" /> @@ -190,6 +218,7 @@ import Share from '../models/Share.ts' import SharingEntryInternal from '../components/SharingEntryInternal.vue' import SharingEntrySimple from '../components/SharingEntrySimple.vue' import SharingInput from '../components/SharingInput.vue' +import PendingActions from '../components/PendingActions.vue' import SharingInherited from './SharingInherited.vue' import SharingLinkList from './SharingLinkList.vue' @@ -201,8 +230,7 @@ import logger from '../services/logger.ts' import LinkIcon from 'vue-material-design-icons/Link.vue' import LoadingIcon from 'vue-material-design-icons/Loading.vue' -import PendingActionsHandlersMixin from '../mixins/PendingActionsHandlersMixin.ts' -import logger from '../logger.ts' +import PendingActionsHandlersMixin from '../mixins/PendingActionsHandlersMixin.js' export default { name: 'SharingTab', @@ -222,6 +250,7 @@ export default { SharingLinkList, SharingList, SharingDetailsTab, + PendingActions, }, mixins: [ShareDetails, PendingActionsHandlersMixin], @@ -240,6 +269,7 @@ export default { sharedWithMe: {}, shares: [], linkShares: [], + externalShares: [], logger, sections: OCA.Sharing.ShareTabSections.getSections(), @@ -247,6 +277,8 @@ export default { showSharingDetailsView: false, shareDetailsData: {}, returnFocusElement: null, + pendingActionResolve: null, + defaultExpirationDateEnabled: false, internalSharesHelpText: t('files_sharing', 'Use this method to share files with individuals or teams within your organization. If the recipient already has access to the share but cannot locate it, you can send them the internal share link for easy access.'), externalSharesHelpText: t('files_sharing', 'Use this method to share files with individuals or organizations outside your organization. Files and folders can be shared via public share links and email addresses. You can also share to other Nextcloud accounts hosted on different instances using their federated cloud ID.'), @@ -301,8 +333,24 @@ export default { : t('files_sharing', 'Email, federated cloud ID') }, }, + beforeMount() { + this.defaultExpirationDateEnabled = this.config.isDefaultExpireDateEnabled + }, methods: { + _handleBeforeAddShare(share, resolve) { + this.share = share + this.open = true + this.pendingActionResolve = resolve + }, + _handleShareAdded(share, resolve) { + this.addShare(share, resolve) + }, + + _handleShareUpdated(share, resolve) { + this.updateShare(share, resolve) + }, + /** * Update current fileInfo and fetch new data * @@ -371,6 +419,7 @@ export default { this.sharedWithMe = {} this.shares = [] this.linkShares = [] + this.externalShares = [] this.showSharingDetailsView = false this.shareDetailsData = {} }, @@ -489,9 +538,7 @@ export default { * @param {Function} [resolve] a function to run after the share is added and its component initialized */ addShare(share, resolve = () => { }) { - // only catching share type MAIL as link shares are added differently - // meaning: not from the ShareInput - if (share.type === ShareType.Email) { + if ([ShareType.Link, ShareType.Email].includes(share.type)) { this.linkShares.unshift(share) } else if ([ShareType.Remote, ShareType.RemoteGroup].includes(share.type)) { if (this.config.showFederatedSharesAsInternal) { @@ -504,6 +551,34 @@ export default { } this.awaitForShare(share, resolve) }, + + /** + * Update a share in the shares list + * and return the newly created share component + * + * @param {Share} share the share to update in the array + * @param {Function} [resolve] a function to run after the share is updated and its component initialized + */ + updateShare(share, resolve = () => {}) { + let shareList + if ([ShareType.Link, ShareType.Email].includes(share.type)) { + shareList = this.linkShares + } else if ([ShareType.Remote, ShareType.RemoteGroup].includes(share.type)) { + if (this.config.showFederatedSharesAsInternal) { + shareList = this.shares + } else { + shareList = this.externalShares + } + } else { + shareList = this.shares + } + const index = shareList.findIndex(item => item.id === share.id) + if (index !== -1) { + this.$set(shareList, index, share) + } + this.awaitForShare(share, resolve) + }, + /** * Remove a share from the shares list * @@ -532,9 +607,8 @@ export default { awaitForShare(share, resolve) { this.$nextTick(() => { let listComponent = this.$refs.shareList - // Only mail shares comes from the input, link shares - // are managed internally in the SharingLinkList component - if (share.type === ShareType.Email) { + // Link and mail shares are managed in the SharingLinkList component + if ([ShareType.Link, ShareType.Email].includes(share.type)) { listComponent = this.$refs.linkShareList } const newShare = listComponent.$children.find(component => component.share === share) |