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
10 changes: 10 additions & 0 deletions __tests__/component/imageEditor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ describe('ImageEditor wrapper', () => {
expect(wrapper.emitted('close')).toBeUndefined()
})

// The editor loads the image from that URL, so a name holding a `#` or
// a `?` has to be encoded the same way the save request encodes it.
it('hands the editor the encoded source', async () => {
const file = makeFile({ basename: 'a#b c?.jpg', mime: 'image/jpeg' })
const wrapper = mount(ImageEditor, { props: { file } })
await flushPromises()

expect(wrapper.findComponent({ name: 'LibImageEditor' }).attributes('src')).toBe(file.encodedSource)
})

it('closes without saving on cancel', async () => {
const { wrapper, editor } = mountEditor()
editor.vm.$emit('cancel')
Expand Down
19 changes: 19 additions & 0 deletions __tests__/component/media.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,25 @@ describe('Images.vue', () => {
expect(preloadMediaMock).not.toHaveBeenCalled()
})

// The element's `src` is a URL: a name holding a `#` or a `?` cuts it
// short unless it is encoded, and the image then fails to load.
it('renders the encoded source of a file whose name needs it', async () => {
const file = makeFile({ basename: 'a#b c?.jpg', mime: 'image/jpeg' })
const wrapper = mountImages({ file, files: [file] })
await flushPromises()

expect(wrapper.find('img').attributes('src')).toBe(file.encodedSource)
})

it('renders the encoded source of a live photo', async () => {
const photo = makeFile({ id: 1, basename: 'a#b.jpg', attributes: { 'metadata-files-live-photo': 2 } })
const movie = makeFile({ id: 2, basename: 'a#b.mov', mime: 'video/quicktime' })
const wrapper = mountImages({ file: photo, files: [photo, movie] })
await flushPromises()

expect(wrapper.find('video').attributes('src')).toBe(movie.encodedSource)
})

it('shows the hand-fetched bytes when the source fails to load', async () => {
const file = makeFile({ basename: 'broken.jpg' })
const wrapper = mountImages({ file, files: [file] })
Expand Down
8 changes: 8 additions & 0 deletions __tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ describe('previewUtils.getPreviewIfAny', () => {
const file = makeFileWithAttributes({ hasPreview: false })
expect(getPreviewIfAny(file)).toBe(file.source)
})

// What comes back is handed to a media element as its `src`, so a name
// holding a `#` or a `?` has to be encoded or the URL is cut short.
it('encodes the fallback source', () => {
const file = makeFile({ basename: 'a#b c?.jpg', attributes: { hasPreview: false } })
expect(getPreviewIfAny(file)).toBe(file.encodedSource)
expect(getPreviewIfAny(file)).not.toContain('#')
})
})

describe('livePhotoUtils.findLivePhotoPeerFromFileId', () => {
Expand Down
2 changes: 1 addition & 1 deletion lib/components/ImageEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->
<template>
<NcImageEditor
:src="file.source"
:src="file.encodedSource"
:label="file.displayname"
:exportOptions="exportOptions"
:saving="saving"
Expand Down
5 changes: 3 additions & 2 deletions lib/components/Images.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ const livePhoto = computed(() => {
return findLivePhotoPeerFromFileId(metadataFilesLivePhoto.value, props.files)
})

const livePhotoSrc = computed(() => livePhoto.value?.source ?? null)
// Encoded, as it goes straight into the video element's `src`
const livePhotoSrc = computed(() => livePhoto.value?.encodedSource ?? null)

/**
* What is fetching the current file, so it can be dropped when the viewer
Expand Down Expand Up @@ -224,7 +225,7 @@ async function loadData() {

// If there is no preview and we have a direct source, load it instead
if (props.file.source && !hasPreview.value && !previewUrl.value) {
data.value = props.file.source
data.value = src.value
return
}

Expand Down
4 changes: 3 additions & 1 deletion lib/utils/previewUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@ export function getPreviewIfAny(file: IFile, available?: AvailableSpace): string
return generateUrl(`/core/preview?${searchParams}`)
}

return file.source
// Encoded: this is handed to a media element as its `src`, and a name
// holding a `#` or a `?` would otherwise cut the URL short.
return file.encodedSource
}
Loading