mirror of https://github.com/nextcloud/photos
Tagging listing #1 & merging folder/tag component
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
parent
4174c7bea6
commit
68ed13a6ce
|
@ -21,33 +21,10 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<router-link :class="{'folder--clear': isEmpty}"
|
||||
class="folder"
|
||||
:to="to"
|
||||
:aria-label="ariaLabel">
|
||||
<transition name="fade">
|
||||
<div v-show="loaded"
|
||||
:class="`folder-content--grid-${fileList.length}`"
|
||||
class="folder-content"
|
||||
role="none">
|
||||
<img v-for="file in fileList"
|
||||
:key="file.fileid"
|
||||
:src="generateImgSrc(file)"
|
||||
alt=""
|
||||
@load="loaded = true">
|
||||
</div>
|
||||
</transition>
|
||||
<div
|
||||
class="folder-name">
|
||||
<span :class="[!isEmpty ? 'icon-white' : 'icon-dark', icon]"
|
||||
class="folder-name__icon"
|
||||
role="img" />
|
||||
<p :id="ariaUuid" class="folder-name__name">
|
||||
{{ basename }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="cover" role="none" />
|
||||
</router-link>
|
||||
<FolderTagPreview :id="fileid"
|
||||
:name="basename"
|
||||
:path="filename"
|
||||
:file-list="fileList" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -56,9 +33,14 @@ import { mapGetters } from 'vuex'
|
|||
|
||||
import getAlbumContent from '../services/AlbumContent'
|
||||
import cancelableRequest from '../utils/CancelableRequest'
|
||||
import FolderTagPreview from './FolderTagPreview'
|
||||
|
||||
export default {
|
||||
name: 'Folder',
|
||||
|
||||
components: {
|
||||
FolderTagPreview,
|
||||
},
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
|
@ -74,10 +56,6 @@ export default {
|
|||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'icon-folder',
|
||||
},
|
||||
showShared: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
@ -86,11 +64,13 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
cancelRequest: () => {},
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.cancelRequest('Navigated away')
|
||||
},
|
||||
computed: {
|
||||
// global lists
|
||||
...mapGetters([
|
||||
|
@ -105,38 +85,11 @@ export default {
|
|||
fileList() {
|
||||
return this.folderContent
|
||||
? this.folderContent
|
||||
.slice(0, 4) // only get the 4 first images
|
||||
.map(id => this.files[id])
|
||||
.filter(file => !!file)
|
||||
.slice(0, 4) // only get the 4 first images
|
||||
: []
|
||||
},
|
||||
|
||||
// folder is empty
|
||||
isEmpty() {
|
||||
return this.fileList.length === 0
|
||||
},
|
||||
|
||||
ariaUuid() {
|
||||
return `folder-${this.fileid}`
|
||||
},
|
||||
ariaLabel() {
|
||||
return t('photos', 'Open the "{name}" sub-directory', { name: this.basename })
|
||||
},
|
||||
|
||||
/**
|
||||
* We do not want encoded slashes when browsing by folder
|
||||
* so we generate a new valid route object, get the final url back
|
||||
* decode it and use it as a direct string, which vue-router
|
||||
* does not encode afterwards
|
||||
* @returns {string}
|
||||
*/
|
||||
to() {
|
||||
const route = Object.assign({}, this.$route, {
|
||||
// always remove first slash
|
||||
params: { path: this.filename.substr(1) },
|
||||
})
|
||||
return decodeURIComponent(this.$router.resolve(route).resolved.path)
|
||||
},
|
||||
},
|
||||
|
||||
async created() {
|
||||
|
@ -160,17 +113,6 @@ export default {
|
|||
beforeDestroy() {
|
||||
this.cancelRequest('Navigated away')
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateImgSrc({ fileid, etag }) {
|
||||
// use etag to force cache reload if file changed
|
||||
return generateUrl(`/core/preview?fileId=${fileid}&x=${256}&y=${256}&a=true&v=${etag}`)
|
||||
},
|
||||
|
||||
fetch() {
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- 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>
|
||||
<router-link :class="{'folder--clear': isEmpty}"
|
||||
class="folder"
|
||||
:to="to"
|
||||
:aria-label="ariaLabel">
|
||||
<!-- Images preview -->
|
||||
<transition name="fade">
|
||||
<div v-show="loaded"
|
||||
:class="`folder-content--grid-${fileList.length}`"
|
||||
class="folder-content"
|
||||
role="none">
|
||||
<img v-for="file in fileList"
|
||||
:key="file.fileid"
|
||||
:src="generateImgSrc(file)"
|
||||
alt=""
|
||||
@load="loaded = true">
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<div
|
||||
class="folder-name">
|
||||
<span :class="[!isEmpty ? 'icon-white' : 'icon-dark', icon]"
|
||||
class="folder-name__icon"
|
||||
role="img" />
|
||||
<p :id="ariaUuid" class="folder-name__name">
|
||||
{{ name }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="cover" role="none" />
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export default {
|
||||
name: 'FolderTagPreview',
|
||||
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: 'icon-folder',
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
fileList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
// folder is empty
|
||||
isEmpty() {
|
||||
return this.fileList.length === 0
|
||||
},
|
||||
|
||||
ariaUuid() {
|
||||
return `folder-${this.id}`
|
||||
},
|
||||
ariaLabel() {
|
||||
return t('photos', 'Open the "{name}" sub-directory', { name: this.name })
|
||||
},
|
||||
|
||||
/**
|
||||
* We do not want encoded slashes when browsing by folder
|
||||
* so we generate a new valid route object based on the
|
||||
* current named route, get the final url back, decode it
|
||||
* and use it as a direct string.
|
||||
* Which vue-router does not encode afterwards!
|
||||
* @returns {string}
|
||||
*/
|
||||
to() {
|
||||
// always remove first slash, the router
|
||||
// manage it automatically
|
||||
const regex = /^\/?(.+)/i
|
||||
const path = regex.exec(this.path)[1]
|
||||
|
||||
// apply to current route
|
||||
const route = Object.assign({}, this.$route, {
|
||||
params: { path },
|
||||
})
|
||||
// returning a string prevent vue-router to encode it again
|
||||
return decodeURIComponent(this.$router.resolve(route).resolved.path)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateImgSrc({ fileid, etag }) {
|
||||
// use etag to force cache reload if file changed
|
||||
return generateUrl(`/core/preview?fileId=${fileid}&x=${256}&y=${256}&a=true&v=${etag}`)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../mixins/FileFolder.scss';
|
||||
|
||||
.folder-content {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// folder layout if less than 4 pictures
|
||||
&--grid-1 {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
&--grid-2 {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
&--grid-3 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
img:first-child {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
&--grid-4 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
$name-height: 1.2rem;
|
||||
|
||||
.folder-name {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: opacity var(--animation-quick) ease-in-out;
|
||||
opacity: 1;
|
||||
&__icon {
|
||||
height: 40%;
|
||||
margin-top: calc(30% - #{$name-height} / 2); // center name+icon
|
||||
background-size: 40%;
|
||||
}
|
||||
&__name {
|
||||
overflow: hidden;
|
||||
height: $name-height;
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--color-main-background);
|
||||
text-shadow: 0 0 8px var(--color-main-text);
|
||||
font-size: $name-height;
|
||||
line-height: $name-height;
|
||||
}
|
||||
}
|
||||
|
||||
// Cover management empty/full
|
||||
.folder {
|
||||
// if no img, let's display the folder icon as default black
|
||||
&--clear {
|
||||
.folder-name__icon {
|
||||
opacity: .3;
|
||||
}
|
||||
.folder-name__name {
|
||||
color: var(--color-main-text);
|
||||
text-shadow: 0 0 8px var(--color-main-background);
|
||||
}
|
||||
}
|
||||
|
||||
// show the cover as background
|
||||
// if there are pictures in it
|
||||
// so we can sho the folder+name above it
|
||||
&:not(.folder--clear) {
|
||||
.cover {
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
// hide everything but pictures
|
||||
// on hover/active/focus
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
.folder-name,
|
||||
.cover {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,106 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- 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>
|
||||
<FolderTagPreview :id="id"
|
||||
icon="icon-tag"
|
||||
:name="displayName"
|
||||
:path="displayName"
|
||||
:file-list="fileList" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import getTaggedImages from '../services/TaggedImages'
|
||||
import cancelableRequest from '../utils/CancelableRequest'
|
||||
import FolderTagPreview from './FolderTagPreview'
|
||||
|
||||
export default {
|
||||
name: 'Tag',
|
||||
|
||||
components: {
|
||||
FolderTagPreview,
|
||||
},
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
displayName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
cancelRequest: () => {},
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.cancelRequest('Navigated away')
|
||||
},
|
||||
computed: {
|
||||
// global lists
|
||||
...mapGetters([
|
||||
'files',
|
||||
'tags',
|
||||
]),
|
||||
|
||||
// files list of the current folder
|
||||
folderContent() {
|
||||
return this.tags[this.id].files
|
||||
},
|
||||
fileList() {
|
||||
return this.folderContent
|
||||
? this.folderContent
|
||||
.map(id => this.files[id])
|
||||
.filter(file => !!file)
|
||||
.slice(0, 4) // only get the 4 first images
|
||||
: []
|
||||
},
|
||||
},
|
||||
|
||||
async created() {
|
||||
// init cancellable request
|
||||
const { request, cancel } = cancelableRequest(getTaggedImages)
|
||||
this.cancelRequest = cancel
|
||||
|
||||
try {
|
||||
// get data
|
||||
const files = await request(this.id, { shared: this.showShared })
|
||||
this.$store.dispatch('updateTag', { id: this.id, files })
|
||||
this.$store.dispatch('appendFiles', files)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status) {
|
||||
console.error('Failed to get folder content', this.id, error.response)
|
||||
}
|
||||
// else we just cancelled the request
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
|
@ -33,8 +33,14 @@ request.prepareRequestOptions = function(requestOptions, methodOptions) {
|
|||
if (methodOptions.cancelToken && typeof methodOptions.cancelToken === 'object') {
|
||||
requestOptions.cancelToken = Object.assign({}, requestOptions.cancelToken || {}, methodOptions.cancelToken)
|
||||
}
|
||||
|
||||
// exploit old method
|
||||
oldPrepareRequestOptions(requestOptions, methodOptions)
|
||||
|
||||
// allow us to override the request method
|
||||
if (methodOptions.method && typeof methodOptions.method === 'string') {
|
||||
requestOptions.method = methodOptions.method
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = request
|
||||
|
|
|
@ -38,14 +38,13 @@ export default async function(path = '/', options = {}) {
|
|||
|
||||
// fetch listing
|
||||
const response = await axios.get(prefixPath + path, options)
|
||||
|
||||
const list = response.data.map(data => genFileInfo(data, prefixPath))
|
||||
|
||||
// filter all the files and folders
|
||||
let folder = {}
|
||||
const folders = []
|
||||
const files = []
|
||||
console.info(allowedMimes)
|
||||
|
||||
for (const entry of list) {
|
||||
// is this the current provided path ?
|
||||
if (entry.filename === path) {
|
||||
|
|
|
@ -70,7 +70,7 @@ export default async function(path, options) {
|
|||
.then(result => getDirectoryFiles(result, remotePath + prefixPath, options.details))
|
||||
.then(files => processResponsePayload(response, files, options.details))
|
||||
|
||||
const list = data.map(data => genFileInfo(data, prefixPath))
|
||||
const list = data.map(data => genFileInfo(data))
|
||||
|
||||
// filter all the files and folders
|
||||
let folder = {}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { genFileInfo } from '../utils/fileUtils'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import client from './DavClient'
|
||||
|
||||
/**
|
||||
* Get tagged files based on provided tag id
|
||||
*
|
||||
* @param {number} id the tag id to filter
|
||||
* @param {Object} [options] optional options for axios
|
||||
* @returns {Array} the file list
|
||||
*/
|
||||
export default async function(id, options = {}) {
|
||||
|
||||
const prefixPath = `/files/${getCurrentUser().uid}`
|
||||
|
||||
const response = await client.getDirectoryContents(prefixPath, Object.assign({}, {
|
||||
method: 'REPORT',
|
||||
data: `<?xml version="1.0"?>
|
||||
<oc:filter-files
|
||||
xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns"
|
||||
xmlns:nc="http://nextcloud.org/ns"
|
||||
xmlns:ocs="http://open-collaboration-services.org/ns">
|
||||
<d:prop>
|
||||
<d:getlastmodified />
|
||||
<d:getetag />
|
||||
<d:getcontenttype />
|
||||
<d:resourcetype />
|
||||
<oc:fileid />
|
||||
<oc:permissions />
|
||||
<oc:size />
|
||||
<d:getcontentlength />
|
||||
<nc:has-preview />
|
||||
<nc:mount-type />
|
||||
<nc:is-encrypted />
|
||||
<ocs:share-permissions />
|
||||
<oc:tags />
|
||||
<oc:favorite />
|
||||
<oc:comments-unread />
|
||||
<oc:owner-id />
|
||||
<oc:owner-display-name />
|
||||
<oc:share-types />
|
||||
</d:prop>
|
||||
<oc:filter-rules>
|
||||
<oc:systemtag>${id}</oc:systemtag>
|
||||
</oc:filter-rules>
|
||||
</oc:filter-files>`,
|
||||
details: true,
|
||||
}, options))
|
||||
|
||||
return response.data
|
||||
.map(data => genFileInfo(data, prefixPath))
|
||||
// remove prefix path from full file path
|
||||
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }))
|
||||
}
|
|
@ -52,6 +52,7 @@ const mutations = {
|
|||
if (state.files[fileid]) {
|
||||
const subfolders = folders
|
||||
.map(folder => folder.fileid)
|
||||
// some invalid folders have an id of -1 (ext storage)
|
||||
.filter(id => id >= 0)
|
||||
Vue.set(state.files[fileid], 'folders', subfolders)
|
||||
}
|
||||
|
@ -64,7 +65,7 @@ const getters = {
|
|||
|
||||
const actions = {
|
||||
/**
|
||||
* Increment the number of contacts accepted
|
||||
* Update files, folders and their respective subfolders
|
||||
*
|
||||
* @param {Object} context the store mutations
|
||||
* @param {Object} data destructuring object
|
||||
|
@ -72,11 +73,21 @@ const actions = {
|
|||
* @param {Array} data.files list of files
|
||||
* @param {Array} data.folders list of folders within current folder
|
||||
*/
|
||||
updateFiles(context, { folder, files, folders }) {
|
||||
updateFiles(context, { folder, files = [], folders = [] } = {}) {
|
||||
// we want all the FileInfo! Folders included!
|
||||
context.commit('updateFiles', [folder, ...files, ...folders])
|
||||
context.commit('setSubFolders', { fileid: folder.fileid, folders })
|
||||
},
|
||||
|
||||
/**
|
||||
* Append or update given files
|
||||
*
|
||||
* @param {Object} context the store mutations
|
||||
* @param {Array} files list of files
|
||||
*/
|
||||
appendFiles(context, files = []) {
|
||||
context.commit('updateFiles', files)
|
||||
},
|
||||
}
|
||||
|
||||
export default { state, mutations, getters, actions }
|
||||
|
|
|
@ -61,7 +61,8 @@ const mutations = {
|
|||
const list = files.sort((a, b) => sortCompare(a, b, 'lastmod'))
|
||||
|
||||
// overwrite list
|
||||
Vue.set(state.tags[id], 'files', list.map(file => file.id))
|
||||
console.info(id, list)
|
||||
Vue.set(state.tags[id], 'files', list.map(file => file.fileid))
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,13 @@
|
|||
{{ t('photos', 'An error occurred') }}
|
||||
</EmptyContent>
|
||||
<EmptyContent v-else-if="!loading && isEmpty" illustration-name="empty">
|
||||
{{ t('photos', 'This folder does not contain pictures') }}
|
||||
{{ t('photos', 'No photos in here') }}
|
||||
</EmptyContent>
|
||||
|
||||
<!-- Folder content -->
|
||||
<Grid v-else>
|
||||
<Navigation v-if="folder" key="navigation" v-bind="folder" />
|
||||
|
||||
<Folder v-for="dir in folderList"
|
||||
:key="dir.fileid"
|
||||
v-bind="dir"
|
||||
|
@ -182,7 +183,7 @@ export default {
|
|||
if (error.response.status === 404) {
|
||||
this.error = 404
|
||||
setTimeout(() => {
|
||||
this.$router.push({ name: 'root' })
|
||||
this.$router.push({ name: this.$route.name })
|
||||
}, 3000)
|
||||
} else {
|
||||
this.error = error
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
<div v-if="haveIllustration" class="illustration" v-html="illustration" />
|
||||
<div v-else class="icon-error" />
|
||||
<h2><slot /></h2>
|
||||
<p v-show="$slots.desc">
|
||||
<slot name="desc" />
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -22,35 +22,29 @@
|
|||
|
||||
<template>
|
||||
<!-- Errors handlers-->
|
||||
<!-- <EmptyContent v-if="error === 404" illustration-name="folder">
|
||||
{{ t('photos', 'This folder does not exists') }}
|
||||
</EmptyContent>
|
||||
<EmptyContent v-else-if="error">
|
||||
<EmptyContent v-if="error">
|
||||
{{ t('photos', 'An error occurred') }}
|
||||
</EmptyContent>
|
||||
<EmptyContent v-else-if="!loading && isEmpty" illustration-name="empty">
|
||||
{{ t('photos', 'This folder does not contain pictures') }}
|
||||
</EmptyContent> -->
|
||||
{{ t('photos', 'No tags yet') }}
|
||||
<template #desc>
|
||||
{{ t('photos', 'Photos with tags will show up here') }}
|
||||
</template>
|
||||
</EmptyContent>
|
||||
|
||||
<!-- Folder content -->
|
||||
<Grid v-if="isRoot">
|
||||
<Grid v-else-if="isRoot">
|
||||
<Navigation v-if="tag"
|
||||
key="navigation"
|
||||
:basename="tagname"
|
||||
:filename="'/' + tagname"
|
||||
:root-title="t('photos', 'Tags')" />
|
||||
<Folder v-for="id in tagsNames"
|
||||
<Tag v-for="id in tagsNames"
|
||||
:key="id"
|
||||
v-bind="tags[id]"
|
||||
:fileid="id"
|
||||
:basename="tags[id].displayName"
|
||||
icon="icon-tag" />
|
||||
:basename="tags[id].displayName" />
|
||||
</Grid>
|
||||
<!-- <Grid v-else>
|
||||
<Navigation v-if="folder" key="navigation" v-bind="folder" />
|
||||
<Folder v-for="dir in folderList" :key="dir.id" :folder="dir" />
|
||||
<File v-for="file in fileList" :key="file.id" v-bind="file" />
|
||||
</Grid> -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -59,7 +53,7 @@ import { mapGetters } from 'vuex'
|
|||
import getSystemTags from '../services/SystemTags'
|
||||
|
||||
import EmptyContent from './EmptyContent'
|
||||
import Folder from '../components/Folder'
|
||||
import Tag from '../components/Tag'
|
||||
import File from '../components/File'
|
||||
import Grid from '../components/Grid'
|
||||
import Navigation from '../components/Navigation'
|
||||
|
@ -71,7 +65,7 @@ export default {
|
|||
components: {
|
||||
EmptyContent,
|
||||
File,
|
||||
Folder,
|
||||
Tag,
|
||||
Grid,
|
||||
Navigation,
|
||||
},
|
||||
|
@ -120,6 +114,10 @@ export default {
|
|||
isRoot() {
|
||||
return this.tagname === ''
|
||||
},
|
||||
|
||||
isEmpty() {
|
||||
return Object.keys(this.tagsNames).length === 0
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
|
Loading…
Reference in New Issue