|
|
|
|
@ -14,16 +14,22 @@
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- Decorative image, should not be aria documented -->
|
|
|
|
|
<img v-else-if="previewUrl && backgroundFailed !== true"
|
|
|
|
|
ref="previewImg"
|
|
|
|
|
alt=""
|
|
|
|
|
class="files-list__row-icon-preview"
|
|
|
|
|
:class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}"
|
|
|
|
|
loading="lazy"
|
|
|
|
|
:src="previewUrl"
|
|
|
|
|
@error="onBackgroundError"
|
|
|
|
|
@load="backgroundFailed = false">
|
|
|
|
|
<!-- Decorative images, should not be aria documented -->
|
|
|
|
|
<span v-else-if="previewUrl" class="files-list__row-icon-preview-container">
|
|
|
|
|
<canvas v-if="hasBlurhash && (backgroundFailed === true || !backgroundLoaded)"
|
|
|
|
|
ref="canvas"
|
|
|
|
|
class="files-list__row-icon-blurhash"
|
|
|
|
|
aria-hidden="true" />
|
|
|
|
|
<img v-if="backgroundFailed !== true"
|
|
|
|
|
ref="previewImg"
|
|
|
|
|
alt=""
|
|
|
|
|
class="files-list__row-icon-preview"
|
|
|
|
|
:class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}"
|
|
|
|
|
loading="lazy"
|
|
|
|
|
:src="previewUrl"
|
|
|
|
|
@error="onBackgroundError"
|
|
|
|
|
@load="onBackgroundLoad">
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
<FileIcon v-else v-once />
|
|
|
|
|
|
|
|
|
|
@ -45,9 +51,10 @@ import type { UserConfig } from '../../types.ts'
|
|
|
|
|
import { Node, FileType } from '@nextcloud/files'
|
|
|
|
|
import { generateUrl } from '@nextcloud/router'
|
|
|
|
|
import { translate as t } from '@nextcloud/l10n'
|
|
|
|
|
import { Type as ShareType } from '@nextcloud/sharing'
|
|
|
|
|
import { ShareType } from '@nextcloud/sharing'
|
|
|
|
|
import { decode } from 'blurhash'
|
|
|
|
|
import { defineComponent } from 'vue'
|
|
|
|
|
|
|
|
|
|
import Vue from 'vue'
|
|
|
|
|
import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue'
|
|
|
|
|
import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue'
|
|
|
|
|
import FileIcon from 'vue-material-design-icons/File.vue'
|
|
|
|
|
@ -64,8 +71,9 @@ import FavoriteIcon from './FavoriteIcon.vue'
|
|
|
|
|
|
|
|
|
|
import { isLivePhoto } from '../../services/LivePhotos'
|
|
|
|
|
import { useUserConfigStore } from '../../store/userconfig.ts'
|
|
|
|
|
import logger from '../../logger.ts'
|
|
|
|
|
|
|
|
|
|
export default Vue.extend({
|
|
|
|
|
export default defineComponent({
|
|
|
|
|
name: 'FileEntryPreview',
|
|
|
|
|
|
|
|
|
|
components: {
|
|
|
|
|
@ -107,6 +115,7 @@ export default Vue.extend({
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
backgroundFailed: undefined as boolean | undefined,
|
|
|
|
|
backgroundLoaded: false,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
@ -183,7 +192,7 @@ export default Vue.extend({
|
|
|
|
|
|
|
|
|
|
// Link and mail shared folders
|
|
|
|
|
const shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[]
|
|
|
|
|
if (shareTypes.some(type => type === ShareType.SHARE_TYPE_LINK || type === ShareType.SHARE_TYPE_EMAIL)) {
|
|
|
|
|
if (shareTypes.some(type => type === ShareType.Link || type === ShareType.Email)) {
|
|
|
|
|
return LinkIcon
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -206,6 +215,16 @@ export default Vue.extend({
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
hasBlurhash() {
|
|
|
|
|
return this.source.attributes['metadata-blurhash'] !== undefined
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
mounted() {
|
|
|
|
|
if (this.hasBlurhash && this.$refs.canvas) {
|
|
|
|
|
this.drawBlurhash()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
@ -213,17 +232,44 @@ export default Vue.extend({
|
|
|
|
|
reset() {
|
|
|
|
|
// Reset background state to cancel any ongoing requests
|
|
|
|
|
this.backgroundFailed = undefined
|
|
|
|
|
if (this.$refs.previewImg) {
|
|
|
|
|
this.$refs.previewImg.src = ''
|
|
|
|
|
this.backgroundLoaded = false
|
|
|
|
|
const previewImg = this.$refs.previewImg as HTMLImageElement | undefined
|
|
|
|
|
if (previewImg) {
|
|
|
|
|
previewImg.src = ''
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onBackgroundLoad() {
|
|
|
|
|
this.backgroundFailed = false
|
|
|
|
|
this.backgroundLoaded = true
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onBackgroundError(event) {
|
|
|
|
|
// Do not fail if we just reset the background
|
|
|
|
|
if (event.target?.src === '') {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.backgroundFailed = true
|
|
|
|
|
this.backgroundLoaded = false
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
drawBlurhash() {
|
|
|
|
|
const canvas = this.$refs.canvas as HTMLCanvasElement
|
|
|
|
|
|
|
|
|
|
const width = canvas.width
|
|
|
|
|
const height = canvas.height
|
|
|
|
|
|
|
|
|
|
const pixels = decode(this.source.attributes['metadata-blurhash'], width, height)
|
|
|
|
|
|
|
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
|
|
if (ctx === null) {
|
|
|
|
|
logger.error('Cannot create context for blurhash canvas')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const imageData = ctx.createImageData(width, height)
|
|
|
|
|
imageData.data.set(pixels)
|
|
|
|
|
ctx.putImageData(imageData, 0, 0)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
t,
|
|
|
|
|
|