Skip to content

Commit

Permalink
Merge pull request #47605 from nextcloud/artonge/feat/files_blurhash
Browse files Browse the repository at this point in the history
feat: Use the blurhash in Files
  • Loading branch information
artonge authored Aug 29, 2024
2 parents bfb5835 + 49f2801 commit 1cc7851
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 23 deletions.
78 changes: 62 additions & 16 deletions apps/files/src/components/FileEntry/FileEntryPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 />

Expand All @@ -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'
Expand All @@ -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: {
Expand Down Expand Up @@ -107,6 +115,7 @@ export default Vue.extend({
data() {
return {
backgroundFailed: undefined as boolean | undefined,
backgroundLoaded: false,
}
},
Expand Down Expand Up @@ -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
}
Expand All @@ -206,24 +215,61 @@ export default Vue.extend({
return null
},
hasBlurhash() {
return this.source.attributes['metadata-blurhash'] !== undefined
},
},
mounted() {
if (this.hasBlurhash && this.$refs.canvas) {
this.drawBlurhash()
}
},
methods: {
// Called from FileEntry
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,
Expand Down
15 changes: 14 additions & 1 deletion apps/files/src/components/FilesListVirtual.vue
Original file line number Diff line number Diff line change
Expand Up @@ -556,11 +556,24 @@ export default defineComponent({
}
}
&-preview {
&-preview-container {
position: relative; // Needed for the blurshash to be positioned correctly
overflow: hidden;
width: var(--icon-preview-size);
height: var(--icon-preview-size);
border-radius: var(--border-radius);
}
&-blurhash {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
object-fit: cover;
}
&-preview {
// Center and contain the preview
object-fit: contain;
object-position: center;
Expand Down
1 change: 1 addition & 0 deletions apps/files/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ registerPreviewServiceWorker()

registerDavProperty('nc:hidden', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:is-mount-root', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:metadata-blurhash', { nc: 'http://nextcloud.org/ns' })

initLivePhotos()
4 changes: 2 additions & 2 deletions dist/files-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-init.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-main.js.map

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"@vueuse/integrations": "^11.0.1",
"backbone": "^1.4.1",
"blueimp-md5": "^2.19.0",
"blurhash": "^2.0.5",
"browserslist-useragent-regexp": "^4.1.1",
"camelcase": "^8.0.0",
"cancelable-promise": "^4.3.1",
Expand Down

0 comments on commit 1cc7851

Please sign in to comment.