Skip to content

Commit 83cac36

Browse files
committed
fix(image-editor): open newly created image when closing the image editor
Signed-off-by: Hamza <hamzamahjoubi221@gmail.com>
1 parent 65bd4f9 commit 83cac36

5 files changed

Lines changed: 111 additions & 3 deletions

File tree

cypress/e2e/actions/edit.cy.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
describe('Open the new saved as image', function() {
7+
before(function() {
8+
cy.createRandomUser().then(user => {
9+
cy.uploadFile(user, 'image1.jpg', 'image/jpeg')
10+
cy.login(user)
11+
cy.visit('/apps/files')
12+
})
13+
})
14+
after(function() {
15+
cy.logout()
16+
})
17+
18+
it('See images in the list', function() {
19+
cy.getFile('image1.jpg', { timeout: 10000 })
20+
.should('contain', 'image1 .jpg')
21+
})
22+
it('Open the viewer on file click', function() {
23+
cy.openFile('image1.jpg')
24+
cy.get('body > .viewer').should('be.visible')
25+
})
26+
it('open the image editor', function() {
27+
cy.get('button[aria-label="Edit"]').click()
28+
})
29+
it('Save the image', function() {
30+
cy.get('.FIE_topbar-save-button').click()
31+
cy.get('input[type="text"].SfxInput-Base').clear()
32+
cy.get('input[type="text"].SfxInput-Base').type('imageSave')
33+
cy.get('.SfxModal-Container button[color="primary"].SfxButton-root').contains('Save').click()
34+
cy.get('.FIE_topbar-close-button').click()
35+
cy.get('.modal-header__name').should('contain', 'imageSave.jpg')
36+
cy.get('.modal-header button[aria-label="Close"]').click()
37+
})
38+
it('See the new saved image in the list', function() {
39+
40+
cy.getFile('imageSave.jpg', { timeout: 10000 })
41+
.should('contain', 'imageSave .jpg')
42+
})
43+
44+
})

src/components/ImageEditor.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,16 @@ export default {
253253
try {
254254
const blob = await new Promise(resolve => imageCanvas.toBlob(resolve, mimeType, quality))
255255
const response = await axios.put(putUrl, new File([blob], fullName))
256-
257256
logger.info('Edited image saved!', { response })
258257
showSuccess(t('viewer', 'Image saved'))
259258
if (putUrl !== this.src) {
260-
emit('files:node:created', { fileid: parseInt(response?.headers?.['oc-fileid']?.split('oc')[0]) || null })
259+
const fileId = parseInt(response?.headers?.['oc-fileid']?.split('oc')[0]) || null
260+
emit('editor:file:created', putUrl)
261+
if (fileId) {
262+
const newParams = window.OCP.Files.Router.params
263+
newParams.fileId = fileId
264+
window.OCP.Files.Router.goToRoute(null, newParams, window.OCP.Files.Router.query)
265+
}
261266
} else {
262267
this.$emit('updated')
263268
const updatedFile = await rawStat(origin, decodeURI(pathname))

src/services/FetchFile.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { getClient, getDefaultPropfind, getRootPath, resultToNode } from '@nextcloud/files/dav'
7+
import type { FileStat, ResponseDataDetailed } from 'webdav'
8+
import type { Node } from '@nextcloud/files'
9+
10+
export default async (path: string): Promise<Node> => {
11+
if (!path.startsWith('/')) {
12+
path = `/${path}`
13+
}
14+
const client = getClient()
15+
const propfindPayload = getDefaultPropfind()
16+
const result = await client.stat(`${getRootPath()}${path}`, {
17+
details: true,
18+
data: propfindPayload,
19+
}) as ResponseDataDetailed<FileStat>
20+
return resultToNode(result.data)
21+
}

src/utils/fileUtils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { FileStat } from 'webdav'
77
import { davRemoteURL, davRootPath } from '@nextcloud/files'
88
import { getLanguage } from '@nextcloud/l10n'
99
import { encodePath } from '@nextcloud/paths'
10+
import { getCurrentUser } from '@nextcloud/auth'
1011
import camelcase from 'camelcase'
1112

1213
import { isNumber } from './numberUtil'
@@ -64,6 +65,24 @@ export function extractFilePaths(path: string): [string, string] {
6465
return [dirPath, fileName]
6566
}
6667

68+
/**
69+
* Extract path from source
70+
*
71+
* @param source the full source URL
72+
* @return path
73+
*/
74+
export function extractFilePathFromSource(source: string): string {
75+
const uid = getCurrentUser()?.uid
76+
77+
if (uid) {
78+
const path = source.split(`${uid}/`)[1]
79+
if (path) {
80+
return path
81+
}
82+
}
83+
throw new Error(`Invalid source URL: ${source}. Unable to extract file paths.`)
84+
}
85+
6786
/**
6887
* Sorting comparison function
6988
*

src/views/Viewer.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
:is-sidebar-shown="isSidebarShown"
149149
:loaded.sync="currentFile.loaded"
150150
class="viewer__file viewer__file--active"
151+
@update:editing="toggleEditor"
151152
@error="currentFailed" />
152153
<Error v-else
153154
:name="currentFile.basename" />
@@ -187,12 +188,13 @@ import isFullscreen from '@nextcloud/vue/dist/Mixins/isFullscreen.js'
187188
import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js'
188189
189190
import { canDownload } from '../utils/canDownload.ts'
190-
import { extractFilePaths, sortCompare } from '../utils/fileUtils.ts'
191+
import { extractFilePaths, sortCompare, extractFilePathFromSource } from '../utils/fileUtils.ts'
191192
import getSortingConfig from '../services/FileSortingConfig.ts'
192193
import cancelableRequest from '../utils/CancelableRequest.js'
193194
import Error from '../components/Error.vue'
194195
import File from '../models/file.js'
195196
import getFileInfo from '../services/FileInfo.ts'
197+
import fetchNode from '../services/FetchFile.ts'
196198
import getFileList from '../services/FileList.ts'
197199
import Mime from '../mixins/Mime.js'
198200
import logger from '../services/logger.js'
@@ -545,6 +547,7 @@ export default defineComponent({
545547
subscribe('files:node:updated', this.handleFileUpdated)
546548
subscribe('viewer:trapElements:changed', this.handleTrapElementsChange)
547549
subscribe('editor:toggle', this.toggleEditor)
550+
subscribe('editor:file:created', this.handleNewFile)
548551
window.addEventListener('keydown', this.keyboardDeleteFile)
549552
window.addEventListener('keydown', this.keyboardDownloadFile)
550553
window.addEventListener('keydown', this.keyboardEditFile)
@@ -659,6 +662,22 @@ export default defineComponent({
659662
}
660663
}
661664
},
665+
async handleNewFile(source) {
666+
let path
667+
try {
668+
path = extractFilePathFromSource(source)
669+
this.openFile(path)
670+
671+
} catch (e) {
672+
logger.error('Could not extract file path from source', { source, e })
673+
}
674+
try {
675+
const node = await fetchNode('/' + path)
676+
emit('files:node:created', node)
677+
} catch (e) {
678+
logger.error('Could not fetch new file', { path, e })
679+
}
680+
},
662681
663682
/**
664683
* Open the view and display the clicked file from a known file info object

0 commit comments

Comments
 (0)