Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/files/lib/Service/UserConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class UserConfig {
'default' => false,
'allowed' => [true, false],
],
[
// Whether to show the "confirm file deletion" warning
'key' => 'show_dialog_deletion',
'default' => false,
'allowed' => [true, false],
],
[
// Whether to show the "confirm file extension change" warning
'key' => 'show_dialog_file_extension',
Expand Down
20 changes: 14 additions & 6 deletions apps/files/src/actions/deleteAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as eventBus from '@nextcloud/event-bus'

import { action } from './deleteAction'
import logger from '../logger'
import { shouldAskForConfirmation } from './deleteUtils'

vi.mock('@nextcloud/auth')
vi.mock('@nextcloud/axios')
Expand Down Expand Up @@ -235,7 +236,6 @@ describe('Delete action execute tests', () => {
vi.spyOn(eventBus, 'emit')

const confirmMock = vi.fn()
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }

const file1 = new File({
Expand Down Expand Up @@ -275,7 +275,6 @@ describe('Delete action execute tests', () => {

// Emulate the confirmation dialog to always confirm
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(true))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }

const file1 = new File({
Expand Down Expand Up @@ -339,7 +338,11 @@ describe('Delete action execute tests', () => {
expect(eventBus.emit).toHaveBeenNthCalledWith(5, 'files:node:deleted', file5)
})

test('Delete action batch trashbin disabled', async () => {
test('Delete action batch dialog enabled', async () => {
// Enable the confirmation dialog
eventBus.emit('files:config:updated', { key: 'show_dialog_deletion', value: true })
expect(shouldAskForConfirmation()).toBe(true)

vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
vi.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
Expand All @@ -350,7 +353,6 @@ describe('Delete action execute tests', () => {

// Emulate the confirmation dialog to always confirm
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(true))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }

const file1 = new File({
Expand Down Expand Up @@ -382,6 +384,8 @@ describe('Delete action execute tests', () => {
expect(eventBus.emit).toBeCalledTimes(2)
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file1)
expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:node:deleted', file2)

eventBus.emit('files:config:updated', { key: 'show_dialog_deletion', value: false })
})

test('Delete fails', async () => {
Expand All @@ -407,7 +411,10 @@ describe('Delete action execute tests', () => {
expect(logger.error).toBeCalledTimes(1)
})

test('Delete is cancelled', async () => {
test('Delete is cancelled with dialog enabled', async () => {
// Enable the confirmation dialog
eventBus.emit('files:config:updated', { key: 'show_dialog_deletion', value: true })

vi.spyOn(axios, 'delete')
vi.spyOn(eventBus, 'emit')
vi.spyOn(capabilities, 'getCapabilities').mockImplementation(() => {
Expand All @@ -418,7 +425,6 @@ describe('Delete action execute tests', () => {

// Emulate the confirmation dialog to always confirm
const confirmMock = vi.fn().mockImplementation((a, b, c, resolve) => resolve(false))
// @ts-expect-error We only mock what needed
window.OC = { dialogs: { confirmDestructive: confirmMock } }

const file1 = new File({
Expand All @@ -437,5 +443,7 @@ describe('Delete action execute tests', () => {
expect(axios.delete).toBeCalledTimes(0)

expect(eventBus.emit).toBeCalledTimes(0)

eventBus.emit('files:config:updated', { key: 'show_dialog_deletion', value: false })
})
})
8 changes: 3 additions & 5 deletions apps/files/src/actions/deleteAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import NetworkOffSvg from '@mdi/svg/svg/network-off.svg?raw'
import TrashCanSvg from '@mdi/svg/svg/trash-can.svg?raw'

import { TRASHBIN_VIEW_ID } from '../../../files_trashbin/src/files_views/trashbinView.ts'
import { askConfirmation, canDisconnectOnly, canUnshareOnly, deleteNode, displayName, isTrashbinEnabled } from './deleteUtils.ts'
import { askConfirmation, canDisconnectOnly, canUnshareOnly, deleteNode, displayName, shouldAskForConfirmation } from './deleteUtils.ts'
import logger from '../logger.ts'

const queue = new PQueue({ concurrency: 5 })
Expand Down Expand Up @@ -58,8 +58,7 @@ export const action = new FileAction({
const callStack = new Error().stack || ''
const isCalledFromEventListener = callStack.toLocaleLowerCase().includes('keydown')

// If trashbin is disabled, we need to ask for confirmation
if (!isTrashbinEnabled() || isCalledFromEventListener) {
if (shouldAskForConfirmation() || isCalledFromEventListener) {
confirm = await askConfirmation([node], view)
}

Expand All @@ -81,8 +80,7 @@ export const action = new FileAction({
async execBatch(nodes: Node[], view: View): Promise<(boolean | null)[]> {
let confirm = true

// If trashbin is disabled, we need to ask for confirmation
if (!isTrashbinEnabled()) {
if (shouldAskForConfirmation()) {
confirm = await askConfirmation(nodes, view)
} else if (nodes.length >= 5 && !canUnshareOnly(nodes) && !canDisconnectOnly(nodes)) {
confirm = await askConfirmation(nodes, view)
Expand Down
7 changes: 7 additions & 0 deletions apps/files/src/actions/deleteUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ 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

Expand Down Expand Up @@ -101,6 +103,11 @@ export const displayName = (nodes: Node[], view: View) => {
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 })
Expand Down
1 change: 1 addition & 0 deletions apps/files/src/store/userconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const initialUserConfig = loadState<UserConfig>('files', 'config', {
sort_favorites_first: true,
sort_folders_first: true,

show_dialog_deletion: false,
show_dialog_file_extension: true,
})

Expand Down
4 changes: 3 additions & 1 deletion apps/files/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ export interface UserConfig {
crop_image_previews: boolean
default_view: 'files' | 'personal'
grid_view: boolean
show_dialog_file_extension: boolean,
show_hidden: boolean
show_mime_column: boolean
sort_favorites_first: boolean
sort_folders_first: boolean

show_dialog_deletion: boolean
show_dialog_file_extension: boolean,
}

export interface UserConfigStore {
Expand Down
5 changes: 5 additions & 0 deletions apps/files/src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
@update:checked="setConfig('show_dialog_file_extension', $event)">
{{ t('files', 'Show a warning dialog when changing a file extension.') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch type="switch"
:checked="userConfig.show_dialog_deletion"
@update:checked="setConfig('show_dialog_deletion', $event)">
{{ t('files', 'Show a warning dialog when deleting files.') }}
</NcCheckboxRadioSwitch>
</NcAppSettingsSection>

<NcAppSettingsSection id="shortcuts"
Expand Down
4 changes: 2 additions & 2 deletions dist/files-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-init.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-main.js.map

Large diffs are not rendered by default.

Loading