forked from vikunja/frontend
kolaente
b2897545e4
Probably caused by the blur hash feature, switching between lists would not work if the list background was set via unsplash. I've refactored the whole decision tree which checks if a background should be loaded or not. It actually does not matter where the background is from (unsplash or upload) or if we had one in the last list - we only need to know if the current list has a background or if we just changed it and need to update right away.
273 lines
7.3 KiB
Vue
273 lines
7.3 KiB
Vue
<template>
|
|
<create-edit
|
|
:title="$t('list.background.title')"
|
|
primary-label=""
|
|
:loading="backgroundService.loading"
|
|
class="list-background-setting"
|
|
:wide="true"
|
|
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
|
:tertiary="hasBackground ? $t('list.background.remove') : ''"
|
|
@tertiary="removeBackground()"
|
|
>
|
|
<div class="mb-4" v-if="uploadBackgroundEnabled">
|
|
<input
|
|
@change="uploadBackground"
|
|
accept="image/*"
|
|
class="is-hidden"
|
|
ref="backgroundUploadInput"
|
|
type="file"
|
|
/>
|
|
<x-button
|
|
:loading="backgroundUploadService.loading"
|
|
@click="$refs.backgroundUploadInput.click()"
|
|
variant="primary"
|
|
>
|
|
{{ $t('list.background.upload') }}
|
|
</x-button>
|
|
</div>
|
|
<template v-if="unsplashBackgroundEnabled">
|
|
<input
|
|
:class="{'is-loading': backgroundService.loading}"
|
|
@keyup="() => debounceNewBackgroundSearch()"
|
|
class="input is-expanded"
|
|
:placeholder="$t('list.background.searchPlaceholder')"
|
|
type="text"
|
|
v-model="backgroundSearchTerm"
|
|
/>
|
|
<p class="unsplash-link">
|
|
<a href="https://unsplash.com" rel="noreferrer noopener nofollow" target="_blank">
|
|
{{ $t('list.background.poweredByUnsplash') }}
|
|
</a>
|
|
</p>
|
|
<div class="image-search-result">
|
|
<a
|
|
:key="im.id"
|
|
:style="{'background-image': `url(${backgroundBlurHashes[im.id]})`}"
|
|
@click="() => setBackground(im.id)"
|
|
class="image"
|
|
v-for="im in backgroundSearchResult">
|
|
<transition name="fade">
|
|
<img :src="backgroundThumbs[im.id]" alt="" v-if="backgroundThumbs[im.id]"/>
|
|
</transition>
|
|
<a
|
|
:href="`https://unsplash.com/@${im.info.author}`"
|
|
rel="noreferrer noopener nofollow"
|
|
target="_blank"
|
|
class="info">
|
|
{{ im.info.authorName }}
|
|
</a>
|
|
</a>
|
|
</div>
|
|
<x-button
|
|
:disabled="backgroundService.loading"
|
|
@click="() => searchBackgrounds(currentPage + 1)"
|
|
class="is-load-more-button mt-4"
|
|
:shadow="false"
|
|
variant="secondary"
|
|
v-if="backgroundSearchResult.length > 0"
|
|
>
|
|
{{ backgroundService.loading ? $t('misc.loading') : $t('list.background.loadMore') }}
|
|
</x-button>
|
|
</template>
|
|
</create-edit>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import {defineComponent} from 'vue'
|
|
import {mapState} from 'vuex'
|
|
import {getBlobFromBlurHash} from '../../../helpers/getBlobFromBlurHash'
|
|
|
|
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
|
import BackgroundUploadService from '../../../services/backgroundUpload'
|
|
import ListService from '@/services/list'
|
|
import {CURRENT_LIST} from '@/store/mutation-types'
|
|
import CreateEdit from '@/components/misc/create-edit.vue'
|
|
import debounce from 'lodash.debounce'
|
|
|
|
const SEARCH_DEBOUNCE = 300
|
|
|
|
export default defineComponent({
|
|
name: 'list-setting-background',
|
|
components: {CreateEdit},
|
|
data() {
|
|
return {
|
|
backgroundService: new BackgroundUnsplashService(),
|
|
backgroundSearchTerm: '',
|
|
backgroundSearchResult: [],
|
|
backgroundThumbs: {},
|
|
backgroundBlurHashes: {},
|
|
currentPage: 1,
|
|
|
|
// We're using debounce to not search on every keypress but with a delay.
|
|
debounceNewBackgroundSearch: debounce(this.newBackgroundSearch, SEARCH_DEBOUNCE, {
|
|
trailing: true,
|
|
}),
|
|
|
|
backgroundUploadService: new BackgroundUploadService(),
|
|
listService: new ListService(),
|
|
}
|
|
},
|
|
computed: mapState({
|
|
unsplashBackgroundEnabled: state => state.config.enabledBackgroundProviders.includes('unsplash'),
|
|
uploadBackgroundEnabled: state => state.config.enabledBackgroundProviders.includes('upload'),
|
|
currentList: state => state.currentList,
|
|
hasBackground: state => state.background !== null,
|
|
}),
|
|
created() {
|
|
this.setTitle(this.$t('list.background.title'))
|
|
// Show the default collection of backgrounds
|
|
this.newBackgroundSearch()
|
|
},
|
|
methods: {
|
|
newBackgroundSearch() {
|
|
if (!this.unsplashBackgroundEnabled) {
|
|
return
|
|
}
|
|
// This is an extra method to reset a few things when searching to not break loading more photos.
|
|
this.backgroundSearchResult = []
|
|
this.backgroundThumbs = {}
|
|
this.searchBackgrounds()
|
|
},
|
|
|
|
async searchBackgrounds(page = 1) {
|
|
this.currentPage = page
|
|
const result = await this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
|
|
this.backgroundSearchResult = this.backgroundSearchResult.concat(result)
|
|
result.forEach(background => {
|
|
getBlobFromBlurHash(background.blurHash)
|
|
.then(b => {
|
|
this.backgroundBlurHashes[background.id] = window.URL.createObjectURL(b)
|
|
})
|
|
|
|
this.backgroundService.thumb(background)
|
|
.then(b => {
|
|
this.backgroundThumbs[background.id] = b
|
|
})
|
|
})
|
|
},
|
|
|
|
async setBackground(backgroundId) {
|
|
// Don't set a background if we're in the process of setting one
|
|
if (this.backgroundService.loading) {
|
|
return
|
|
}
|
|
|
|
const list = await this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
|
|
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
|
this.$store.commit('namespaces/setListInNamespaceById', list)
|
|
this.$store.commit('lists/setList', list)
|
|
this.$message.success({message: this.$t('list.background.success')})
|
|
},
|
|
|
|
async uploadBackground() {
|
|
if (this.$refs.backgroundUploadInput.files.length === 0) {
|
|
return
|
|
}
|
|
|
|
const list = await this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
|
|
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
|
this.$store.commit('namespaces/setListInNamespaceById', list)
|
|
this.$store.commit('lists/setList', list)
|
|
this.$message.success({message: this.$t('list.background.success')})
|
|
},
|
|
|
|
async removeBackground() {
|
|
const list = await this.listService.removeBackground(this.currentList)
|
|
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
|
this.$store.commit('namespaces/setListInNamespaceById', list)
|
|
this.$store.commit('lists/setList', list)
|
|
this.$message.success({message: this.$t('list.background.removeSuccess')})
|
|
this.$router.back()
|
|
},
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.list-background-setting {
|
|
|
|
.unsplash-link {
|
|
text-align: right;
|
|
font-size: .8rem;
|
|
|
|
a {
|
|
color: var(--grey-800);
|
|
}
|
|
}
|
|
|
|
.image-search-result {
|
|
margin-top: 1rem;
|
|
display: flex;
|
|
flex-flow: row wrap;
|
|
|
|
.image {
|
|
width: calc(100% / 5 - 1rem);
|
|
height: 120px;
|
|
margin: .5rem;
|
|
background-size: cover;
|
|
background-position: center;
|
|
display: flex;
|
|
position: relative;
|
|
|
|
@media screen and (min-width: $desktop) {
|
|
&:nth-child(5n) {
|
|
break-after: always;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: $desktop) {
|
|
width: calc(100% / 4 - 1rem);
|
|
|
|
&:nth-child(4n) {
|
|
break-after: always;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: $tablet) {
|
|
width: calc(100% / 2 - 1rem);
|
|
|
|
&:nth-child(2n) {
|
|
break-after: always;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: ($mobile)) {
|
|
width: calc(100% - 1rem);
|
|
|
|
&:nth-child(1n) {
|
|
break-after: always;
|
|
}
|
|
}
|
|
|
|
.info {
|
|
align-self: flex-end;
|
|
display: block;
|
|
opacity: 0;
|
|
width: 100%;
|
|
padding: .25rem 0;
|
|
text-align: center;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
font-size: .75rem;
|
|
font-weight: bold;
|
|
color: var(--white);
|
|
transition: opacity $transition;
|
|
position: absolute;
|
|
}
|
|
|
|
img {
|
|
object-fit: cover;
|
|
}
|
|
|
|
&:hover .info {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.is-load-more-button {
|
|
margin: 1rem auto 0 !important;
|
|
display: block;
|
|
width: 200px;
|
|
}
|
|
}
|
|
</style> |