aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/actions/deleteUtils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/actions/deleteUtils.ts')
-rw-r--r--apps/files/src/actions/deleteUtils.ts141
1 files changed, 141 insertions, 0 deletions
diff --git a/apps/files/src/actions/deleteUtils.ts b/apps/files/src/actions/deleteUtils.ts
new file mode 100644
index 00000000000..1ca7859b6c5
--- /dev/null
+++ b/apps/files/src/actions/deleteUtils.ts
@@ -0,0 +1,141 @@
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import type { Capabilities } from '../types'
+import type { Node, View } from '@nextcloud/files'
+
+import { emit } from '@nextcloud/event-bus'
+import { FileType } from '@nextcloud/files'
+import { getCapabilities } from '@nextcloud/capabilities'
+import { n, t } from '@nextcloud/l10n'
+import axios from '@nextcloud/axios'
+import { useUserConfigStore } from '../store/userconfig'
+import { getPinia } from '../store'
+
+export const isTrashbinEnabled = () => (getCapabilities() as Capabilities)?.files?.undelete === true
+
+export const canUnshareOnly = (nodes: Node[]) => {
+ return nodes.every(node => node.attributes['is-mount-root'] === true
+ && node.attributes['mount-type'] === 'shared')
+}
+
+export const canDisconnectOnly = (nodes: Node[]) => {
+ return nodes.every(node => node.attributes['is-mount-root'] === true
+ && node.attributes['mount-type'] === 'external')
+}
+
+export const isMixedUnshareAndDelete = (nodes: Node[]) => {
+ if (nodes.length === 1) {
+ return false
+ }
+
+ const hasSharedItems = nodes.some(node => canUnshareOnly([node]))
+ const hasDeleteItems = nodes.some(node => !canUnshareOnly([node]))
+ return hasSharedItems && hasDeleteItems
+}
+
+export const isAllFiles = (nodes: Node[]) => {
+ return !nodes.some(node => node.type !== FileType.File)
+}
+
+export const isAllFolders = (nodes: Node[]) => {
+ return !nodes.some(node => node.type !== FileType.Folder)
+}
+
+export const displayName = (nodes: Node[], view: View) => {
+ /**
+ * If those nodes are all the root node of a
+ * share, we can only unshare them.
+ */
+ if (canUnshareOnly(nodes)) {
+ if (nodes.length === 1) {
+ return t('files', 'Leave this share')
+ }
+ return t('files', 'Leave these shares')
+ }
+
+ /**
+ * If those nodes are all the root node of an
+ * external storage, we can only disconnect it.
+ */
+ if (canDisconnectOnly(nodes)) {
+ if (nodes.length === 1) {
+ return t('files', 'Disconnect storage')
+ }
+ return t('files', 'Disconnect storages')
+ }
+
+ /**
+ * If we're in the trashbin, we can only delete permanently
+ */
+ if (view.id === 'trashbin' || !isTrashbinEnabled()) {
+ return t('files', 'Delete permanently')
+ }
+
+ /**
+ * If we're in the sharing view, we can only unshare
+ */
+ if (isMixedUnshareAndDelete(nodes)) {
+ return t('files', 'Delete and unshare')
+ }
+
+ /**
+ * If we're only selecting files, use proper wording
+ */
+ if (isAllFiles(nodes)) {
+ if (nodes.length === 1) {
+ return t('files', 'Delete file')
+ }
+ return t('files', 'Delete files')
+ }
+
+ /**
+ * If we're only selecting folders, use proper wording
+ */
+ if (isAllFolders(nodes)) {
+ if (nodes.length === 1) {
+ return t('files', 'Delete folder')
+ }
+ return t('files', 'Delete folders')
+ }
+
+ return t('files', 'Delete')
+}
+
+export const shouldAskForConfirmation = () => {
+ const userConfig = useUserConfigStore(getPinia())
+ return userConfig.userConfig.show_dialog_deletion !== false
+}
+
+export const askConfirmation = async (nodes: Node[], view: View) => {
+ const message = view.id === 'trashbin' || !isTrashbinEnabled()
+ ? n('files', 'You are about to permanently delete {count} item', 'You are about to permanently delete {count} items', nodes.length, { count: nodes.length })
+ : n('files', 'You are about to delete {count} item', 'You are about to delete {count} items', nodes.length, { count: nodes.length })
+
+ return new Promise<boolean>(resolve => {
+ // TODO: Use the new dialog API
+ window.OC.dialogs.confirmDestructive(
+ message,
+ t('files', 'Confirm deletion'),
+ {
+ type: window.OC.dialogs.YES_NO_BUTTONS,
+ confirm: displayName(nodes, view),
+ confirmClasses: 'error',
+ cancel: t('files', 'Cancel'),
+ },
+ (decision: boolean) => {
+ resolve(decision)
+ },
+ )
+ })
+}
+
+export const deleteNode = async (node: Node) => {
+ await axios.delete(node.encodedSource)
+
+ // Let's delete even if it's moved to the trashbin
+ // since it has been removed from the current view
+ // and changing the view will trigger a reload anyway.
+ emit('files:node:deleted', node)
+}