Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/photos.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-11-22 15:01:42 +0300
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-11-22 15:01:42 +0300
commitcff7fd88f115beb4d33ddc2be32d7a580a44742e (patch)
treeccfec8efbab6ccfd4f6738649aea89e8c2e67b46 /src
parent9051ca63c5211f84582d4cc9f0634a4e20102390 (diff)
Timeline
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/router/index.js3
-rw-r--r--src/services/AlbumContent.js2
-rw-r--r--src/services/FileList.js2
-rw-r--r--src/services/FolderInfo.js2
-rw-r--r--src/services/PhotoSearch.js97
-rw-r--r--src/services/TaggedImages.js11
-rw-r--r--src/store/files.js2
-rw-r--r--src/store/index.js2
-rw-r--r--src/store/timeline.js58
-rw-r--r--src/views/Albums.vue2
-rw-r--r--src/views/Tags.vue4
-rw-r--r--src/views/Timeline.vue144
12 files changed, 275 insertions, 54 deletions
diff --git a/src/router/index.js b/src/router/index.js
index 2310dc27..802aabac 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -25,6 +25,7 @@ import Router from 'vue-router'
import Vue from 'vue'
import Albums from '../views/Albums'
+import Timeline from '../views/Timeline'
import Tags from '../views/Tags'
Vue.use(Router)
@@ -38,7 +39,7 @@ export default new Router({
routes: [
{
path: '/',
- component: Albums,
+ component: Timeline,
name: 'root',
},
{
diff --git a/src/services/AlbumContent.js b/src/services/AlbumContent.js
index 9bbacbc8..a35fcfd1 100644
--- a/src/services/AlbumContent.js
+++ b/src/services/AlbumContent.js
@@ -38,7 +38,7 @@ 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))
+ const list = response.data.map(data => genFileInfo(data))
// filter all the files and folders
let folder = {}
diff --git a/src/services/FileList.js b/src/services/FileList.js
index adbab9b2..82a47887 100644
--- a/src/services/FileList.js
+++ b/src/services/FileList.js
@@ -36,7 +36,7 @@ import { genFileInfo } from '../utils/fileUtils'
* @param {Object} [options] optional options for axios
* @returns {Array} the file list
*/
-export default async function(path, options) {
+export default async function(path, options = {}) {
options = Object.assign({
method: 'PROPFIND',
diff --git a/src/services/FolderInfo.js b/src/services/FolderInfo.js
index b58ddb79..bc9bfb55 100644
--- a/src/services/FolderInfo.js
+++ b/src/services/FolderInfo.js
@@ -43,5 +43,5 @@ export default async function(path) {
details: true,
})
- return genFileInfo(response.data, prefixPath)
+ return genFileInfo(response.data)
}
diff --git a/src/services/PhotoSearch.js b/src/services/PhotoSearch.js
index 8d3789ea..e2e34ca2 100644
--- a/src/services/PhotoSearch.js
+++ b/src/services/PhotoSearch.js
@@ -20,7 +20,6 @@
*
*/
-import { generateRemoteUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import client from './DavClient'
import { genFileInfo } from '../utils/fileUtils'
@@ -28,53 +27,69 @@ import { genFileInfo } from '../utils/fileUtils'
/**
* List files from a folder and filter out unwanted mimes
*
+ * @param {string} [path] not used
+ * @param {Object} [options] used for the cancellable requests
* @returns {Array} the file list
*/
-export default async function() {
- // fetch listing
- const response = await client.customRequest('', {
+export default async function(path = '', options) {
+
+ const prefixPath = `/files/${getCurrentUser().uid}`
+
+ options = Object.assign({
method: 'SEARCH',
headers: {
'content-Type': 'text/xml',
},
- url: generateRemoteUrl(`dav`),
data: `<?xml version="1.0" encoding="UTF-8"?>
-<d:searchrequest xmlns:d="DAV:"
- xmlns:oc="http://owncloud.org/ns"
- xmlns:nc="http://nextcloud.org/ns"
- xmlns:ocs="http://open-collaboration-services.org/ns">
- <d:basicsearch>
- <d:select>
- <d:prop>
- <d:getlastmodified />
- <d:getetag />
- <d:getcontenttype />
- <oc:fileid />
- <d:getcontentlength />
- <nc:has-preview />
- <oc:favorite />
- <d:resourcetype />
- </d:prop>
- </d:select>
- <d:from>
- <d:scope>
- <d:href>/files/${getCurrentUser().uid}/</d:href>
- <d:depth>infinity</d:depth>
- </d:scope>
- </d:from>
- <d:where>
- <d:like>
- <d:prop>
- <d:getcontenttype/>
- </d:prop>
- <d:literal>image/jpeg</d:literal>
- </d:like>
- </d:where>
- <d:orderby/>
- </d:basicsearch>
-</d:searchrequest>` })
+ <d:searchrequest xmlns:d="DAV:"
+ xmlns:oc="http://owncloud.org/ns"
+ xmlns:nc="http://nextcloud.org/ns"
+ xmlns:ocs="http://open-collaboration-services.org/ns">
+ <d:basicsearch>
+ <d:select>
+ <d:prop>
+ <d:getlastmodified />
+ <d:getetag />
+ <d:getcontenttype />
+ <oc:fileid />
+ <d:getcontentlength />
+ <nc:has-preview />
+ <oc:favorite />
+ <d:resourcetype />
+ </d:prop>
+ </d:select>
+ <d:from>
+ <d:scope>
+ <d:href>${prefixPath}</d:href>
+ <d:depth>infinity</d:depth>
+ </d:scope>
+ </d:from>
+ <d:where>
+ <d:like>
+ <d:prop>
+ <d:getcontenttype/>
+ </d:prop>
+ <d:literal>image/jpeg</d:literal>
+ </d:like>
+ <d:eq>
+ <d:prop>
+ <oc:owner-id/>
+ </d:prop>
+ <d:literal>admin</d:literal>
+ </d:eq>
+ </d:where>
+ <d:orderby/>
+ </d:basicsearch>
+ </d:searchrequest>`,
+ deep: true,
+ details: true,
+ }, options)
+
+ const response = await client.getDirectoryContents('', options)
- const entry = response.data
- console.info(entry)
+ return response.data
+ .map(data => genFileInfo(data))
+ // remove prefix path from full file path
+ .map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }))
}
diff --git a/src/services/TaggedImages.js b/src/services/TaggedImages.js
index fe812264..5fe4b3ef 100644
--- a/src/services/TaggedImages.js
+++ b/src/services/TaggedImages.js
@@ -55,9 +55,7 @@ import client from './DavClient'
*/
export default async function(id, options = {}) {
- const prefixPath = `/files/${getCurrentUser().uid}`
-
- const response = await client.getDirectoryContents(prefixPath, Object.assign({}, {
+ options = Object.assign({
method: 'REPORT',
data: `<?xml version="1.0"?>
<oc:filter-files
@@ -90,10 +88,13 @@ export default async function(id, options = {}) {
</oc:filter-rules>
</oc:filter-files>`,
details: true,
- }, options))
+ }, options)
+
+ const prefixPath = `/files/${getCurrentUser().uid}`
+ const response = await client.getDirectoryContents(prefixPath, options)
return response.data
- .map(data => genFileInfo(data, prefixPath))
+ .map(data => genFileInfo(data))
// remove prefix path from full file path
.map(data => Object.assign({}, data, { filename: data.filename.replace(prefixPath, '') }))
}
diff --git a/src/store/files.js b/src/store/files.js
index 2dc5fa8d..e74d0714 100644
--- a/src/store/files.js
+++ b/src/store/files.js
@@ -27,7 +27,7 @@ const state = {
const mutations = {
/**
- * Increment the number of contacts accepted
+ * Append or update given files
*
* @param {Object} state the store mutations
* @param {Array} files the store mutations
diff --git a/src/store/index.js b/src/store/index.js
index e48270b7..1ce9b647 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -26,6 +26,7 @@ import Vuex, { Store } from 'vuex'
import files from './files'
import folders from './folders'
import systemtags from './systemtags'
+import timeline from './timeline'
Vue.use(Vuex)
export default new Store({
@@ -33,6 +34,7 @@ export default new Store({
files,
folders,
systemtags,
+ timeline,
},
strict: process.env.NODE_ENV !== 'production',
diff --git a/src/store/timeline.js b/src/store/timeline.js
new file mode 100644
index 00000000..c343d764
--- /dev/null
+++ b/src/store/timeline.js
@@ -0,0 +1,58 @@
+/**
+ * @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/>.
+ *
+ */
+
+const state = {
+ timeline: [],
+}
+
+const mutations = {
+ /**
+ * Update timeline files list
+ *
+ * @param {Object} state the store mutations
+ * @param {Array} files the store mutations
+ */
+ updateTimeline(state, files) {
+ state.timeline = files
+ .map(file => file.fileid)
+ .filter(id => id >= 0)
+ },
+}
+
+const getters = {
+ timeline: state => state.timeline,
+}
+
+const actions = {
+ /**
+ * Update timeline files list
+ *
+ * @param {Object} context the store mutations
+ * @param {Number[]} files list of files ids
+ */
+ updateTimeline(context, files = []) {
+ // we want all the FileInfo! Folders included!
+ context.commit('updateTimeline', files)
+ },
+}
+
+export default { state, mutations, getters, actions }
diff --git a/src/views/Albums.vue b/src/views/Albums.vue
index 1063b6bb..54ff735c 100644
--- a/src/views/Albums.vue
+++ b/src/views/Albums.vue
@@ -155,7 +155,7 @@ export default {
},
beforeDestroy() {
- this.cancelRequest()
+ this.cancelRequest('Changed view')
},
methods: {
diff --git a/src/views/Tags.vue b/src/views/Tags.vue
index 44827e8f..30407b55 100644
--- a/src/views/Tags.vue
+++ b/src/views/Tags.vue
@@ -142,7 +142,7 @@ export default {
},
beforeDestroy() {
- this.cancelRequest()
+ this.cancelRequest('Changed view')
},
async beforeMount() {
@@ -161,7 +161,7 @@ export default {
methods: {
async fetchRootContent() {
// cancel any pending requests
- this.cancelRequest()
+ this.cancelRequest('Changed folder')
// close any potential opened viewer
OCA.Viewer.close()
diff --git a/src/views/Timeline.vue b/src/views/Timeline.vue
new file mode 100644
index 00000000..61f85ac0
--- /dev/null
+++ b/src/views/Timeline.vue
@@ -0,0 +1,144 @@
+<!--
+ - @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>
+ <!-- Errors handlers-->
+ <EmptyContent v-if="error === 404" illustration-name="folder">
+ {{ t('photos', 'This folder does not exists') }}
+ </EmptyContent>
+ <EmptyContent v-else-if="error">
+ {{ t('photos', 'An error occurred') }}
+ </EmptyContent>
+ <EmptyContent v-else-if="!loading && isEmpty" illustration-name="empty">
+ {{ t('photos', 'No photos in here') }}
+ </EmptyContent>
+
+ <!-- Folder content -->
+ <Grid v-else>
+ <File v-for="file in fileList" :key="file.fileid" v-bind="file" />
+ </Grid>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+import getPhotos from '../services/PhotoSearch'
+
+import EmptyContent from './EmptyContent'
+import File from '../components/File'
+import Grid from '../components/Grid'
+
+import cancelableRequest from '../utils/CancelableRequest'
+
+export default {
+ name: 'Timeline',
+ components: {
+ EmptyContent,
+ File,
+ Grid,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ error: null,
+ cancelRequest: () => {},
+ }
+ },
+
+ computed: {
+ // global lists
+ ...mapGetters([
+ 'files',
+ 'timeline',
+ ]),
+
+ fileList() {
+ return this.timeline
+ .map(id => this.files[id])
+ .filter(file => !!file)
+ },
+
+ // is current folder empty?
+ isEmpty() {
+ return this.fileList.length === 0
+ },
+ },
+
+ async beforeMount() {
+ this.fetchFolderContent()
+ },
+
+ beforeDestroy() {
+ this.cancelRequest()
+ },
+
+ methods: {
+ async fetchFolderContent() {
+ // cancel any pending requests
+ this.cancelRequest('Changed view')
+
+ // close any potential opened viewer
+ OCA.Viewer.close()
+
+ // if we don't already have some cached data let's show a loader
+ if (this.timeline.length === 0) {
+ this.$emit('update:loading', true)
+ }
+ this.error = null
+
+ // init cancellable request
+ const { request, cancel } = cancelableRequest(getPhotos)
+ this.cancelRequest = cancel
+
+ try {
+ // get content and current folder info
+ const files = await request()
+ this.$store.dispatch('updateTimeline', files)
+ this.$store.dispatch('appendFiles', files)
+ } catch (error) {
+ if (error.response && error.response.status) {
+ if (error.response.status === 404) {
+ this.error = 404
+ setTimeout(() => {
+ this.$router.push({ name: this.$route.name })
+ }, 3000)
+ } else {
+ this.error = error
+ }
+ }
+ // cancelled request, moving on...
+ console.error('Error fetching timeline', error)
+ } finally {
+ // done loading even with errors
+ this.$emit('update:loading', false)
+ }
+ },
+ },
+
+}
+</script>