зеркало из https://github.com/nextcloud/photos.git
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
Родитель
ae28cc9b2d
Коммит
2366aaf6ad
|
@ -24,4 +24,4 @@
|
|||
@include icon-color('folder', 'filetypes', $color-black, 1, true);
|
||||
}
|
||||
|
||||
@include icon-black-white('photos', 'photos', 1);
|
||||
@include icon-black-white('photos', 'photos', 2);
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1"><g transform="translate(-11.5 2.5)"><path d="M20.5 7.5a1 1 0 00-1 1v17c0 .5.6 1 1 1h20c.5 0 1-.5 1-1v-17c0-.4-.5-1-1-1zM21 9h19v14.5H21z"/><circle cx="24.8" cy="13" r="2.3"/><path d="M38.4 15.5L35 20.2 33.6 22l-1.2-1.4-3.5-3.5-4.5 4.3-3.6 3.3h19.9v-6.6zM14.5 2.5a1 1 0 00-1 1v17c0 .5.6 1 1 1h6v-3H15V4h19v3.5h1.5v-4c0-.4-.5-1-1-1h-20z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1"><g transform="translate(-11.5 2.5)"><path d="M20.5 7.5a1 1 0 00-1 1v17c0 .5.6 1 1 1h20c.5 0 1-.5 1-1v-17c0-.4-.5-1-1-1zM21 9h19v14.5H21z"/><circle cx="24.8" cy="13" r="2.3"/><path d="M38.4 15.5L35 20.2 33.6 22l-1.2-1.4-3.5-3.5-4.5 4.3-3.6 3.3h19.9v-6.6l-2.3-2.6zM14.5.5a1 1 0 00-1 1v17c0 .5.6 1 1 1h5.9v-3H15V2h19v6.6h1.5V1.5c0-.4-.5-1-1-1h-20z"/></g></svg>
|
До Ширина: | Высота: | Размер: 422 B После Ширина: | Высота: | Размер: 432 B |
|
@ -1853,8 +1853,7 @@
|
|||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"camelcase-keys": {
|
||||
"version": "2.1.0",
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"@nextcloud/l10n": "^0.2.1",
|
||||
"@nextcloud/router": "^0.1.0",
|
||||
"@nextcloud/vue": "^1.1.0",
|
||||
"camelcase": "^5.3.1",
|
||||
"cdav-library": "git+https://github.com/nextcloud/cdav-library.git",
|
||||
"path-posix": "^1.0.0",
|
||||
"qs": "^6.9.0",
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<AppNavigationItem to="/favorites" :title="t('photos', 'Favorites')" icon="icon-favorite" />
|
||||
<AppNavigationItem :to="{name: 'albums'}" :title="t('photos', 'Your albums')" icon="icon-files-dark" />
|
||||
<AppNavigationItem :to="{name: 'shared'}" :title="t('photos', 'Shared albums')" icon="icon-share" />
|
||||
<AppNavigationItem :to="{name: 'tags'}" :title="t('photos', 'Tags')" icon="icon-tag" />
|
||||
<AppNavigationItem :to="{name: 'tags'}" :title="t('photos', 'Tagged photos')" icon="icon-tag" />
|
||||
<AppNavigationItem :to="{name: 'maps'}" :title="t('photos', 'Locations')" icon="icon-address" />
|
||||
</AppNavigation>
|
||||
<AppContent :class="{ 'icon-loading': loading }">
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
class="folder-name__icon"
|
||||
role="img" />
|
||||
<p :id="ariaUuid" class="folder-name__name">
|
||||
{{ folder.basename }}
|
||||
{{ basename }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="cover" role="none" />
|
||||
|
@ -62,8 +62,16 @@ export default {
|
|||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
folder: {
|
||||
type: Object,
|
||||
basename: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
|
@ -88,7 +96,7 @@ export default {
|
|||
|
||||
// files list of the current folder
|
||||
folderContent() {
|
||||
return this.folders[this.folder.id]
|
||||
return this.folders[this.id]
|
||||
},
|
||||
fileList() {
|
||||
return this.folderContent
|
||||
|
@ -105,23 +113,24 @@ export default {
|
|||
},
|
||||
|
||||
ariaUuid() {
|
||||
return `folder-${this.folder.id}`
|
||||
return `folder-${this.id}`
|
||||
},
|
||||
ariaLabel() {
|
||||
return t('photos', 'Open the "{name}" sub-directory', { name: this.folder.basename })
|
||||
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
|
||||
* 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.folder.filename.substr(1) }
|
||||
});
|
||||
params: { path: this.filename.substr(1) },
|
||||
})
|
||||
return decodeURIComponent(this.$router.resolve(route).resolved.path)
|
||||
},
|
||||
},
|
||||
|
@ -133,9 +142,9 @@ export default {
|
|||
|
||||
try {
|
||||
// get data
|
||||
const { files, folders } = await request(this.folder.filename)
|
||||
const { files, folders } = await request(this.filename)
|
||||
// this.cancelRequest('Stop!')
|
||||
this.$store.dispatch('updateFolders', { id: this.folder.id, files, folders })
|
||||
this.$store.dispatch('updateFolders', { id: this.id, files, folders })
|
||||
this.$store.dispatch('updateFiles', { folder: this.folder, files, folders })
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status) {
|
||||
|
|
|
@ -56,6 +56,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rootTitle: {
|
||||
type: String,
|
||||
default: t('photos', 'Photos'),
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
@ -68,7 +72,7 @@ export default {
|
|||
},
|
||||
name() {
|
||||
if (this.isRoot) {
|
||||
return t('photos', 'Photos')
|
||||
return this.rootTitle
|
||||
}
|
||||
return this.basename
|
||||
},
|
||||
|
@ -93,14 +97,15 @@ export default {
|
|||
/**
|
||||
* 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
|
||||
* 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.parentPath.substr(1) }
|
||||
});
|
||||
params: { path: this.parentPath.substr(1) },
|
||||
})
|
||||
return decodeURIComponent(this.$router.resolve(route).resolved.path)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
*/
|
||||
|
||||
const request = require('webdav/dist/request')
|
||||
const merge = require('webdav/dist/merge')
|
||||
|
||||
const oldPrepareRequestOptions = request.prepareRequestOptions
|
||||
|
||||
|
@ -32,7 +31,7 @@ const oldPrepareRequestOptions = request.prepareRequestOptions
|
|||
request.prepareRequestOptions = function(requestOptions, methodOptions) {
|
||||
// add our cancelToken support
|
||||
if (methodOptions.cancelToken && typeof methodOptions.cancelToken === 'object') {
|
||||
requestOptions.cancelToken = merge(requestOptions.cancelToken || {}, methodOptions.cancelToken)
|
||||
requestOptions.cancelToken = Object.assign({}, requestOptions.cancelToken || {}, methodOptions.cancelToken)
|
||||
}
|
||||
// exploit old method
|
||||
oldPrepareRequestOptions(requestOptions, methodOptions)
|
||||
|
|
|
@ -55,7 +55,7 @@ export default new Router({
|
|||
children: [
|
||||
{
|
||||
path: ':path*',
|
||||
name: 'path',
|
||||
name: 'albumspath',
|
||||
component: Albums,
|
||||
},
|
||||
],
|
||||
|
@ -68,7 +68,7 @@ export default new Router({
|
|||
children: [
|
||||
{
|
||||
path: ':path*',
|
||||
name: 'path',
|
||||
name: 'sharedpath',
|
||||
component: Albums,
|
||||
},
|
||||
],
|
||||
|
@ -77,24 +77,18 @@ export default new Router({
|
|||
path: '/favorites',
|
||||
component: Tags,
|
||||
name: 'favorites',
|
||||
props,
|
||||
children: [
|
||||
{
|
||||
path: ':path*',
|
||||
name: 'path',
|
||||
component: Tags,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/tags',
|
||||
component: Tags,
|
||||
name: 'tags',
|
||||
props,
|
||||
props: route => ({
|
||||
tagname: route.params.tagname,
|
||||
}),
|
||||
children: [
|
||||
{
|
||||
path: ':path*',
|
||||
name: 'path',
|
||||
path: ':tagname',
|
||||
name: 'tagname',
|
||||
component: Tags,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -26,8 +26,7 @@ import { handleResponseCode, processResponsePayload } from 'webdav/dist/response
|
|||
import { normaliseHREF, normalisePath } from 'webdav/dist/url'
|
||||
import client, { remotePath } from './DavClient'
|
||||
import pathPosix from 'path-posix'
|
||||
import request from './DavRequest'
|
||||
import parseFile from '../utils/ParseFile'
|
||||
import { genFileInfo } from '../utils/fileUtils'
|
||||
|
||||
/**
|
||||
* List files from a folder and filter out unwanted mimes
|
||||
|
@ -37,6 +36,8 @@ import parseFile from '../utils/ParseFile'
|
|||
* @returns {Array} the file list
|
||||
*/
|
||||
export default async function(path, options) {
|
||||
|
||||
console.trace();
|
||||
options = Object.assign({
|
||||
method: 'PROPFIND',
|
||||
headers: {
|
||||
|
@ -44,7 +45,6 @@ export default async function(path, options) {
|
|||
Depth: options.deep ? 'infinity' : 1,
|
||||
},
|
||||
responseType: 'text',
|
||||
data: request,
|
||||
details: true,
|
||||
}, options)
|
||||
|
||||
|
@ -68,7 +68,7 @@ export default async function(path, options) {
|
|||
.then(result => getDirectoryFiles(result, remotePath, options.details))
|
||||
.then(files => processResponsePayload(response, files, options.details))
|
||||
|
||||
const list = data.map(data => parseFile(data, prefixPath))
|
||||
const list = data.map(data => genFileInfo(data, prefixPath))
|
||||
|
||||
// filter all the files and folders
|
||||
let folder = {}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import client from './DavClient'
|
||||
import request from './DavRequest'
|
||||
import parseFile from '../utils/ParseFile'
|
||||
import { genFileInfo } from '../utils/fileUtils'
|
||||
|
||||
/**
|
||||
* List files from a folder and filter out unwanted mimes
|
||||
|
@ -43,5 +43,5 @@ export default async function(path) {
|
|||
details: true,
|
||||
})
|
||||
|
||||
return parseFile(response.data, prefixPath)
|
||||
return genFileInfo(response.data, prefixPath)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
import { generateRemoteUrl } from '@nextcloud/router'
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import client from './DavClient'
|
||||
import parseFile from '../utils/ParseFile'
|
||||
import { genFileInfo } from '../utils/fileUtils'
|
||||
|
||||
/**
|
||||
* List files from a folder and filter out unwanted mimes
|
||||
|
|
|
@ -21,15 +21,17 @@
|
|||
*/
|
||||
|
||||
import client from './DavClient'
|
||||
import { generateRemoteUrl } from '@nextcloud/router'
|
||||
import { genFileInfo } from '../utils/fileUtils'
|
||||
|
||||
/**
|
||||
* List files from a folder and filter out unwanted mimes
|
||||
* List system tags
|
||||
*
|
||||
* @param {String} path the path relative to the user root
|
||||
* @param {Object} [options] optional options for axios
|
||||
* @returns {Array} the file list
|
||||
*/
|
||||
export default async function() {
|
||||
const response = await client.getDirectoryContents('/systemtags/', {
|
||||
export default async function(path, options = {}) {
|
||||
const response = await client.getDirectoryContents('/systemtags/', Object.assign({}, {
|
||||
data: `<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:"
|
||||
xmlns:oc="http://owncloud.org/ns">
|
||||
|
@ -42,15 +44,7 @@ export default async function() {
|
|||
</d:prop>
|
||||
</d:propfind>`,
|
||||
details: true,
|
||||
})
|
||||
|
||||
console.info(response)
|
||||
|
||||
const entry = response.data
|
||||
return Object.assign({
|
||||
id: parseInt(entry.props.fileid),
|
||||
isFavorite: entry.props.favorite !== '0',
|
||||
hasPreview: entry.props['has-preview'] !== 'false',
|
||||
}, entry)
|
||||
}, options))
|
||||
|
||||
return response.data.map(data => genFileInfo(data))
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
*/
|
||||
import Vue from 'vue'
|
||||
import { sortCompare } from '../utils/fileUtils'
|
||||
|
||||
const state = {
|
||||
paths: {},
|
||||
|
@ -39,9 +40,7 @@ const mutations = {
|
|||
if (files.length > 0) {
|
||||
const t0 = performance.now()
|
||||
// sort by last modified
|
||||
const list = files.sort((a, b) => {
|
||||
return new Date(b.lastmod).getTime() - new Date(a.lastmod).getTime()
|
||||
})
|
||||
const list = files.sort((a, b) => sortCompare(a, b, 'lastmod'))
|
||||
|
||||
// Set folder list
|
||||
Vue.set(state.folders, id, list.map(file => file.id))
|
||||
|
|
|
@ -20,50 +20,56 @@
|
|||
*
|
||||
*/
|
||||
import Vue from 'vue'
|
||||
import { sortCompare } from '../utils/fileUtils'
|
||||
|
||||
const state = {
|
||||
paths: {},
|
||||
tags: {},
|
||||
names: {},
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
/**
|
||||
* Index folders paths and ids
|
||||
* Order and save tags
|
||||
*
|
||||
* @param {Object} state vuex state
|
||||
* @param {Object} data destructuring object
|
||||
* @param {number} data.id current folder id
|
||||
* @param {Array} data.files list of files
|
||||
* @param {Array} tags the tags list
|
||||
*/
|
||||
updateTags(state, { id, files }) {
|
||||
if (files.length > 0) {
|
||||
// sort by last modified
|
||||
const list = files.sort((a, b) => {
|
||||
return new Date(b.lastmod).getTime() - new Date(a.lastmod).getTime()
|
||||
})
|
||||
updateTags(state, tags) {
|
||||
if (tags.length > 0) {
|
||||
// sort by basename
|
||||
const list = tags.sort((a, b) => sortCompare(a, b, 'displayName'))
|
||||
|
||||
// Set folder list
|
||||
Vue.set(state.tags, id, list.map(file => file.id))
|
||||
// store tag and its index
|
||||
list.forEach(tag => {
|
||||
Vue.set(state.tags, tag.id, tag)
|
||||
Vue.set(state.tags[tag.id], 'files', [])
|
||||
Vue.set(state.names, tag.displayName, tag.id)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Index folders paths and ids
|
||||
* Update tag files list
|
||||
*
|
||||
* @param {Object} state vuex state
|
||||
* @param {Object} data destructuring object
|
||||
* @param {string} data.path path of this folder
|
||||
* @param {number} data.id id of this folder
|
||||
* @param {number} data.id current tag id
|
||||
* @param {Object[]} data.files list of files
|
||||
*/
|
||||
addPath(state, { path, id }) {
|
||||
Vue.set(state.paths, path, id)
|
||||
updateTag(state, { id, files }) {
|
||||
// sort by last modified
|
||||
const list = files.sort((a, b) => sortCompare(a, b, 'lastmod'))
|
||||
|
||||
// overwrite list
|
||||
Vue.set(state.tags[id], 'files', list.map(file => file.id))
|
||||
},
|
||||
}
|
||||
|
||||
const getters = {
|
||||
tags: state => state.tags,
|
||||
tagsNames: state => state.names,
|
||||
tag: state => id => state.tags[id],
|
||||
tagId: state => path => state.paths[path],
|
||||
tagId: state => name => state.names[name],
|
||||
}
|
||||
|
||||
const actions = {
|
||||
|
@ -71,28 +77,22 @@ const actions = {
|
|||
* Update files and folders
|
||||
*
|
||||
* @param {Object} context vuex context
|
||||
* @param {Object} data destructuring object
|
||||
* @param {number} data.id current folder id
|
||||
* @param {Array} data.files list of files
|
||||
* @param {Array} data.folders list of folders
|
||||
* @param {Array} tags the tag list
|
||||
*/
|
||||
updateTags(context, { id, files, folders }) {
|
||||
context.commit('updateTags', { id, files })
|
||||
|
||||
// then add each folders path indexes
|
||||
folders.forEach(folder => context.commit('addPath', { path: folder.filename, id: folder.id }))
|
||||
updateTags(context, tags) {
|
||||
context.commit('updateTags', tags)
|
||||
},
|
||||
|
||||
/**
|
||||
* Index folders paths and ids
|
||||
* Update tag files list
|
||||
*
|
||||
* @param {Object} context vuex context
|
||||
* @param {Object} data destructuring object
|
||||
* @param {string} data.path path of this folder
|
||||
* @param {number} data.id id of this folder
|
||||
* @param {number} data.id current tag id
|
||||
* @param {Object[]} data.files list of files
|
||||
*/
|
||||
addPath(context, { path, id }) {
|
||||
context.commit('addPath', { path, id })
|
||||
updateTag(context, { id, files }) {
|
||||
context.commit('updateTag', { id, files })
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @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 camelcase from 'camelcase'
|
||||
import { isNumber } from './numberUtil'
|
||||
|
||||
/**
|
||||
* Get an url encoded path
|
||||
*
|
||||
* @param {String} path the full path
|
||||
* @returns {string} url encoded file path
|
||||
*/
|
||||
const encodeFilePath = function(path) {
|
||||
const pathSections = (path.startsWith('/') ? path : `/${path}`).split('/')
|
||||
let relativePath = ''
|
||||
pathSections.forEach((section) => {
|
||||
if (section !== '') {
|
||||
relativePath += '/' + encodeURIComponent(section)
|
||||
}
|
||||
})
|
||||
return relativePath
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract dir and name from file path
|
||||
*
|
||||
* @param {String} path the full path
|
||||
* @returns {String[]} [dirPath, fileName]
|
||||
*/
|
||||
const extractFilePaths = function(path) {
|
||||
const pathSections = path.split('/')
|
||||
const fileName = pathSections[pathSections.length - 1]
|
||||
const dirPath = pathSections.slice(0, pathSections.length - 1).join('/')
|
||||
return [dirPath, fileName]
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting comparison function
|
||||
*
|
||||
* @param {Object} fileInfo1 file 1 fileinfo
|
||||
* @param {Object} fileInfo2 file 2 fileinfo
|
||||
* @param {string} key key to sort with
|
||||
* @param {boolean} [asc=true] sort ascending?
|
||||
* @returns {number}
|
||||
*/
|
||||
const sortCompare = function(fileInfo1, fileInfo2, key, asc = true) {
|
||||
|
||||
// favorite always first
|
||||
if (fileInfo1.isFavorite && !fileInfo2.isFavorite) {
|
||||
return -1
|
||||
} else if (!fileInfo1.isFavorite && fileInfo2.isFavorite) {
|
||||
return 1
|
||||
}
|
||||
|
||||
// if this is a number, let's sort by integer
|
||||
if (isNumber(fileInfo1[key]) && isNumber(fileInfo2[key])) {
|
||||
return asc
|
||||
? Number(fileInfo2[key]) - Number(fileInfo1[key])
|
||||
: Number(fileInfo1[key]) - Number(fileInfo2[key])
|
||||
}
|
||||
|
||||
// else we sort by string, so let's sort directories first
|
||||
if (fileInfo1.type === 'directory' && fileInfo2.type !== 'directory') {
|
||||
return asc ? -1 : 1
|
||||
} else if (fileInfo1.type !== 'directory' && fileInfo2.type === 'directory') {
|
||||
return asc ? 1 : -1
|
||||
}
|
||||
|
||||
// if this is a date, let's sort by date
|
||||
if (isNumber(new Date(fileInfo1[key]).getTime()) && isNumber(new Date(fileInfo2[key])).getTime()) {
|
||||
return asc
|
||||
? new Date(fileInfo2[key]).getTime() - new Date(fileInfo1[key]).getTime()
|
||||
: new Date(fileInfo1[key]).getTime() - new Date(fileInfo2[key]).getTime()
|
||||
}
|
||||
|
||||
// finally sort by name
|
||||
return asc
|
||||
? fileInfo1[key].localeCompare(fileInfo2[key], OC.getLanguage())
|
||||
: -fileInfo1[key].localeCompare(fileInfo2[key], OC.getLanguage())
|
||||
}
|
||||
|
||||
const genFileInfo = function(obj) {
|
||||
const fileInfo = {}
|
||||
|
||||
Object.keys(obj).forEach(key => {
|
||||
const data = obj[key]
|
||||
|
||||
// flatten object if any
|
||||
if (!!data && typeof data === 'object') {
|
||||
Object.assign(fileInfo, genFileInfo(data))
|
||||
} else {
|
||||
// format key and add it to the fileInfo
|
||||
if (data === 'false') {
|
||||
fileInfo[camelcase(key)] = false
|
||||
} else if (data === 'true') {
|
||||
fileInfo[camelcase(key)] = true
|
||||
} else {
|
||||
fileInfo[camelcase(key)] = isNumber(data)
|
||||
? Number(data)
|
||||
: data
|
||||
}
|
||||
}
|
||||
})
|
||||
return fileInfo
|
||||
}
|
||||
|
||||
export { encodeFilePath, extractFilePaths, sortCompare, genFileInfo }
|
|
@ -20,18 +20,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format a file into a usable fileinfo object
|
||||
*
|
||||
* @param {Object} fileData file data returned by the webdav lib
|
||||
* @param {String} prefixPath path to substract from the files
|
||||
* @returns {Object}
|
||||
*/
|
||||
export default function(fileData, prefixPath = '') {
|
||||
const filename = fileData.filename.replace(prefixPath, '/').replace(/^\/\//, '/')
|
||||
return Object.assign({
|
||||
id: parseInt(fileData.props.fileid),
|
||||
isFavorite: fileData.props.favorite !== '0',
|
||||
hasPreview: fileData.props['has-preview'] !== 'false',
|
||||
}, fileData, { filename })
|
||||
const isNumber = function(num) {
|
||||
if (!num) {
|
||||
return false
|
||||
}
|
||||
return Number(num).toString() === num.toString()
|
||||
}
|
||||
|
||||
export { isNumber }
|
|
@ -35,7 +35,7 @@
|
|||
<!-- Folder content -->
|
||||
<Grid v-else>
|
||||
<Navigation v-if="folder" key="navigation" v-bind="folder" />
|
||||
<Folder v-for="dir in folderList" :key="dir.id" :folder="dir" />
|
||||
<Folder v-for="dir in folderList" :key="dir.id" v-bind="dir" />
|
||||
<File v-for="file in fileList" :key="file.id" v-bind="file" />
|
||||
</Grid>
|
||||
</template>
|
||||
|
|
|
@ -33,12 +33,23 @@
|
|||
</EmptyContent> -->
|
||||
|
||||
<!-- Folder content -->
|
||||
<Grid v-if="isRoot">
|
||||
<Navigation v-if="tag"
|
||||
key="navigation"
|
||||
:basename="tagname"
|
||||
:filename="'/' + tagname"
|
||||
:root-title="t('photos', 'Tags')" />
|
||||
<Folder v-for="id in tagsNames"
|
||||
:key="id"
|
||||
v-bind="tags[id]"
|
||||
:basename="tags[id].displayName"
|
||||
icon="icon-tag" />
|
||||
</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> -->
|
||||
<span>Test</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -64,9 +75,9 @@ export default {
|
|||
Navigation,
|
||||
},
|
||||
props: {
|
||||
path: {
|
||||
tagname: {
|
||||
type: String,
|
||||
default: '/',
|
||||
default: '',
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
|
@ -86,24 +97,66 @@ export default {
|
|||
...mapGetters([
|
||||
'files',
|
||||
'tags',
|
||||
'tagsNames',
|
||||
]),
|
||||
|
||||
// current tag id from current path
|
||||
tagId() {
|
||||
return this.$store.getters.tagId(this.tagname)
|
||||
},
|
||||
|
||||
// current tag
|
||||
tag() {
|
||||
return this.tags[this.tagId]
|
||||
},
|
||||
// files list of the current tag
|
||||
fileList() {
|
||||
return this.tag && this.tag.files
|
||||
.map(id => this.files[id])
|
||||
.filter(file => !!file)
|
||||
},
|
||||
|
||||
isRoot() {
|
||||
return this.tagname === ''
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
path(path) {
|
||||
console.debug('changed:', path)
|
||||
this.fetchFolderContent()
|
||||
tagname(name) {
|
||||
console.debug('changed:', name)
|
||||
this.fetchRootContent()
|
||||
},
|
||||
},
|
||||
|
||||
async beforeMount() {
|
||||
console.debug('beforemount: GRID')
|
||||
this.fetchFolderContent()
|
||||
this.fetchRootContent()
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchFolderContent() {
|
||||
await getSystemTags()
|
||||
async fetchRootContent() {
|
||||
console.debug('start: fetchRootContent', this.path)
|
||||
// cancel any pending requests
|
||||
this.cancelRequest()
|
||||
|
||||
// close any potential opened viewer
|
||||
OCA.Viewer.close()
|
||||
|
||||
// if we don't already have some cached data let's show a loader
|
||||
if (!this.tags[this.folderId]) {
|
||||
this.$emit('update:loading', true)
|
||||
}
|
||||
this.error = null
|
||||
|
||||
// init cancellable request
|
||||
const { request, cancel } = cancelableRequest(getSystemTags)
|
||||
this.cancelRequest = cancel
|
||||
|
||||
const tags = await request()
|
||||
this.$store.dispatch('updateTags', tags)
|
||||
|
||||
// done loading
|
||||
this.$emit('update:loading', false)
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ module.exports = {
|
|||
modules: [{
|
||||
test: /request.js/,
|
||||
replace: './src/patchedRequest.js',
|
||||
exclude: [/patchedRequest.js$/],
|
||||
}],
|
||||
}),
|
||||
],
|
||||
|
|
Загрузка…
Ссылка в новой задаче