summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-12-11 20:38:14 +0100
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-12-17 14:02:40 +0100
commitb501e72f31bbaf90d89e3c55ef2438b002244d0b (patch)
treeffe999b2cc4f3d2c31ef7aaff55e95d38825fb54
parent42024d21866b5fbb3b3644d0bfb81b17d2e605c9 (diff)
Grid virtual scroller
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r--package-lock.json80
-rw-r--r--package.json4
-rw-r--r--src/Photos.vue8
-rw-r--r--src/assets/grid-sizes.js15
-rw-r--r--src/components/File.vue5
-rw-r--r--src/components/Grid.vue3
-rw-r--r--src/components/GridRow.vue77
-rw-r--r--src/components/VirtualGrid.vue168
-rw-r--r--src/mixins/GridConfig.js46
-rw-r--r--src/services/DavRequest.js19
-rw-r--r--src/services/GridConfig.js51
-rw-r--r--src/services/PhotoSearch.js26
-rw-r--r--src/services/TaggedImages.js21
-rw-r--r--src/store/timeline.js4
-rw-r--r--src/utils/ArrayChunk.js39
-rw-r--r--src/utils/fileUtils.js2
-rw-r--r--src/utils/numberUtils.js (renamed from src/utils/numberUtil.js)0
-rw-r--r--src/views/Timeline.vue91
18 files changed, 567 insertions, 92 deletions
diff --git a/package-lock.json b/package-lock.json
index cf0851a8..78af04cd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1703,22 +1703,6 @@
"@babel/helper-plugin-utils": "^7.0.0"
}
},
- "@babel/polyfill": {
- "version": "7.7.0",
- "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.7.0.tgz",
- "integrity": "sha512-/TS23MVvo34dFmf8mwCisCbWGrfhbiWZSwBo6HkADTBhUa2Q/jWltyY/tpofz/b6/RIhqaqQcquptCirqIhOaQ==",
- "requires": {
- "core-js": "^2.6.5",
- "regenerator-runtime": "^0.13.2"
- },
- "dependencies": {
- "core-js": {
- "version": "2.6.11",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
- "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
- }
- }
- },
"@babel/preset-env": {
"version": "7.7.6",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.6.tgz",
@@ -1856,6 +1840,13 @@
"requires": {
"@nextcloud/event-bus": "^0.2.0",
"core-js": "3.2.1"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
+ "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw=="
+ }
}
},
"@nextcloud/axios": {
@@ -1907,6 +1898,13 @@
"integrity": "sha512-iLdyxluCehsRibR4R/nH3O8T9CcGoAaW3eWEdQW2qPtn6eEiBXASek5nWhXa5hko1GvE7koYia4FoTWuL85/Ng==",
"requires": {
"core-js": "3.2.1"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
+ "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw=="
+ }
}
},
"@nextcloud/router": {
@@ -3012,13 +3010,6 @@
"integrity": "sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w==",
"dev": true
},
- "cdav-library": {
- "version": "git+https://github.com/nextcloud/cdav-library.git#964c26c33c348315424d313b4ba9fc3aab58fed6",
- "from": "git+https://github.com/nextcloud/cdav-library.git#964c26c33c348315424d313b4ba9fc3aab58fed6",
- "requires": {
- "@babel/polyfill": "^7.7.0"
- }
- },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -3405,9 +3396,9 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
},
"core-js": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
- "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw=="
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz",
+ "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw=="
},
"core-js-compat": {
"version": "3.4.8",
@@ -3617,6 +3608,11 @@
"integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=",
"dev": true
},
+ "debounce": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
+ "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -8920,9 +8916,9 @@
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"serialize-javascript": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
- "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
+ "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true
},
"set-blocking": {
@@ -9147,9 +9143,9 @@
}
},
"source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "version": "0.5.16",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+ "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -9315,9 +9311,9 @@
}
},
"stream-shift": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
- "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true
},
"string-width": {
@@ -10044,9 +10040,9 @@
}
},
"terser": {
- "version": "4.3.9",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
- "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz",
+ "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -10063,16 +10059,16 @@
}
},
"terser-webpack-plugin": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz",
- "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==",
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
+ "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
"dev": true,
"requires": {
"cacache": "^12.0.2",
"find-cache-dir": "^2.1.0",
"is-wsl": "^1.1.0",
"schema-utils": "^1.0.0",
- "serialize-javascript": "^1.7.0",
+ "serialize-javascript": "^2.1.2",
"source-map": "^0.6.1",
"terser": "^4.1.2",
"webpack-sources": "^1.4.0",
diff --git a/package.json b/package.json
index 9b1b9c9a..5763db91 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,11 @@
"@nextcloud/router": "^0.1.0",
"@nextcloud/vue": "^1.2.2",
"camelcase": "^5.3.1",
- "cdav-library": "git+https://github.com/nextcloud/cdav-library.git",
+ "core-js": "^3.5.0",
+ "debounce": "^1.2.0",
"path-posix": "^1.0.0",
"qs": "^6.9.1",
+ "regenerator-runtime": "^0.13.3",
"url-parse": "^1.4.7",
"vue": "^2.6.10",
"vue-router": "^3.1.3",
diff --git a/src/Photos.vue b/src/Photos.vue
index acd5057a..50766d90 100644
--- a/src/Photos.vue
+++ b/src/Photos.vue
@@ -73,6 +73,14 @@ export default {
}
</script>
<style lang="scss" scoped>
+#app-content {
+ height: 100%;
+ display: flex;
+ flex-grow: 1;
+ flex-direction: column;
+ align-content: space-between;
+}
+
.app-navigation__photos::v-deep .app-navigation-entry-icon.icon-photos {
background-size: 20px;
}
diff --git a/src/assets/grid-sizes.js b/src/assets/grid-sizes.js
index 18f42ece..c17879ba 100644
--- a/src/assets/grid-sizes.js
+++ b/src/assets/grid-sizes.js
@@ -20,6 +20,9 @@
*
*/
+// for now we want to keep the same gap everywhere
+const gap = 8
+
/**
* Define the max width proportions
* The number (key) indicate the MAX size
@@ -31,53 +34,63 @@ module.exports = {
sizes: {
400: {
marginTop: 66, // same as grid-gap
- marginW: 8, // same as grid-gap
+ marginW: gap, // same as grid-gap
count: 3,
+ gap,
},
700: {
marginTop: 66,
marginW: 8, // same as grid-gap
count: 4,
+ gap,
},
1024: {
marginTop: 66,
marginW: 44,
count: 5,
+ gap,
},
1280: {
marginTop: 66,
marginW: 44,
count: 4,
+ gap,
},
1440: {
marginTop: 88,
marginW: 66,
count: 5,
+ gap,
},
1600: {
marginTop: 88,
marginW: 66,
count: 6,
+ gap,
},
2048: {
marginTop: 88,
marginW: 66,
count: 7,
+ gap,
},
2560: {
marginTop: 88,
marginW: 88,
count: 8,
+ gap,
},
3440: {
marginTop: 88,
marginW: 88,
count: 9,
+ gap,
},
max: {
marginTop: 88,
marginW: 88,
count: 10,
+ gap,
},
},
}
diff --git a/src/components/File.vue b/src/components/File.vue
index 60d18a41..0af883cf 100644
--- a/src/components/File.vue
+++ b/src/components/File.vue
@@ -100,12 +100,15 @@ export default {
isImage() {
return this.mime.startsWith('image')
},
+ srcUrl() {
+ return generateUrl(`/core/preview?fileId=${this.fileid}&x=${256}&y=${256}&a=true&v=${this.etag}`)
+ },
},
created() {
// Allow us to cancel the img loading on destroy
// use etag to force cache reload if file changed
- this.img.src = generateUrl(`/core/preview?fileId=${this.fileid}&x=${256}&y=${256}&a=true&v=${this.etag}`)
+ this.img.src = this.srcUrl
this.img.addEventListener('load', () => {
this.src = this.img.src
})
diff --git a/src/components/Grid.vue b/src/components/Grid.vue
index 85023f5a..822ecd10 100644
--- a/src/components/Grid.vue
+++ b/src/components/Grid.vue
@@ -43,7 +43,6 @@ export default {
display: grid;
align-items: center;
justify-content: center;
- gap: 8px;
grid-template-columns: repeat(10, 1fr);
position: relative;
@@ -69,6 +68,7 @@ $previous: 0;
$count: map-get($config, 'count');
$marginTop: map-get($config, 'marginTop');
$marginW: map-get($config, 'marginW');
+ $gap: map-get($config, 'gap');
// if this is the last entry, only use min-width
$rule: '(min-width: #{$previous}px) and (max-width: #{$size}px)';
@@ -78,6 +78,7 @@ $previous: 0;
@media #{$rule} {
.photos-grid {
+ gap: #{$gap}px;
padding: #{$marginTop}px #{$marginW}px #{$marginW}px #{$marginW}px;
grid-template-columns: repeat($count, 1fr);
}
diff --git a/src/components/GridRow.vue b/src/components/GridRow.vue
new file mode 100644
index 00000000..6ba385da
--- /dev/null
+++ b/src/components/GridRow.vue
@@ -0,0 +1,77 @@
+<!--
+ - @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>
+ <!-- Folder content -->
+ <transition-group
+ class="photos-grid-row"
+ role="grid"
+ name="list"
+ tag="div">
+ <slot />
+ </transition-group>
+</template>
+
+<script>
+export default {
+ name: 'GridRow',
+}
+</script>
+
+<style scoped lang="scss">
+.photos-grid-row {
+ display: grid;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ margin-bottom: 8px;
+ grid-template-columns: repeat(10, 1fr);
+ position: relative;
+}
+
+.list-move {
+ transition: transform 2s;
+}
+
+// TODO: use mixins/GridSizes as soon as node-sass supports it
+// needs node-sass 5.0 (with libsass 3.6)
+// https://github.com/sass/node-sass/pull/2312
+$previous: 0;
+@each $size, $config in get('sizes') {
+ $count: map-get($config, 'count');
+ $marginTop: map-get($config, 'marginTop');
+ $marginW: map-get($config, 'marginW');
+
+ // if this is the last entry, only use min-width
+ $rule: '(min-width: #{$previous}px) and (max-width: #{$size}px)';
+ @if $size == 'max' {
+ $rule: '(min-width: #{$previous}px)';
+ }
+
+ @media #{$rule} {
+ .photos-grid-row {
+ grid-template-columns: repeat($count, 1fr);
+ }
+ }
+ $previous: $size;
+}
+</style>
diff --git a/src/components/VirtualGrid.vue b/src/components/VirtualGrid.vue
new file mode 100644
index 00000000..26501a0d
--- /dev/null
+++ b/src/components/VirtualGrid.vue
@@ -0,0 +1,168 @@
+
+<template>
+ <Grid ref="grid">
+ <span v-show="shownFirstRow > 0"
+ ref="filler-top"
+ key="filler-top"
+ class="grid-filler grid-filler--top"
+ role="none"
+ :style="{paddingBottom: topPadding}" />
+ <component :is="component(item)"
+ v-for="(item, index) in shownList"
+ :key="item.fileid"
+ :ref="`item-${index}`"
+ :class="`row-${getRowNumber(index)}`"
+ v-bind="props(item)" />
+ <span v-show="shownLastRow < lastRow"
+ ref="filler-bottom"
+ key="filler-bottom"
+ class="grid-filler grid-filler--bottom"
+ role="none"
+ :style="{paddingBottom: bottomPadding}" />
+ </Grid>
+</template>
+
+<script>
+import debounce from 'debounce'
+import Grid from './Grid'
+import GridConfigMixin from '../mixins/GridConfig'
+
+export default {
+ name: 'VirtualGrid',
+ components: {
+ Grid,
+ },
+ mixins: [GridConfigMixin],
+
+ props: {
+ list: {
+ type: Array,
+ default: () => ([]),
+ },
+ props: {
+ type: Function,
+ default: () => ({}),
+ },
+ component: {
+ type: Function,
+ required: true,
+ },
+ },
+
+ data() {
+ return {
+ shownFirstRow: 0,
+ shownLastRow: this.getRowNumber(this.list.length - 1),
+ }
+ },
+
+ computed: {
+
+ shownList() {
+ return this.list.filter((item, index) => this.isVisible(index))
+ },
+
+ /**
+ * Calculate the top filler needed padding
+ * to compensate for the hidden items
+ * @returns {string}
+ */
+ topPadding() {
+ return `${this.shownFirstRow * 100}%`
+ },
+ /**
+ * Calculate the bottom filler needed padding
+ * to compensate for the hidden items
+ * Because bottomShift indicate the index of the last visible item,
+ * we need to calcuta ehow any rows there is to compensate
+ * between bottomShift and the end of the list
+ * @returns {string}
+ */
+ bottomPadding() {
+ return `${(this.lastRow - this.shownLastRow) * 100}%`
+ },
+
+ lastRow() {
+ return this.getRowNumber(this.list.length - 1)
+ },
+ },
+
+ created() {
+ window.addEventListener('scroll', this.onDocumentScroll)
+ },
+ mounted() {
+ this.onDocumentScroll()
+ },
+ beforeDestroy() {
+ window.removeEventListener('scroll', this.onDocumentScroll)
+ },
+
+ methods: {
+ debounceOnDocumentScroll: debounce(function() {
+ this.onDocumentScroll()
+ }, 50),
+
+ /**
+ * Handle document scroll
+ * Detect first visible/hidden to implement virtual scrolling
+ */
+ onDocumentScroll() {
+
+ // get the row height
+ const gridContainer = this.$refs.grid.$el
+ const gridStyles = getComputedStyle(gridContainer)
+ const rowHeight = parseFloat(gridStyles.gridTemplateColumns.split(' ')[0], 10)
+
+ // scrolled content
+ // rounding up to tens to make sure we only detect changes by steps of 10px
+ const scrolled = this.roundToTen(window.pageYOffset - this.gridConfig.marginTop)
+
+ // adding one above and one under to have a trigger area of one row
+ const shownFirstRow = Math.floor(scrolled / (rowHeight + this.gridConfig.gap)) - 1
+ const shownLastRow = Math.ceil(window.innerHeight / rowHeight) + shownFirstRow + 1
+
+ this.shownFirstRow = Math.max(shownFirstRow, 0) // the first shown row cannot be negative
+ this.shownLastRow = Math.min(shownLastRow, this.lastRow) // the last shown row cannot be lower than the last row
+
+ if (this.shownLastRow >= this.lastRow) {
+ this.$emit('bottomReached')
+ }
+
+ },
+
+ isVisible(index) {
+ const row = this.getRowNumber(index)
+ return row >= this.shownFirstRow && row < this.shownLastRow + 1
+ },
+
+ /**
+ * Return the row number of the provided index
+ *
+ * @param {number} index the index
+ * @returns {number}
+ */
+ getRowNumber(index) {
+ // in case the grid config is not here yet, let's
+ const count = this.gridConfig ? this.gridConfig.count : this.list.length
+ return Math.floor(index / count)
+ },
+
+ /**
+ * Round the provided number to a tens of its value
+ *
+ * @param {number} number the number to round
+ * @returns {number}
+ */
+ roundToTen(number) {
+ return Math.floor(number / 10) * 10
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.grid-filler {
+ // put the filler at the end of the row to put the next one into a new line
+ grid-column-end: -1;
+}
+</style>
diff --git a/src/mixins/GridConfig.js b/src/mixins/GridConfig.js
new file mode 100644
index 00000000..e7b18817
--- /dev/null
+++ b/src/mixins/GridConfig.js
@@ -0,0 +1,46 @@
+/**
+ * @copyright Copyright (c) 2018 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 getGridConfig from '../services/GridConfig'
+
+/**
+ * Get the current used grid config
+ */
+export default {
+ data() {
+ return {
+ gridConfig: {},
+ }
+ },
+
+ created() {
+ getGridConfig.$on('changed', val => {
+ this.gridConfig = val
+ })
+ console.debug('Current grid config', getGridConfig.gridConfig)
+ this.gridConfig = getGridConfig.gridConfig
+ },
+
+ beforeDestroy() {
+ getGridConfig.$off('changed', this.gridConfig)
+ },
+}
diff --git a/src/services/DavRequest.js b/src/services/DavRequest.js
index 9a2ca05a..79c7c586 100644
--- a/src/services/DavRequest.js
+++ b/src/services/DavRequest.js
@@ -19,20 +19,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+const props = `
+ <oc:fileid />
+ <d:getlastmodified />
+ <d:getetag />
+ <d:getcontenttype />
+ <d:getcontentlength />
+ <nc:has-preview />
+ <oc:favorite />
+ <d:resourcetype />`
+export { props }
export default `<?xml version="1.0"?>
<d:propfind 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 />
- <oc:fileid />
- <d:getcontentlength />
- <nc:has-preview />
- <oc:favorite />
- <d:resourcetype />
+ ${props}
</d:prop>
</d:propfind>`
diff --git a/src/services/GridConfig.js b/src/services/GridConfig.js
new file mode 100644
index 00000000..c3ee14b7
--- /dev/null
+++ b/src/services/GridConfig.js
@@ -0,0 +1,51 @@
+/**
+ * @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 Vue from 'vue'
+import { sizes } from '../assets/grid-sizes'
+
+export default new Vue({
+ data() {
+ return {
+ gridConfig: sizes.max,
+ }
+ },
+ watch: {
+ gridConfig(val) {
+ this.$emit('changed', val)
+ },
+ },
+ created() {
+ window.addEventListener('resize', this.handleWindowResize)
+ this.handleWindowResize()
+ },
+ beforeDestroy() {
+ window.removeEventListener('resize', this.handleWindowResize)
+ },
+ methods: {
+ handleWindowResize() {
+ // find the first grid size that fit the current window width
+ const currentSize = Object.keys(sizes).find(size => size > document.documentElement.clientWidth)
+ this.gridConfig = sizes[currentSize]
+ },
+ },
+})
diff --git a/src/services/PhotoSearch.js b/src/services/PhotoSearch.js
index 06d8dda3..1050ff8f 100644
--- a/src/services/PhotoSearch.js
+++ b/src/services/PhotoSearch.js
@@ -24,15 +24,25 @@ import { genFileInfo } from '../utils/fileUtils'
import { getCurrentUser } from '@nextcloud/auth'
import allowedMimes from './AllowedMimes'
import client from './DavClient'
+import { props } from './DavRequest'
+import { sizes } from '../assets/grid-sizes'
/**
* List files from a folder and filter out unwanted mimes
*
* @param {boolean} [onlyFavorites=false] not used
* @param {Object} [options] used for the cancellable requests
+ * @param {number} [options.page=0] which page to start (starts at 0)
+ * @param {number} [options.perPage] how many to display per page default is 5 times the max number per line from the grid-sizes config file
+ * @param {boolean} [options.full=false] get full data of the files
* @returns {Array} the file list
*/
export default async function(onlyFavorites = false, options = {}) {
+ // default function options
+ options = Object.assign({}, {
+ page: 0, // start at the first page
+ perPage: sizes.max.count * 10, // ten rows of the max width
+ }, options)
const prefixPath = `/files/${getCurrentUser().uid}`
@@ -65,18 +75,12 @@ export default async function(onlyFavorites = false, options = {}) {
<d:searchrequest xmlns:d="DAV:"
xmlns:oc="http://owncloud.org/ns"
xmlns:nc="http://nextcloud.org/ns"
+ xmlns:ns="https://github.com/icewind1991/SearchDAV/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 />
+ ${props}
</d:prop>
</d:select>
<d:from>
@@ -104,7 +108,11 @@ export default async function(onlyFavorites = false, options = {}) {
<d:prop><d:getlastmodified/></d:prop>
<d:descending/>
</d:order>
- </d:orderby>
+ </d:orderby>