Skip to content

Commit 76e5ba8

Browse files
authored
Merge pull request #56439 from nextcloud/refactor/files_trashbin-vue3
refactor(files_trashbin): migrate app to Vue 3
2 parents f2f9cb7 + 5daeee2 commit 76e5ba8

File tree

113 files changed

+429
-1113
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+429
-1113
lines changed

apps/files/src/actions/deleteAction.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5+
56
import type { Node, View } from '@nextcloud/files'
67

78
import CloseSvg from '@mdi/svg/svg/close.svg?raw'
@@ -11,10 +12,13 @@ import { FileAction, Permission } from '@nextcloud/files'
1112
import { loadState } from '@nextcloud/initial-state'
1213
import { t } from '@nextcloud/l10n'
1314
import PQueue from 'p-queue'
14-
import { TRASHBIN_VIEW_ID } from '../../../files_trashbin/src/files_views/trashbinView.ts'
1515
import logger from '../logger.ts'
1616
import { askConfirmation, canDisconnectOnly, canUnshareOnly, deleteNode, displayName, shouldAskForConfirmation } from './deleteUtils.ts'
1717

18+
// TODO: once the files app is migrated to the new frontend use the import instead:
19+
// import { TRASHBIN_VIEW_ID } from '../../../files_trashbin/src/files_views/trashbinView.ts'
20+
const TRASHBIN_VIEW_ID = 'trashbin'
21+
1822
const queue = new PQueue({ concurrency: 5 })
1923

2024
export const ACTION_DELETE = 'delete'

apps/files_sharing/src/files_filters/AccountFilter.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import { ShareType } from '@nextcloud/sharing'
1111
import { isPublicShare } from '@nextcloud/sharing/public'
1212
import Vue from 'vue'
1313
import FileListFilterAccount from '../components/FileListFilterAccount.vue'
14-
import { TRASHBIN_VIEW_ID } from '../../../files_trashbin/src/files_views/trashbinView.ts'
14+
15+
// once files_sharing is migrated to the new frontend use the import instead:
16+
// import { TRASHBIN_VIEW_ID } from '../../../files_trashbin/src/files_views/trashbinView.ts'
17+
const TRASHBIN_VIEW_ID = 'trashbin'
1518

1619
export interface IAccountData {
1720
uid: string

apps/files_trashbin/src/files_actions/restoreAction.spec.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@ import * as ncEventBus from '@nextcloud/event-bus'
77
import { Folder } from '@nextcloud/files'
88
import isSvg from 'is-svg'
99
import { beforeEach, describe, expect, it, vi } from 'vitest'
10-
import { PERMISSION_ALL, PERMISSION_NONE } from '../../../../core/src/OC/constants.js'
1110
import { trashbinView } from '../files_views/trashbinView.ts'
1211
import { restoreAction } from './restoreAction.ts'
1312

13+
// TODO: once core is migrated to the new frontend use the import instead:
14+
// import { PERMISSION_ALL, PERMISSION_NONE } from '../../../../core/src/OC/constants.js'
15+
export const PERMISSION_NONE = 0
16+
export const PERMISSION_ALL = 31
17+
1418
const axiosMock = vi.hoisted(() => ({
1519
request: vi.fn(),
1620
}))
17-
vi.mock('@nextcloud/axios', () => ({ default: axiosMock }))
21+
vi.mock('@nextcloud/axios', async (origial) => ({ ...(await origial()), default: axiosMock }))
1822
vi.mock('@nextcloud/auth')
1923

24+
const errorSpy = vi.spyOn(window.console, 'error').mockImplementation(() => {})
25+
beforeEach(() => errorSpy.mockClear())
26+
2027
describe('files_trashbin: file actions - restore action', () => {
2128
it('has id set', () => {
2229
expect(restoreAction.id).toBe('restore')
@@ -99,9 +106,9 @@ describe('files_trashbin: file actions - restore action', () => {
99106

100107
expect(await restoreAction.exec(node, trashbinView, '/')).toBe(true)
101108
expect(axiosMock.request).toBeCalled()
102-
expect(axiosMock.request.mock.calls[0][0].method).toBe('MOVE')
103-
expect(axiosMock.request.mock.calls[0][0].url).toBe(node.encodedSource)
104-
expect(axiosMock.request.mock.calls[0][0].headers.destination).toContain('/restore/')
109+
expect(axiosMock.request.mock.calls[0]![0].method).toBe('MOVE')
110+
expect(axiosMock.request.mock.calls[0]![0].url).toBe(node.encodedSource)
111+
expect(axiosMock.request.mock.calls[0]![0].headers.destination).toContain('/restore/')
105112
})
106113

107114
it('deletes node from current view after successfull request', async () => {
@@ -115,7 +122,7 @@ describe('files_trashbin: file actions - restore action', () => {
115122
expect(emitSpy).toBeCalledWith('files:node:deleted', node)
116123
})
117124

118-
it('does not delete node from view if reuest failed', async () => {
125+
it('does not delete node from view if request failed', async () => {
119126
const node = new Folder({ owner: 'test', source: 'https://example.com/remote.php/dav/trashbin/test/folder', root: '/trashbin/test/', permissions: PERMISSION_ALL })
120127

121128
axiosMock.request.mockImplementationOnce(() => {
@@ -126,6 +133,7 @@ describe('files_trashbin: file actions - restore action', () => {
126133
expect(await restoreAction.exec(node, trashbinView, '/')).toBe(false)
127134
expect(axiosMock.request).toBeCalled()
128135
expect(emitSpy).not.toBeCalled()
136+
expect(errorSpy).toBeCalled()
129137
})
130138

131139
it('batch: only returns success if all requests worked', async () => {
@@ -143,6 +151,7 @@ describe('files_trashbin: file actions - restore action', () => {
143151
})
144152
expect(await restoreAction.execBatch!([node, node], trashbinView, '/')).toStrictEqual([false, true])
145153
expect(axiosMock.request).toBeCalledTimes(2)
154+
expect(errorSpy).toBeCalled()
146155
})
147156
})
148157
})

apps/files_trashbin/src/files_actions/restoreAction.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
import type { Node, View } from '@nextcloud/files'
2-
3-
import svgHistory from '@mdi/svg/svg/history.svg?raw'
4-
/**
1+
/*!
52
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
63
* SPDX-License-Identifier: AGPL-3.0-or-later
74
*/
5+
6+
import type { Node, View } from '@nextcloud/files'
7+
8+
import svgHistory from '@mdi/svg/svg/history.svg?raw'
89
import { getCurrentUser } from '@nextcloud/auth'
9-
import axios from '@nextcloud/axios'
10+
import axios, { isAxiosError } from '@nextcloud/axios'
1011
import { showError } from '@nextcloud/dialogs'
1112
import { emit } from '@nextcloud/event-bus'
1213
import { FileAction, Permission } from '@nextcloud/files'
1314
import { t } from '@nextcloud/l10n'
1415
import { encodePath } from '@nextcloud/paths'
1516
import { generateRemoteUrl } from '@nextcloud/router'
16-
import logger from '../../../files/src/logger.ts'
1717
import { TRASHBIN_VIEW_ID } from '../files_views/trashbinView.ts'
18+
import { logger } from '../logger.ts'
1819

1920
export const restoreAction = new FileAction({
2021
id: 'restore',
@@ -54,7 +55,7 @@ export const restoreAction = new FileAction({
5455
emit('files:node:deleted', node)
5556
return true
5657
} catch (error) {
57-
if (error.response?.status === 507) {
58+
if (isAxiosError(error) && error.response?.status === 507) {
5859
showError(t('files_trashbin', 'Not enough free space to restore the file/folder'))
5960
}
6061
logger.error('Failed to restore node', { error, node })

apps/files_trashbin/src/files_listActions/emptyTrashAction.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
126126

127127
dialogBuilder.build.mockImplementationOnce(() => ({
128128
show: async () => {
129-
const buttons = dialogBuilder.setButtons.mock.calls[0][0]
129+
const buttons = dialogBuilder.setButtons.mock.calls[0]![0]
130130
const cancel = buttons.find(({ label }) => label === 'Cancel')
131131
await cancel.callback()
132132
},
@@ -142,7 +142,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
142142

143143
dialogBuilder.build.mockImplementationOnce(() => ({
144144
show: async () => {
145-
const buttons = dialogBuilder.setButtons.mock.calls[0][0]
145+
const buttons = dialogBuilder.setButtons.mock.calls[0]![0]
146146
const cancel = buttons.find(({ label }) => label === 'Empty deleted files')
147147
await cancel.callback()
148148
},
@@ -160,7 +160,7 @@ describe('files_trashbin: file list actions - empty trashbin', () => {
160160

161161
dialogBuilder.build.mockImplementationOnce(() => ({
162162
show: async () => {
163-
const buttons = dialogBuilder.setButtons.mock.calls[0][0]
163+
const buttons = dialogBuilder.setButtons.mock.calls[0]![0]
164164
const cancel = buttons.find(({ label }) => label === 'Empty deleted files')
165165
await cancel.callback()
166166
},

apps/files_trashbin/src/files_views/columns.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,14 @@ describe('files_trashbin: file list columns', () => {
180180
const node = new File({ owner: 'test', source: 'https://example.com/remote.php/dav/files/test/a.txt', mime: 'text/plain', attributes: { 'trashbin-deleted-by-id': 'user-id' } })
181181
const el: HTMLElement = deletedBy.render(node, trashbinView)
182182
expect(el).toBeInstanceOf(HTMLElement)
183-
expect(el.textContent).toMatch(/\suser-id\s/)
183+
expect(el.textContent.trim()).toBe('user-id')
184184
})
185185

186186
it('renders a node with deleting user display name', () => {
187187
const node = new File({ owner: 'test', source: 'https://example.com/remote.php/dav/files/test/a.txt', mime: 'text/plain', attributes: { 'trashbin-deleted-by-display-name': 'user-name', 'trashbin-deleted-by-id': 'user-id' } })
188188
const el: HTMLElement = deletedBy.render(node, trashbinView)
189189
expect(el).toBeInstanceOf(HTMLElement)
190-
expect(el.textContent).toMatch(/\suser-name\s/)
190+
expect(el.textContent.trim()).toBe('user-name')
191191
})
192192

193193
it('renders a node even when information is missing', () => {

apps/files_trashbin/src/files_views/columns.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { getCurrentUser } from '@nextcloud/auth'
99
import { Column } from '@nextcloud/files'
1010
import { formatRelativeTime, getCanonicalLocale, getLanguage, t } from '@nextcloud/l10n'
1111
import { dirname } from '@nextcloud/paths'
12-
import Vue from 'vue'
12+
import { createApp } from 'vue'
1313
import NcUserBubble from '@nextcloud/vue/components/NcUserBubble'
1414

1515
export const originalLocation = new Column({
@@ -40,14 +40,13 @@ export const deletedBy = new Column({
4040
return span
4141
}
4242

43-
const UserBubble = Vue.extend(NcUserBubble)
44-
const propsData = {
43+
const el = document.createElement('div')
44+
createApp(NcUserBubble, {
4545
size: 32,
4646
user: userId ?? undefined,
4747
displayName: displayName ?? userId,
48-
}
49-
const userBubble = new UserBubble({ propsData }).$mount().$el
50-
return userBubble as HTMLElement
48+
}).mount(el)
49+
return el
5150
},
5251
sort(nodeA, nodeB) {
5352
const deletedByA = parseDeletedBy(nodeA)

apps/files_trashbin/src/files_views/trashbinView.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import isSvg from 'is-svg'
2-
/**
1+
/*!
32
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
43
* SPDX-License-Identifier: AGPL-3.0-or-later
54
*/
5+
6+
import isSvg from 'is-svg'
67
import { describe, expect, it } from 'vitest'
78
import { getContents } from '../services/trashbin.ts'
89
import { deleted, deletedBy, originalLocation } from './columns.ts'

apps/files_trashbin/src/files_views/trashbinView.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import svgDelete from '@mdi/svg/svg/trash-can-outline.svg?raw'
2-
/**
1+
/*!
32
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
43
* SPDX-License-Identifier: AGPL-3.0-or-later
54
*/
5+
6+
import svgDelete from '@mdi/svg/svg/trash-can-outline.svg?raw'
67
import { View } from '@nextcloud/files'
78
import { t } from '@nextcloud/l10n'
89
import { getContents } from '../services/trashbin.ts'

apps/files_trashbin/src/logger.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ describe('files_trashbin: logger', () => {
1313

1414
logger.error('<message>')
1515
expect(consoleSpy).toBeCalledTimes(1)
16-
expect(consoleSpy.mock.calls[0][0]).toContain('<message>')
17-
expect(consoleSpy.mock.calls[0][0]).toContain('files_trashbin')
18-
expect(consoleSpy.mock.calls[0][1].app).toBe('files_trashbin')
16+
expect(consoleSpy.mock.calls[0]![0]).toContain('<message>')
17+
expect(consoleSpy.mock.calls[0]![0]).toContain('files_trashbin')
18+
expect(consoleSpy.mock.calls[0]![1].app).toBe('files_trashbin')
1919
})
2020
})

0 commit comments

Comments
 (0)