Custom backgrounds for lists (#144)
Make backgrounds list responsive Show initial collection of backgrounds Remove test data Fix "backgroundInformation is null" when navigating Fix kanban height Remove debug log Move list title to top header Add styling for title in top header Set the current list (and background) when loading settings Only load the background if it changed Make task detail view look good again Fix bottom spacing Make list and table view look good again Make pages with background at least 100vh Fix kanban height Make extra buttons look good again Move list title and view-switcher in one row Add styling for backgrounds Set background globally Add getting list background and putting it in vuex Add setting list background Move list background setting to seperate list Add search timeout to not search on every keypress Add getting thumbnails through api Add basic search for unsplash backgrounds Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/144
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="loader-container" :class="{ 'is-loading': listService.loading}">
|
||||
<div class="loader-container edit-list" :class="{ 'is-loading': listService.loading}">
|
||||
<div class="notification is-warning" v-if="list.isArchived">
|
||||
This list is archived.
|
||||
It is not possible to create new or edit tasks or it.
|
||||
@ -104,6 +104,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<background :list-id="$route.params.id"/>
|
||||
|
||||
<component
|
||||
:is="manageUsersComponent"
|
||||
:id="list.id"
|
||||
@ -141,6 +143,8 @@
|
||||
import ListModel from '../../models/list'
|
||||
import ListService from '../../services/list'
|
||||
import Fancycheckbox from '../global/fancycheckbox'
|
||||
import Background from './settings/background'
|
||||
import {CURRENT_LIST} from '../../store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: "EditList",
|
||||
@ -156,6 +160,7 @@
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Background,
|
||||
Fancycheckbox,
|
||||
LinkSharing,
|
||||
manageSharing,
|
||||
@ -183,6 +188,7 @@
|
||||
this.listService.get(list)
|
||||
.then(r => {
|
||||
this.$set(this, 'list', r)
|
||||
this.$store.commit(CURRENT_LIST, r)
|
||||
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
|
||||
this.manageTeamsComponent = 'manageSharing'
|
||||
this.manageUsersComponent = 'manageSharing'
|
||||
|
@ -1,20 +1,33 @@
|
||||
<template>
|
||||
<div class="loader-container" :class="{ 'is-loading': listService.loading}">
|
||||
<div class="content">
|
||||
<router-link :to="{ name: 'editList', params: { id: list.id } }" class="icon settings is-medium">
|
||||
<icon icon="cog" size="2x"/>
|
||||
<div
|
||||
class="loader-container"
|
||||
:class="{ 'is-loading': listService.loading}"
|
||||
>
|
||||
<div class="switch-view">
|
||||
<router-link
|
||||
:to="{ name: 'list.list', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.list'}">
|
||||
List
|
||||
</router-link>
|
||||
<h1 :style="{ 'opacity': list.title === '' ? '0': '1' }">{{ list.title === '' ? 'Loading...': list.title}}</h1>
|
||||
<div class="notification is-warning" v-if="list.isArchived">
|
||||
This list is archived.
|
||||
It is not possible to create new or edit tasks or it.
|
||||
</div>
|
||||
<div class="switch-view">
|
||||
<router-link :to="{ name: 'list.list', params: { listId: listId } }" :class="{'is-active': $route.name === 'list.list'}">List</router-link>
|
||||
<router-link :to="{ name: 'list.gantt', params: { listId: listId } }" :class="{'is-active': $route.name === 'list.gantt'}">Gantt</router-link>
|
||||
<router-link :to="{ name: 'list.table', params: { listId: listId } }" :class="{'is-active': $route.name === 'list.table'}">Table</router-link>
|
||||
<router-link :to="{ name: 'list.kanban', params: { listId: listId } }" :class="{'is-active': $route.name === 'list.kanban'}">Kanban</router-link>
|
||||
</div>
|
||||
<router-link
|
||||
:to="{ name: 'list.gantt', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.gantt'}">
|
||||
Gantt
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'list.table', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.table'}">
|
||||
Table
|
||||
</router-link>
|
||||
<router-link
|
||||
:to="{ name: 'list.kanban', params: { listId: listId } }"
|
||||
:class="{'is-active': $route.name === 'list.kanban'}">
|
||||
Kanban
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="notification is-warning" v-if="list.isArchived">
|
||||
This list is archived.
|
||||
It is not possible to create new or edit tasks or it.
|
||||
</div>
|
||||
|
||||
<router-view/>
|
||||
@ -53,12 +66,15 @@
|
||||
listId() {
|
||||
return typeof this.$route.params.listId === 'undefined' ? 0 : this.$route.params.listId
|
||||
},
|
||||
background() {
|
||||
return this.$store.state.background
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadList() {
|
||||
|
||||
// Don't load the list if we either already loaded it or aren't dealing with a list at all currently
|
||||
if(this.$route.params.listId === this.listLoaded || typeof this.$route.params.listId === 'undefined') {
|
||||
if (this.$route.params.listId === this.listLoaded || typeof this.$route.params.listId === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
@ -76,13 +92,12 @@
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit(CURRENT_LIST, Number(this.$route.params.listId))
|
||||
|
||||
// We create an extra list object instead of creating it in this.list because that would trigger a ui update which would result in bad ux.
|
||||
let list = new ListModel({id: this.$route.params.listId})
|
||||
this.listService.get(list)
|
||||
.then(r => {
|
||||
this.$set(this, 'list', r)
|
||||
this.$store.commit(CURRENT_LIST, r)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
|
132
src/components/lists/settings/background.vue
Normal file
132
src/components/lists/settings/background.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="unsplashBackgroundEnabled"
|
||||
class="card list-background-setting loader-container"
|
||||
:class="{ 'is-loading': backgroundService.loading}">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Set list background
|
||||
</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search for a background..."
|
||||
class="input is-expanded"
|
||||
v-model="backgroundSearchTerm"
|
||||
@keyup="() => newBackgroundSearch()"
|
||||
:class="{'is-loading': backgroundService.loading}"
|
||||
/>
|
||||
<div class="image-search-result">
|
||||
<a
|
||||
@click="() => setBackground(im.id)"
|
||||
class="image"
|
||||
v-for="im in backgroundSearchResult"
|
||||
:style="{'background-image': `url(${backgroundThumbs[im.id]})`}"
|
||||
:key="im.id">
|
||||
<a class="info" :href="`https://unsplash.com/@${im.info.author}`">
|
||||
{{ im.info.authorName }}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
class="button is-primary is-centered is-load-more-button is-outlined noshadow"
|
||||
@click="() => searchBackgrounds(currentPage + 1)"
|
||||
:disabled="backgroundService.loading"
|
||||
>
|
||||
<template v-if="backgroundService.loading">
|
||||
Loading...
|
||||
</template>
|
||||
<template v-else>
|
||||
Load more photos
|
||||
</template>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
||||
import {CURRENT_LIST} from '../../../store/mutation-types'
|
||||
|
||||
export default {
|
||||
name: 'background',
|
||||
data() {
|
||||
return {
|
||||
backgroundSearchTerm: '',
|
||||
backgroundSearchResult: [],
|
||||
backgroundService: null,
|
||||
backgroundThumbs: {},
|
||||
currentPage: 1,
|
||||
backgroundSearchTimeout: null,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
listId: {
|
||||
default: 0,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
unsplashBackgroundEnabled() {
|
||||
return this.$store.state.config.enabledBackgroundProviders.includes('unsplash')
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.backgroundService = new BackgroundUnsplashService()
|
||||
// Show the default collection of backgrounds
|
||||
this.newBackgroundSearch()
|
||||
},
|
||||
methods: {
|
||||
newBackgroundSearch() {
|
||||
// This is an extra method to reset a few things when searching to not break loading more photos.
|
||||
this.$set(this, 'backgroundSearchResult', [])
|
||||
this.$set(this, 'backgroundThumbs', {})
|
||||
this.searchBackgrounds()
|
||||
},
|
||||
searchBackgrounds(page = 1) {
|
||||
|
||||
if(this.backgroundSearchTimeout !== null) {
|
||||
clearTimeout(this.backgroundSearchTimeout)
|
||||
}
|
||||
|
||||
// We're using the timeout to not search on every keypress but with a 300ms delay.
|
||||
// If another key is pressed within these 300ms, the last search request is dropped and a new one is scheduled.
|
||||
this.backgroundSearchTimeout = setTimeout(() => {
|
||||
this.currentPage = page
|
||||
this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
|
||||
.then(r => {
|
||||
this.backgroundSearchResult = this.backgroundSearchResult.concat(r)
|
||||
r.forEach(b => {
|
||||
this.backgroundService.thumb(b)
|
||||
.then(t => {
|
||||
this.$set(this.backgroundThumbs, b.id, t)
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
}, 300)
|
||||
},
|
||||
setBackground(backgroundId) {
|
||||
// Don't set a background if we're in the process of setting one
|
||||
if (this.backgroundService.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
this.backgroundService.update({id: backgroundId, listId: this.listId})
|
||||
.then(l => {
|
||||
this.$store.commit(CURRENT_LIST, l)
|
||||
this.success({message: 'The background has been set successfully!'}, this)
|
||||
})
|
||||
.catch(e => {
|
||||
this.error(e, this)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="gantt-chart-container">
|
||||
<div class="gantt-options">
|
||||
<fancycheckbox v-model="showTaskswithoutDates" class="is-block">
|
||||
Show tasks which don't have dates set
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="loader-container" :class="{ 'is-loading': taskService.loading}">
|
||||
<div class="loader-container task-view-container" :class="{ 'is-loading': taskService.loading}">
|
||||
<div class="task-view">
|
||||
<div class="heading">
|
||||
<h1 class="title task-id" v-if="task.identifier === ''">
|
||||
|
Reference in New Issue
Block a user