Skip to content

Commit

Permalink
FaceMergeForm: Lazy load faces files once face cover is visible in de…
Browse files Browse the repository at this point in the history
…vice viewport

Signed-off-by: Marcel Klehr <mklehr@gmx.net>
  • Loading branch information
marcelklehr committed Nov 4, 2022
1 parent 8dd6061 commit 3bfccfd
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 75 deletions.
148 changes: 148 additions & 0 deletions src/components/FaceCoverSmall.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<!--
- @copyright Copyright (c) 2022 Marcel Klehr <mklehr@gmx.net>
-
- @author Marcel Klehr <mklehr@gmx.net>
-
- @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/>.
-
-->
<template>
<div class="face-cover-small"
@click="$emit('click')">
<div class="face-cover-small__crop-container">
<img class="face-cover-small__image"
:src="coverUrl"
:style="coverDimensions">
</div>
<div class="face-cover-small__details">
<span :class="{'hidden-visually': face.basename.match(/^[0-9]+$/)}">{{ face.basename }}</span>
</div>
</div>
</template>

<script>
import { mapGetters } from 'vuex'

import { generateUrl } from '@nextcloud/router'

import FaceCoverMixin from '../mixins/FaceCoverMixin.js'
import FetchFacesMixin from '../mixins/FetchFacesMixin.js'

export default {
name: 'FaceCoverSmall',
mixins: [
FaceCoverMixin,
FetchFacesMixin,
],
props: {
baseName: {
type: String,
required: true,
},
},
data() {
return {
observer: null,
}
},
computed: {
...mapGetters([
'files',
'faces',
'facesFiles',
]),

/**
* @return {Face}
*/
face() {
return this.faces[this.baseName]
},

/**
* @return {string}
*/
coverUrl() {
if (!this.cover) {
return ''
}

return generateUrl(`/apps/photos/api/v1/preview/${this.cover.fileid}?x=${512}&y=${512}`)
},

cover() {
return this.getFaceCover(this.face.basename)
},

coverDimensions() {
if (!this.cover) return {}
return this.getCoverStyle(this.face.basename)
},
},
mounted() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
this.fetchFaceContent(this.face.basename)
}
})
})
this.observer.observe(this.$el)
},
beforeDestroy() {
this.observer.disconnect()
},
}
</script>

<style scoped lang="scss">
.face-cover-small {
display: flex;
flex-direction: column;
padding: 10px;
border-radius: var(--border-radius);
align-items: center;
cursor: pointer;
width: 120px;

* {
cursor: pointer;
}

&__crop-container {
overflow: hidden;
width: 60px;
height: 60px;
border-radius: 60px;
position: relative;
background: var(--color-background-darker);
--photos-face-width: 60px;
}

&:hover, &:focus {
background: var(--color-background-hover);
}

&__details {
padding: 10px;
height: 1em;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
text-align: center;
}
}
</style>
92 changes: 17 additions & 75 deletions src/components/FaceMergeForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,23 @@
-->
<template>
<div class="merge-form face-list">
<template v-if="loading">
<NcLoadingIcon class="loader" />
</template>
<template v-else>
<div v-for="face in filteredFaces"
:key="face.basename"
class="face-list__item"
@click="handleSelect(face.basename)">
<div class="face-list__item__crop-container">
<img class="face-list__item__image"
:src="getCoverUrl(face.basename)"
:style="getCoverStyle(face.basename)">
</div>
<div class="face-list__item__details">
<span :class="{'hidden-visually': face.basename.match(/^[0-9]+$/)}">{{ face.basename }}</span>
</div>
</div>
</template>
<FaceCoverSmall v-for="face in filteredFaces"
:key="face.basename"
:base-name="face.basename"
@click="handleSelect(face.basename)" />
</div>
</template>

<script>
import { mapGetters } from 'vuex'

import { generateUrl } from '@nextcloud/router'
import { NcLoadingIcon } from '@nextcloud/vue'

import FaceCoverMixin from '../mixins/FaceCoverMixin.js'
import FetchFacesMixin from '../mixins/FetchFacesMixin.js'
import FaceCoverSmall from './FaceCoverSmall.vue'

export default {
name: 'FaceMergeForm',
components: { NcLoadingIcon },
components: { FaceCoverSmall },
mixins: [
FaceCoverMixin,
FetchFacesMixin,
Expand All @@ -77,24 +61,20 @@ export default {
]),

filteredFaces() {
return Object.values(this.faces).filter(face => face.basename !== this.firstFace).sort((a, b) => {
if (!this.facesFiles[b.basename] || !this.facesFiles[a.basename]) {
return 0
}
return this.facesFiles[b.basename].length - this.facesFiles[a.basename].length
})
return Object.values(this.faces)
.filter(face => face.basename !== this.firstFace)
.sort((a, b) => {
if (a.props.nbItems && b.props.nbItems) {
return b.props.nbItems - a.props.nbItems
}
if (!this.facesFiles[b.basename] || !this.facesFiles[a.basename]) {
return 0
}
return this.facesFiles[b.basename].length - this.facesFiles[a.basename].length
})
},
},
methods: {
getCoverUrl(faceName) {
const cover = this.getFaceCover(faceName)
if (!cover) {
this.fetchFaceContent(faceName)
return ''
}
return generateUrl(`/apps/photos/api/v1/preview/${cover.fileid}?x=${512}&y=${512}`)
},

handleSelect(faceName) {
this.$emit('select', faceName)
this.loading = true
Expand All @@ -110,44 +90,6 @@ export default {
height: 350px;
flex-wrap: wrap;
padding: 12px;
align-content: center;

&__item {
display: flex;
flex-direction: column;
padding: 10px;
border-radius: var(--border-radius);
align-items: center;
cursor: pointer;
width: 120px;

* {
cursor: pointer;
}

&__crop-container {
overflow: hidden;
width: 60px;
height: 60px;
border-radius: 60px;
position: relative;
background: var(--color-background-darker);
--photos-face-width: 60px;
}

&:hover, &:focus {
background: var(--color-background-hover);
}

&__details {
padding: 10px;
height: 1em;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
text-align: center;
}
}
}

.loader {
Expand Down

0 comments on commit 3bfccfd

Please sign in to comment.