Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
fix(image-editor): open newly created image when closing the image ed…
…itor

Signed-off-by: Hamza <[email protected]>

[skip ci]
  • Loading branch information
hamza221 committed Jul 30, 2025
commit cc23a909d3c02aff2adb6b8e0cf4c42eb992fa00
44 changes: 44 additions & 0 deletions cypress/e2e/actions/edit.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

describe('Open the new saved as image', function() {
before(function() {
cy.createRandomUser().then(user => {
cy.uploadFile(user, 'image1.jpg', 'image/jpeg')
cy.login(user)
cy.visit('/apps/files')
})
})
after(function() {
cy.logout()
})

it('See images in the list', function() {
cy.getFile('image1.jpg', { timeout: 10000 })
.should('contain', 'image1 .jpg')
})
it('Open the viewer on file click', function() {
cy.openFile('image1.jpg')
cy.get('body > .viewer').should('be.visible')
})
it('open the image editor', function() {
cy.get('button[aria-label="Edit"]').click()
})
it('Save the image', function() {
cy.get('.FIE_topbar-save-button').click()
cy.get('input[type="text"].SfxInput-Base').clear()
cy.get('input[type="text"].SfxInput-Base').type('imageSave')
cy.get('.SfxModal-Container button[color="primary"].SfxButton-root').contains('Save').click()
cy.get('.FIE_topbar-close-button').click()
cy.get('.modal-header__name').should('contain', 'imageSave.jpg')
cy.get('.modal-header button[aria-label="Close"]').click()
})
it('See the new saved image in the list', function() {

cy.getFile('imageSave.jpg', { timeout: 10000 })
.should('contain', 'imageSave .jpg')
})

})
9 changes: 7 additions & 2 deletions src/components/ImageEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,16 @@ export default {
try {
const blob = await new Promise(resolve => imageCanvas.toBlob(resolve, mimeType, quality))
const response = await axios.put(putUrl, new File([blob], fullName))

logger.info('Edited image saved!', { response })
showSuccess(t('viewer', 'Image saved'))
if (putUrl !== this.src) {
emit('files:node:created', { fileid: parseInt(response?.headers?.['oc-fileid']?.split('oc')[0]) || null })
const fileId = parseInt(response?.headers?.['oc-fileid']?.split('oc')[0]) || null
emit('editor:file:created', putUrl)
if (fileId) {
const newParams = window.OCP.Files.Router.params
newParams.fileId = fileId
window.OCP.Files.Router.goToRoute(null, newParams, window.OCP.Files.Router.query)
}
} else {
this.$emit('updated')
const updatedFile = await rawStat(origin, decodeURI(pathname))
Expand Down
21 changes: 21 additions & 0 deletions src/services/FetchFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { getClient, getDefaultPropfind, getRootPath, resultToNode } from '@nextcloud/files/dav'
import type { FileStat, ResponseDataDetailed } from 'webdav'
import type { Node } from '@nextcloud/files'

export default async (path: string): Promise<Node> => {
if (!path.startsWith('/')) {
path = `/${path}`
}
const client = getClient()
const propfindPayload = getDefaultPropfind()
const result = await client.stat(`${getRootPath()}${path}`, {
details: true,
data: propfindPayload,
}) as ResponseDataDetailed<FileStat>
return resultToNode(result.data)
}
19 changes: 19 additions & 0 deletions src/utils/fileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { FileStat } from 'webdav'
import { davRemoteURL, davRootPath } from '@nextcloud/files'
import { getLanguage } from '@nextcloud/l10n'
import { encodePath } from '@nextcloud/paths'
import { getCurrentUser } from '@nextcloud/auth'
import camelcase from 'camelcase'

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

/**
* Extract path from source
*
* @param source the full source URL
* @return path
*/
export function extractFilePathFromSource(source: string): string {
const uid = getCurrentUser()?.uid

if (uid) {
const path = source.split(`${uid}/`)[1]
if (path) {
return path
}
}
throw new Error(`Invalid source URL: ${source}. Unable to extract file paths.`)
}

/**
* Sorting comparison function
*
Expand Down
21 changes: 20 additions & 1 deletion src/views/Viewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
:is-sidebar-shown="isSidebarShown"
:loaded.sync="currentFile.loaded"
class="viewer__file viewer__file--active"
@update:editing="toggleEditor"
@error="currentFailed" />
<Error v-else
:name="currentFile.basename" />
Expand Down Expand Up @@ -187,12 +188,13 @@ import isFullscreen from '@nextcloud/vue/dist/Mixins/isFullscreen.js'
import isMobile from '@nextcloud/vue/dist/Mixins/isMobile.js'

import { canDownload } from '../utils/canDownload.ts'
import { extractFilePaths, sortCompare } from '../utils/fileUtils.ts'
import { extractFilePaths, sortCompare, extractFilePathFromSource } from '../utils/fileUtils.ts'
import getSortingConfig from '../services/FileSortingConfig.ts'
import cancelableRequest from '../utils/CancelableRequest.js'
import Error from '../components/Error.vue'
import File from '../models/file.js'
import getFileInfo from '../services/FileInfo.ts'
import fetchNode from '../services/FetchFile.ts'
import getFileList from '../services/FileList.ts'
import Mime from '../mixins/Mime.js'
import logger from '../services/logger.js'
Expand Down Expand Up @@ -556,6 +558,7 @@ export default defineComponent({
subscribe('files:node:updated', this.handleFileUpdated)
subscribe('viewer:trapElements:changed', this.handleTrapElementsChange)
subscribe('editor:toggle', this.toggleEditor)
subscribe('editor:file:created', this.handleNewFile)
window.addEventListener('keydown', this.keyboardDeleteFile)
window.addEventListener('keydown', this.keyboardDownloadFile)
window.addEventListener('keydown', this.keyboardEditFile)
Expand Down Expand Up @@ -671,6 +674,22 @@ export default defineComponent({
}
}
},
async handleNewFile(source) {
let path
try {
path = extractFilePathFromSource(source)
this.openFile(path)

} catch (e) {
logger.error('Could not extract file path from source', { source, e })
}
try {
const node = await fetchNode('/' + path)
emit('files:node:created', node)
} catch (e) {
logger.error('Could not fetch new file', { path, e })
}
},

/**
* Open the view and display the clicked file from a known file info object
Expand Down