Skip to content

Commit

Permalink
fix: Use core preview controller for loading file previews and fallba…
Browse files Browse the repository at this point in the history
…ck to MDI icons

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Sep 21, 2023
1 parent 9d36c42 commit 250257f
Show file tree
Hide file tree
Showing 6 changed files with 581 additions and 12 deletions.
13 changes: 5 additions & 8 deletions lib/components/FilePicker/FileListRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</td>
<td class="row-name">
<div class="file-picker__name-container" data-testid="row-name">
<div :class="fileListIconStyles['file-picker__file-icon']" :style="{ backgroundImage }" />
<FilePreview :node="node" />
<div class="file-picker__file-name" :title="displayName" v-text="displayName" />
<div class="file-picker__file-extension" v-text="fileExtension" />
</div>
Expand All @@ -36,13 +36,15 @@
</tr>
</template>
<script setup lang="ts">
import { type Node, formatFileSize, FileType } from '@nextcloud/files'
import type { Node } from '@nextcloud/files'
import { formatFileSize, FileType } from '@nextcloud/files'
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
import { computed } from 'vue'
import { t } from '../../utils/l10n'
import FilePreview from './FilePreview.vue'
import NcDatetime from './NcDatetime.vue'
import fileListIconStyles from './FileListIcon.module.scss'
const props = defineProps<{
/** Can directories be picked */
Expand Down Expand Up @@ -84,11 +86,6 @@ const isDirectory = computed(() => props.node.type === FileType.Folder)
*/
const isPickable = computed(() => props.canPick && (props.allowPickDirectory || !isDirectory.value))
/**
* Background image url for the given nodes mime type
*/
const backgroundImage = computed(() => `url(${window.OC.MimeType.getIconUrl(props.node.mime)})`)
/**
* Toggle the selection state
*/
Expand Down
55 changes: 55 additions & 0 deletions lib/components/FilePicker/FilePreview.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div :style="canLoadPreview ? { backgroundImage: `url(${previewURL})`} : undefined"
:aria-label="t('Mime type {mime}', { mime: node.mime || t('unknown') })"
class="file-picker__file-icon">
<template v-if="!canLoadPreview">
<IconFile v-if="isFile" :size="20" />
<IconFolder v-else :size="20" />
</template>
</div>
</template>

<script setup lang="ts">
import { FileType, type Node } from '@nextcloud/files'
import { usePreviewURL } from '../../usables/preview'
import { computed, ref, toRef, watch } from 'vue'
import { t } from '../../utils/l10n'
import IconFile from 'vue-material-design-icons/File.vue'
import IconFolder from 'vue-material-design-icons/Folder.vue'
const props = defineProps<{
node: Node
}>()
const { previewURL } = usePreviewURL(toRef(props, 'node'))
const isFile = computed(() => props.node.type === FileType.File)
const canLoadPreview = ref(false)
watch(previewURL, () => {
canLoadPreview.value = false
if (previewURL.value) {
const loader = document.createElement('img')
loader.src = previewURL.value.href
loader.onerror = () => loader.remove()
loader.onload = () => { canLoadPreview.value = true; loader.remove() }
document.body.appendChild(loader)
}
}, { immediate: true })
</script>

<style scoped lang="scss">
.file-picker__file-icon {
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
background-repeat: no-repeat;
background-size: contain;
// for the fallback
display: flex;
justify-content: center;
}
</style>
92 changes: 92 additions & 0 deletions lib/usables/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @copyright Copyright (c) 2023 Ferdinand Thiessen <[email protected]>
*
* @author Ferdinand Thiessen <[email protected]>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import type { Node } from '@nextcloud/files'
import type { Ref } from 'vue'

import { generateUrl } from '@nextcloud/router'
import { toValue } from '@vueuse/core'
import { ref, watchEffect } from 'vue'

interface PreviewOptions {
/**
* Size of the previews in px
* @default 32
*/
size?: number
/**
* Should the preview fall back to the mime type icon
* @default true
*/
mimeFallback?: boolean
/**
* Should the preview be cropped or fitted
* @default false (meaning it gets fitted)
*/
cropPreview?: boolean
}

/**
* Generate the preview URL of a file node
*
* @param node The node to generate the preview for
* @param options Preview options
*/
export function getPreviewURL(node: Node, options: PreviewOptions = {}) {
options = { size: 32, cropPreview: false, mimeFallback: true, ...options }

try {
const previewUrl = node.attributes?.previewUrl
|| generateUrl('/core/preview?fileId={fileid}', {
fileid: node.fileid,
})

let url
try {
url = new URL(previewUrl)
} catch (e) {
url = new URL(previewUrl, window.location.origin)
}

// Request preview with params
url.searchParams.set('x', `${options.size}`)
url.searchParams.set('y', `${options.size}`)
url.searchParams.set('mimeFallback', `${options.mimeFallback}`)

// Handle cropping
url.searchParams.set('a', options.cropPreview === true ? '0' : '1')
return url
} catch (e) {
return null
}
}

export const usePreviewURL = (node: Node | Ref<Node>, options?: PreviewOptions | Ref<PreviewOptions>) => {
const previewURL = ref<URL|null>(null)

watchEffect(() => {
previewURL.value = getPreviewURL(toValue(node), toValue(options || {}))
})

return {
previewURL,
}
}
Loading

0 comments on commit 250257f

Please sign in to comment.