66 <form class =" share-panel" @submit.prevent >
77 <!-- First page view -->
88 <template v-if =" ! inSettings " >
9- <!-- Share type is committed once a recipient (or the link) exists -->
109 <NcRadioGroup
11- v-if =" ! hasRecipients "
1210 class="share-panel__tab-bar"
1311 :modelValue =" shareDialogTab "
1412 :label =" t (' Share type' )"
1513 :hideLabel =" true "
16- @update :modelValue =" shareDialogTab = ($event as ShareDialogTab )" >
14+ @update :modelValue =" onTabChange ($event as ShareDialogTab )" >
1715 <NcRadioGroupButton
1816 v-for =" type in shareTypes "
1917 :key =" type .id "
@@ -171,6 +169,7 @@ import AccountPlusOutlineIconSvg from '@mdi/svg/svg/account-plus-outline.svg?raw
171169import IconContentCopy from ' @mdi/svg/svg/content-copy.svg?raw'
172170import IconSend from ' @mdi/svg/svg/send-outline.svg?raw'
173171import WorldMapOutlineSvg from ' @mdi/svg/svg/web.svg?raw'
172+ import { DialogBuilder } from ' @nextcloud/dialogs'
174173import { computed , ref , watch } from ' vue'
175174import NcButton from ' @nextcloud/vue/components/NcButton'
176175import NcIconSvgWrapper from ' @nextcloud/vue/components/NcIconSvgWrapper'
@@ -187,10 +186,10 @@ import { useLinkShare } from '../composables/useLinkShare.ts'
187186import { usePermissionPresets } from ' ../composables/usePermissionPresets.ts'
188187import { useRecipientSearch } from ' ../composables/useRecipientSearch.ts'
189188import { useShareProperties } from ' ../composables/useShareProperties.ts'
190- import { PROPERTY_EXPIRATION , PROPERTY_PASSWORD } from ' ../constants.ts'
189+ import { PROPERTY_EXPIRATION , PROPERTY_PASSWORD , RECIPIENT_TYPE_TOKEN } from ' ../constants.ts'
191190import { ShareDialogTab } from ' ../types/ui.ts'
192191import { getOcsErrorMessage } from ' ../utils/api.ts'
193- import { t } from ' ../utils/l10n.ts'
192+ import { n , t } from ' ../utils/l10n.ts'
194193import { logger } from ' ../utils/logger.ts'
195194import { isLongTextProperty , isOptionalProperty } from ' ../utils/property.ts'
196195import { shareOutcomeSummary } from ' ../utils/summary.ts'
@@ -218,8 +217,70 @@ const isLinkShare = computed(() => shareDialogTab.value === ShareDialogTab.Anyon
218217// A share cannot be submitted without at least one recipient.
219218const canSubmit = computed (() => props .share .recipients .length > 0 )
220219
221- // Once a recipient (or the link) exists, the share type is committed.
222- const hasRecipients = computed (() => props .share .recipients .length > 0 )
220+ // Invited people: every recipient except the link (token) one.
221+ const invitedRecipients = computed (() => props .share .recipients .filter ((recipient ) => recipient .class !== RECIPIENT_TYPE_TOKEN ))
222+
223+ /**
224+ * Ask before dropping the invited people when switching to a public link.
225+ *
226+ * @param count Number of invited people that would be removed
227+ */
228+ async function confirmDropInvited(count : number ): Promise <boolean > {
229+ let confirmed = false
230+ const dialog = (new DialogBuilder ())
231+ .setName (t (' Share with anyone' ))
232+ .setText (n (
233+ ' Switching to a public link removes %n invited person from this share.' ,
234+ ' Switching to a public link removes %n invited people from this share.' ,
235+ count ,
236+ ))
237+ .setButtons ([
238+ {
239+ label: t (' Cancel' ),
240+ variant: ' secondary' ,
241+ callback : () => {},
242+ },
243+ {
244+ label: t (' Continue' ),
245+ variant: ' primary' ,
246+ callback : () => {
247+ confirmed = true
248+ },
249+ },
250+ ])
251+ .build ()
252+ try {
253+ await dialog .show ()
254+ } catch (e ) {
255+ logger .debug (' Share type confirmation dialog closed' , { error: e })
256+ }
257+ return confirmed
258+ }
259+
260+ /**
261+ * Switch the share type. A public link cannot keep invited people, so confirm
262+ * and remove them first.
263+ *
264+ * @param tab The tab to switch to
265+ */
266+ async function onTabChange(tab : ShareDialogTab ) {
267+ if (tab === shareDialogTab .value ) {
268+ return
269+ }
270+ if (tab === ShareDialogTab .Anyone && invitedRecipients .value .length > 0 ) {
271+ if (! await confirmDropInvited (invitedRecipients .value .length )) {
272+ return
273+ }
274+ for (const recipient of invitedRecipients .value ) {
275+ try {
276+ await props .share .removeRecipient (recipient .class , recipient .value , recipient .instance ?? undefined )
277+ } catch (e ) {
278+ logger .error (' Failed to remove recipient while switching to a link share' , { error: e , recipient: recipient .value })
279+ }
280+ }
281+ }
282+ shareDialogTab .value = tab
283+ }
223284
224285// Editable properties, permissions/presets, recipient search and link handling
225286// live in dedicated composables; this component wires them to the template.
0 commit comments