Frontend: Add "download as zip" button to albums #424

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-08-12 05:59:06 +02:00
parent 2633e12dac
commit 20843e2f87
6 changed files with 1436 additions and 1065 deletions

View file

@ -501,11 +501,11 @@
}
},
"@babel/helper-split-export-declaration": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz",
"integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==",
"version": "7.11.0",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
"integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
"requires": {
"@babel/types": "^7.10.4"
"@babel/types": "^7.11.0"
}
},
"@babel/helper-validator-identifier": {
@ -1428,6 +1428,44 @@
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
},
"@nodelib/fs.scandir": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
"integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
"requires": {
"@nodelib/fs.stat": "2.0.3",
"run-parallel": "^1.1.9"
}
},
"@nodelib/fs.stat": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
"integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA=="
},
"@nodelib/fs.walk": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
"integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
"requires": {
"@nodelib/fs.scandir": "2.1.3",
"fastq": "^1.6.0"
}
},
"@npmcli/move-file": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz",
"integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==",
"requires": {
"mkdirp": "^1.0.4"
},
"dependencies": {
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
}
}
},
"@sinonjs/commons": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz",
@ -2006,6 +2044,15 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
},
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
"integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
"requires": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
}
},
"ajv": {
"version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
@ -2208,13 +2255,14 @@
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"asn1.js": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
"integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0"
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
},
"dependencies": {
"bn.js": {
@ -3386,6 +3434,11 @@
}
}
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
},
"clean-webpack-plugin": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
@ -3710,54 +3763,233 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
},
"copy-webpack-plugin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz",
"integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz",
"integrity": "sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==",
"requires": {
"cacache": "^12.0.3",
"find-cache-dir": "^2.1.0",
"glob-parent": "^3.1.0",
"globby": "^7.1.1",
"is-glob": "^4.0.1",
"loader-utils": "^1.2.3",
"minimatch": "^3.0.4",
"cacache": "^15.0.4",
"fast-glob": "^3.2.4",
"find-cache-dir": "^3.3.1",
"glob-parent": "^5.1.1",
"globby": "^11.0.1",
"loader-utils": "^2.0.0",
"normalize-path": "^3.0.0",
"p-limit": "^2.2.1",
"schema-utils": "^1.0.0",
"serialize-javascript": "^2.1.2",
"webpack-log": "^2.0.0"
"p-limit": "^3.0.1",
"schema-utils": "^2.7.0",
"serialize-javascript": "^4.0.0",
"webpack-sources": "^1.4.3"
},
"dependencies": {
"globby": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
"integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
},
"array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
},
"cacache": {
"version": "15.0.5",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz",
"integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==",
"requires": {
"array-union": "^1.0.1",
"dir-glob": "^2.0.0",
"glob": "^7.1.2",
"ignore": "^3.3.5",
"pify": "^3.0.0",
"slash": "^1.0.0"
"@npmcli/move-file": "^1.0.1",
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"glob": "^7.1.4",
"infer-owner": "^1.0.4",
"lru-cache": "^6.0.0",
"minipass": "^3.1.1",
"minipass-collect": "^1.0.2",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.2",
"mkdirp": "^1.0.3",
"p-map": "^4.0.0",
"promise-inflight": "^1.0.1",
"rimraf": "^3.0.2",
"ssri": "^8.0.0",
"tar": "^6.0.2",
"unique-filename": "^1.1.1"
}
},
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
},
"find-cache-dir": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
"integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
"requires": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"requires": {
"is-glob": "^4.0.1"
}
},
"globby": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
"integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"requires": {
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.1.1",
"ignore": "^5.1.4",
"merge2": "^1.3.0",
"slash": "^3.0.0"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"requires": {
"semver": "^6.0.0"
}
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"p-limit": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
"integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
"integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"requires": {
"p-try": "^2.0.0"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
},
"dependencies": {
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
}
}
},
"p-map": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"requires": {
"aggregate-error": "^3.0.0"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"requires": {
"find-up": "^4.0.0"
}
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"requires": {
"glob": "^7.1.3"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"ssri": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz",
"integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==",
"requires": {
"minipass": "^3.1.1"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
@ -4381,11 +4613,11 @@
}
},
"dir-glob": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
"integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"requires": {
"path-type": "^3.0.0"
"path-type": "^4.0.0"
}
},
"doctrine": {
@ -5741,6 +5973,67 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
"integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.0",
"merge2": "^1.3.0",
"micromatch": "^4.0.2",
"picomatch": "^2.2.1"
},
"dependencies": {
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"requires": {
"fill-range": "^7.0.1"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"requires": {
"to-regex-range": "^5.0.1"
}
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"requires": {
"is-glob": "^4.0.1"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -5751,6 +6044,14 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"fastq": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
"integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
"requires": {
"reusify": "^1.0.4"
}
},
"fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@ -6594,6 +6895,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"optional": true,
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
@ -6603,6 +6905,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"optional": true,
"requires": {
"is-extglob": "^2.1.0"
}
@ -7106,9 +7409,9 @@
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
},
"ignore": {
"version": "3.3.10",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
},
"import-cwd": {
"version": "2.1.0",
@ -7149,6 +7452,11 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
"indent-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
},
"indexes-of": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
@ -8402,6 +8710,11 @@
}
}
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@ -8536,6 +8849,30 @@
}
}
},
"minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-flush": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
"requires": {
"minipass": "^3.0.0"
}
},
"minipass-pipeline": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"requires": {
"minipass": "^3.0.0"
}
},
"minizlib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
@ -9435,13 +9772,12 @@
}
},
"parse-asn1": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
"integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
"integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
"requires": {
"asn1.js": "^4.0.0",
"asn1.js": "^5.2.0",
"browserify-aes": "^1.0.0",
"create-hash": "^1.1.0",
"evp_bytestokey": "^1.0.0",
"pbkdf2": "^3.0.3",
"safe-buffer": "^5.1.1"
@ -9512,7 +9848,8 @@
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
"optional": true
},
"path-exists": {
"version": "3.0.0",
@ -9555,19 +9892,9 @@
}
},
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"requires": {
"pify": "^3.0.0"
},
"dependencies": {
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
}
}
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
},
"pathval": {
"version": "1.1.0",
@ -11562,6 +11889,11 @@
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
},
"rework": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz",
@ -11623,6 +11955,11 @@
"inherits": "^2.0.1"
}
},
"run-parallel": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
"integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q=="
},
"run-queue": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@ -11726,9 +12063,12 @@
}
},
"serialize-javascript": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ=="
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"requires": {
"randombytes": "^2.1.0"
}
},
"serve-static": {
"version": "1.14.1",
@ -13430,9 +13770,9 @@
}
},
"vue-router": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.2.tgz",
"integrity": "sha512-n3Ok70hW0EpcJF4lcWIwSHAQbFTnIOLl/fhO8+oTs4jHNtBNsovcVvPZeTOyKEd8C3xF1Crft2ASuOiVT5K1mw=="
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.3.tgz",
"integrity": "sha512-BADg1mjGWX18Dpmy6bOGzGNnk7B/ZA0RxuA6qedY/YJwirMfKXIDzcccmHbQI0A6k5PzMdMloc0ElHfyOoX35A=="
},
"vue-style-loader": {
"version": "4.1.2",

View file

@ -42,7 +42,7 @@
"chrome-finder": "^1.0.7",
"clean-webpack-plugin": "^3.0.0",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^5.1.1",
"copy-webpack-plugin": "^6.0.3",
"core-js": "^3.6.5",
"cross-env": "^7.0.2",
"crypto-random-string": "^3.2.0",
@ -114,7 +114,7 @@
"vue-js-modal": "^2.0.0-rc.6",
"vue-loader": "^14.2.4",
"vue-luxon": "^0.7.3",
"vue-router": "^3.4.2",
"vue-router": "^3.4.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"vue2-filters": "^0.11.0",

View file

@ -21,6 +21,10 @@
<v-icon>share</v-icon>
</v-btn>
<v-btn icon @click.stop="download" v-if="$config.feature('download')" class="hidden-xs-only action-download" :title="$gettext('Download')">
<v-icon>get_app</v-icon>
</v-btn>
<v-btn icon v-if="settings.view === 'cards'" @click.stop="setView('list')">
<v-icon>view_list</v-icon>
</v-btn>
@ -62,106 +66,117 @@
</v-form>
</template>
<script>
import Event from "pubsub-js";
import Event from "pubsub-js";
import Notify from "../../common/notify";
export default {
name: 'p-album-toolbar',
props: {
album: Object,
filter: Object,
settings: Object,
refresh: Function,
filterChange: Function,
export default {
name: 'p-album-toolbar',
props: {
album: Object,
filter: Object,
settings: Object,
refresh: Function,
filterChange: Function,
},
data() {
const cameras = [{
ID: 0,
Name: this.$gettext('All Cameras')
}].concat(this.$config.get('cameras'));
const countries = [{
ID: '',
Name: this.$gettext('All Countries')
}].concat(this.$config.get('countries'));
return {
experimental: this.$config.get("experimental"),
isFullScreen: !!document.fullscreenElement,
categories: this.$config.albumCategories(),
searchExpanded: false,
options: {
'views': [
{value: 'mosaic', text: this.$gettext('Mosaic')},
{value: 'cards', text: this.$gettext('Cards')},
{value: 'list', text: this.$gettext('List')},
],
'countries': countries,
'cameras': cameras,
'sorting': [
{value: 'added', text: this.$gettext('Recently added')},
{value: 'edited', text: this.$gettext('Recently edited')},
{value: 'newest', text: this.$gettext('Newest first')},
{value: 'oldest', text: this.$gettext('Oldest first')},
{value: 'name', text: this.$gettext('Sort by file name')},
{value: 'similar', text: this.$gettext('Group by similarity')},
{value: 'relevance', text: this.$gettext('Most relevant')},
],
},
dialog: {
share: false,
upload: false,
edit: false,
},
labels: {
title: this.$gettext("Album Name"),
description: this.$gettext("Description"),
search: this.$gettext("Search"),
view: this.$gettext("View"),
country: this.$gettext("Country"),
camera: this.$gettext("Camera"),
sort: this.$gettext("Sort Order"),
category: this.$gettext("Category"),
},
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
growDesc: false,
};
},
methods: {
webdavUpload() {
this.dialog.share = false;
this.dialog.upload = true;
},
data() {
const cameras = [{
ID: 0,
Name: this.$gettext('All Cameras')
}].concat(this.$config.get('cameras'));
const countries = [{
ID: '',
Name: this.$gettext('All Countries')
}].concat(this.$config.get('countries'));
return {
experimental: this.$config.get("experimental"),
isFullScreen: !!document.fullscreenElement,
categories: this.$config.albumCategories(),
searchExpanded: false,
options: {
'views': [
{value: 'mosaic', text: this.$gettext('Mosaic')},
{value: 'cards', text: this.$gettext('Cards')},
{value: 'list', text: this.$gettext('List')},
],
'countries': countries,
'cameras': cameras,
'sorting': [
{value: 'added', text: this.$gettext('Recently added')},
{value: 'edited', text: this.$gettext('Recently edited')},
{value: 'newest', text: this.$gettext('Newest first')},
{value: 'oldest', text: this.$gettext('Oldest first')},
{value: 'name', text: this.$gettext('Sort by file name')},
{value: 'similar', text: this.$gettext('Group by similarity')},
{value: 'relevance', text: this.$gettext('Most relevant')},
],
},
dialog: {
share: false,
upload: false,
edit: false,
},
labels: {
title: this.$gettext("Album Name"),
description: this.$gettext("Description"),
search: this.$gettext("Search"),
view: this.$gettext("View"),
country: this.$gettext("Country"),
camera: this.$gettext("Camera"),
sort: this.$gettext("Sort Order"),
category: this.$gettext("Category"),
},
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
growDesc: false,
};
showUpload() {
Event.publish("dialog.upload");
},
methods: {
webdavUpload() {
this.dialog.share = false;
this.dialog.upload = true;
},
showUpload() {
Event.publish("dialog.upload");
},
expand() {
this.searchExpanded = !this.searchExpanded;
this.growDesc = !this.growDesc;
},
updateAlbum() {
if (this.album.wasChanged()) {
this.album.update();
}
},
dropdownChange() {
this.filterChange();
expand() {
this.searchExpanded = !this.searchExpanded;
this.growDesc = !this.growDesc;
},
updateAlbum() {
if (this.album.wasChanged()) {
this.album.update();
}
},
dropdownChange() {
this.filterChange();
if (window.innerWidth < 600) {
this.searchExpanded = false;
}
if (window.innerWidth < 600) {
this.searchExpanded = false;
}
if (this.filter.order !== this.album.Order) {
this.album.Order = this.filter.order;
this.updateAlbum()
}
},
setView(name) {
this.settings.view = name;
this.filterChange();
},
clearQuery() {
this.filter.q = '';
this.filterChange();
},
}
};
if (this.filter.order !== this.album.Order) {
this.album.Order = this.filter.order;
this.updateAlbum()
}
},
setView(name) {
this.settings.view = name;
this.filterChange();
},
clearQuery() {
this.filter.q = '';
this.filterChange();
},
download() {
this.onDownload(`/api/v1/albums/${this.album.UID}/dl?t=${this.$config.downloadToken()}`);
},
onDownload(path) {
Notify.success(this.$gettext("Downloading…"));
const link = document.createElement('a')
link.href = path;
link.download = "album.zip";
link.click();
},
}
};
</script>

View file

@ -30,7 +30,8 @@
#photoprism-info {
position: fixed;
bottom: 0;
right: 0;
left: 50%;
transform: translateX(-50%);
padding: 0 5px;
background-color: rgba(255, 255, 255, 0.6);
margin: 0;

View file

@ -43,453 +43,453 @@
</template>
<script>
import Photo from "model/photo";
import Album from "model/album";
import Event from "pubsub-js";
import Thumb from "model/thumb";
import Photo from "model/photo";
import Album from "model/album";
import Event from "pubsub-js";
import Thumb from "model/thumb";
export default {
name: 'p-page-album-photos',
props: {
staticFilter: Object
},
watch: {
'$route'() {
const query = this.$route.query;
this.filter.q = query['q'] ? query['q'] : '';
this.filter.camera = query['camera'] ? parseInt(query['camera']) : 0;
this.filter.country = query['country'] ? query['country'] : '';
this.settings.view = this.viewType();
this.lastFilter = {};
this.routeName = this.$route.name;
if (this.uid !== this.$route.params.uid) {
this.uid = this.$route.params.uid;
this.findAlbum().then(() => this.search());
} else {
this.search();
}
}
},
data() {
const uid = this.$route.params.uid;
export default {
name: 'p-page-album-photos',
props: {
staticFilter: Object
},
watch: {
'$route'() {
const query = this.$route.query;
const routeName = this.$route.name;
const order = query['order'] ? query['order'] : 'oldest';
const camera = query['camera'] ? parseInt(query['camera']) : 0;
const q = query['q'] ? query['q'] : '';
const country = query['country'] ? query['country'] : '';
const view = this.viewType();
const filter = {country: country, camera: camera, order: order, q: q};
const settings = {view: view};
return {
subscriptions: [],
listen: false,
dirty: false,
complete: false,
model: new Album(),
uid: uid,
this.filter.q = query['q'] ? query['q'] : '';
this.filter.camera = query['camera'] ? parseInt(query['camera']) : 0;
this.filter.country = query['country'] ? query['country'] : '';
this.settings.view = this.viewType();
this.lastFilter = {};
this.routeName = this.$route.name;
if (this.uid !== this.$route.params.uid) {
this.uid = this.$route.params.uid;
this.findAlbum().then(() => this.search());
} else {
this.search();
}
}
},
data() {
const uid = this.$route.params.uid;
const query = this.$route.query;
const routeName = this.$route.name;
const order = query['order'] ? query['order'] : 'oldest';
const camera = query['camera'] ? parseInt(query['camera']) : 0;
const q = query['q'] ? query['q'] : '';
const country = query['country'] ? query['country'] : '';
const view = this.viewType();
const filter = {country: country, camera: camera, order: order, q: q};
const settings = {view: view};
return {
subscriptions: [],
listen: false,
dirty: false,
complete: false,
model: new Album(),
uid: uid,
results: [],
scrollDisabled: true,
pageSize: 60,
offset: 0,
page: 0,
selection: this.$clipboard.selection,
settings: settings,
filter: filter,
lastFilter: {},
routeName: routeName,
loading: true,
viewer: {
results: [],
scrollDisabled: true,
pageSize: 60,
offset: 0,
page: 0,
selection: this.$clipboard.selection,
settings: settings,
filter: filter,
lastFilter: {},
routeName: routeName,
loading: true,
viewer: {
results: [],
loading: false,
},
};
loading: false,
},
};
},
methods: {
viewType() {
let queryParam = this.$route.query['view'];
let defaultType = window.localStorage.getItem("photo_view_type");
let storedType = window.localStorage.getItem("album_view_type");
if (queryParam) {
window.localStorage.setItem("album_view_type", queryParam);
return queryParam;
} else if (storedType) {
return storedType;
} else if (defaultType) {
return defaultType;
} else if (window.innerWidth < 960) {
return 'mosaic';
}
return 'cards';
},
methods: {
viewType() {
let queryParam = this.$route.query['view'];
let defaultType = window.localStorage.getItem("photo_view_type");
let storedType = window.localStorage.getItem("album_view_type");
openLocation(index) {
const photo = this.results[index];
if (queryParam) {
window.localStorage.setItem("album_view_type", queryParam);
return queryParam;
} else if (storedType) {
return storedType;
} else if (defaultType) {
return defaultType;
} else if (window.innerWidth < 960) {
return 'mosaic';
}
return 'cards';
},
openLocation(index) {
const photo = this.results[index];
if (photo.CellID && photo.CellID !== "zz") {
this.$router.push({name: "place", params: {q: photo.CellID}});
} else if (photo.PlaceID && photo.PlaceID !== "zz") {
this.$router.push({name: "place", params: {q: photo.PlaceID}});
} else if (photo.Country && photo.Country !== "zz") {
this.$router.push({name: "place", params: {q: "country:" + photo.Country}});
} else {
this.$notify.warn("unknown location");
}
},
editPhoto(index) {
let selection = this.results.map((p) => {
return p.getId()
});
// Open Edit Dialog
Event.publish("dialog.edit", {selection: selection, album: this.album, index: index});
},
openPhoto(index, showMerged) {
if (this.loading || this.viewer.loading || !this.results[index]) {
return false;
}
const selected = this.results[index];
if (showMerged && (selected.Type === 'video' || selected.Type === 'live')) {
if (this.results[index].isPlayable()) {
this.$modal.show('video', {video: selected, album: this.album});
} else {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
}
} else if (showMerged) {
this.$viewer.show(Thumb.fromFiles([selected]), 0)
} else {
this.viewerResults().then((results) => {
const thumbsIndex = results.findIndex(result => result.UID === selected.UID);
if (thumbsIndex < 0) {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
} else {
this.$viewer.show(Thumb.fromPhotos(results), thumbsIndex);
}
});
}
return true;
},
viewerResults() {
if (this.complete || this.loading || this.viewer.loading) {
return Promise.resolve(this.results);
}
if (this.viewer.results.length >= this.results.length) {
return Promise.resolve(this.viewer.results);
}
this.viewer.loading = true;
const count = Photo.limit();
const offset = 0;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return Photo.search(params).then(resp => {
// Success.
this.viewer.loading = false;
this.viewer.results = resp.models;
return Promise.resolve(this.viewer.results);
}, () => {
// Error.
this.viewer.loading = false;
return Promise.resolve(this.results);
});
},
loadMore() {
if (this.scrollDisabled) return;
this.scrollDisabled = true;
this.listen = false;
const count = this.dirty ? (this.page + 2) * this.pageSize : this.pageSize;
const offset = this.dirty ? 0 : this.offset;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
Photo.search(params).then(response => {
this.results = Photo.mergeResponse(this.results, response);
this.complete = (response.count < count);
this.scrollDisabled = this.complete;
if (this.complete) {
this.offset = offset;
if (this.results.length > 1) {
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} entries loaded"), {n: this.results.length}));
}
} else if (this.results.length >= Photo.limit()) {
this.offset = offset;
this.scrollDisabled = true;
this.complete = true;
this.$notify.warn(this.$gettext("Can't load more, limit reached"));
} else {
this.offset = offset + count;
this.page++;
}
}).catch(() => {
this.scrollDisabled = false;
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
if (offset === 0) {
this.viewerResults();
}
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
const len = this.filter.q.length;
if (len > 1 && len < 3) {
this.$notify.error(this.$gettext("Search term too short"));
return;
}
const query = {
view: this.settings.view
};
Object.assign(query, this.filter);
for (let key in query) {
if (query[key] === undefined || !query[key]) {
delete query[key];
}
}
if (JSON.stringify(this.$route.query) === JSON.stringify(query)) {
return
}
this.$router.replace({query: query});
},
searchParams() {
const params = {
count: this.pageSize,
offset: this.offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.filter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return params;
},
refresh() {
if (this.loading) {
return;
}
this.loading = true;
this.page = 0;
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
this.loadMore();
},
search() {
this.scrollDisabled = true;
// Don't query the same data more than once
if (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter)) {
this.$nextTick(() => this.$emit("scrollRefresh"));
return;
}
Object.assign(this.lastFilter, this.filter);
this.offset = 0;
this.page = 0;
this.loading = true;
this.listen = false;
this.complete = false;
const params = this.searchParams();
Photo.search(params).then(response => {
this.offset = this.pageSize;
this.results = response.models;
this.complete = (response.count < this.pageSize);
this.scrollDisabled = this.complete;
if (this.complete) {
if (!this.results.length) {
this.$notify.warn(this.$gettext("No entries found"));
} else if (this.results.length === 1) {
this.$notify.info(this.$gettext("One entry found"));
} else {
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} entries found"), {n: this.results.length}));
}
} else {
this.$notify.info(this.$gettext('More than 50 entries found'));
this.$nextTick(() => this.$emit("scrollRefresh"));
}
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
this.viewerResults();
});
},
findAlbum() {
return this.model.find(this.uid).then(m => {
this.model = m;
this.filter.order = m.Order;
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`;
return Promise.resolve(this.model)
});
},
onAlbumsUpdated(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
for (let i = 0; i < data.entities.length; i++) {
if (this.model.UID === data.entities[i].UID) {
let values = data.entities[i];
for (let key in values) {
if (values.hasOwnProperty(key)) {
this.model[key] = values[key];
}
}
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
if (this.filter.order !== this.model.Order) {
this.filter.order = this.model.Order;
this.updateQuery();
} else {
this.loadMore();
}
return;
}
}
},
updateResult(results, values) {
const model = results.find((m) => m.UID === values.UID);
if (model) {
for (let key in values) {
if (values.hasOwnProperty(key) && values[key] != null && typeof values[key] !== "object") {
model[key] = values[key];
}
}
}
},
removeResult(results, uid) {
const index = results.findIndex((m) => m.UID === uid);
if (index >= 0) {
results.splice(index, 1);
}
},
onUpdate(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
const type = ev.split('.')[1];
switch (type) {
case 'updated':
for (let i = 0; i < data.entities.length; i++) {
const values = data.entities[i];
this.updateResult(this.results, values);
this.updateResult(this.viewer.results, values);
}
break;
case 'restored':
this.dirty = true;
this.scrollDisabled = false;
this.complete = false;
this.loadMore();
break;
case 'archived':
this.dirty = true;
this.complete = false;
for (let i = 0; i < data.entities.length; i++) {
const uid = data.entities[i];
this.removeResult(this.results, uid);
this.removeResult(this.viewer.results, uid);
this.$clipboard.removeId(uid);
}
break;
}
},
},
created() {
this.findAlbum().then(() => this.search());
this.subscriptions.push(Event.subscribe("albums.updated", (ev, data) => this.onAlbumsUpdated(ev, data)));
this.subscriptions.push(Event.subscribe("photos", (ev, data) => this.onUpdate(ev, data)));
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
this.subscriptions.push(Event.subscribe("touchmove.bottom", () => this.loadMore()));
},
destroyed() {
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
if (photo.CellID && photo.CellID !== "zz") {
this.$router.push({name: "place", params: {q: photo.CellID}});
} else if (photo.PlaceID && photo.PlaceID !== "zz") {
this.$router.push({name: "place", params: {q: photo.PlaceID}});
} else if (photo.Country && photo.Country !== "zz") {
this.$router.push({name: "place", params: {q: "country:" + photo.Country}});
} else {
this.$notify.warn("unknown location");
}
},
};
editPhoto(index) {
let selection = this.results.map((p) => {
return p.getId()
});
// Open Edit Dialog
Event.publish("dialog.edit", {selection: selection, album: this.album, index: index});
},
openPhoto(index, showMerged) {
if (this.loading || this.viewer.loading || !this.results[index]) {
return false;
}
const selected = this.results[index];
if (showMerged && (selected.Type === 'video' || selected.Type === 'live')) {
if (this.results[index].isPlayable()) {
this.$modal.show('video', {video: selected, album: this.album});
} else {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
}
} else if (showMerged) {
this.$viewer.show(Thumb.fromFiles([selected]), 0)
} else {
this.viewerResults().then((results) => {
const thumbsIndex = results.findIndex(result => result.UID === selected.UID);
if (thumbsIndex < 0) {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
} else {
this.$viewer.show(Thumb.fromPhotos(results), thumbsIndex);
}
});
}
return true;
},
viewerResults() {
if (this.complete || this.loading || this.viewer.loading) {
return Promise.resolve(this.results);
}
if (this.viewer.results.length >= this.results.length) {
return Promise.resolve(this.viewer.results);
}
this.viewer.loading = true;
const count = Photo.limit();
const offset = 0;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return Photo.search(params).then(resp => {
// Success.
this.viewer.loading = false;
this.viewer.results = resp.models;
return Promise.resolve(this.viewer.results);
}, () => {
// Error.
this.viewer.loading = false;
return Promise.resolve(this.results);
});
},
loadMore() {
if (this.scrollDisabled) return;
this.scrollDisabled = true;
this.listen = false;
const count = this.dirty ? (this.page + 2) * this.pageSize : this.pageSize;
const offset = this.dirty ? 0 : this.offset;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
Photo.search(params).then(response => {
this.results = Photo.mergeResponse(this.results, response);
this.complete = (response.count < count);
this.scrollDisabled = this.complete;
if (this.complete) {
this.offset = offset;
if (this.results.length > 1) {
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} entries loaded"), {n: this.results.length}));
}
} else if (this.results.length >= Photo.limit()) {
this.offset = offset;
this.scrollDisabled = true;
this.complete = true;
this.$notify.warn(this.$gettext("Can't load more, limit reached"));
} else {
this.offset = offset + count;
this.page++;
}
}).catch(() => {
this.scrollDisabled = false;
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
if (offset === 0) {
this.viewerResults();
}
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
const len = this.filter.q.length;
if (len > 1 && len < 3) {
this.$notify.error(this.$gettext("Search term too short"));
return;
}
const query = {
view: this.settings.view
};
Object.assign(query, this.filter);
for (let key in query) {
if (query[key] === undefined || !query[key]) {
delete query[key];
}
}
if (JSON.stringify(this.$route.query) === JSON.stringify(query)) {
return
}
this.$router.replace({query: query});
},
searchParams() {
const params = {
count: this.pageSize,
offset: this.offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.filter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return params;
},
refresh() {
if (this.loading) {
return;
}
this.loading = true;
this.page = 0;
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
this.loadMore();
},
search() {
this.scrollDisabled = true;
// Don't query the same data more than once
if (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter)) {
this.$nextTick(() => this.$emit("scrollRefresh"));
return;
}
Object.assign(this.lastFilter, this.filter);
this.offset = 0;
this.page = 0;
this.loading = true;
this.listen = false;
this.complete = false;
const params = this.searchParams();
Photo.search(params).then(response => {
this.offset = this.pageSize;
this.results = response.models;
this.complete = (response.count < this.pageSize);
this.scrollDisabled = this.complete;
if (this.complete) {
if (!this.results.length) {
this.$notify.warn(this.$gettext("No entries found"));
} else if (this.results.length === 1) {
this.$notify.info(this.$gettext("One entry found"));
} else {
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} entries found"), {n: this.results.length}));
}
} else {
this.$notify.info(this.$gettext('More than 50 entries found'));
this.$nextTick(() => this.$emit("scrollRefresh"));
}
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
this.viewerResults();
});
},
findAlbum() {
return this.model.find(this.uid).then(m => {
this.model = m;
this.filter.order = m.Order;
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`;
return Promise.resolve(this.model)
});
},
onAlbumsUpdated(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
for (let i = 0; i < data.entities.length; i++) {
if (this.model.UID === data.entities[i].UID) {
let values = data.entities[i];
for (let key in values) {
if (values.hasOwnProperty(key)) {
this.model[key] = values[key];
}
}
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
if (this.filter.order !== this.model.Order) {
this.filter.order = this.model.Order;
this.updateQuery();
} else {
this.loadMore();
}
return;
}
}
},
updateResult(results, values) {
const model = results.find((m) => m.UID === values.UID);
if (model) {
for (let key in values) {
if (values.hasOwnProperty(key) && values[key] != null && typeof values[key] !== "object") {
model[key] = values[key];
}
}
}
},
removeResult(results, uid) {
const index = results.findIndex((m) => m.UID === uid);
if (index >= 0) {
results.splice(index, 1);
}
},
onUpdate(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
const type = ev.split('.')[1];
switch (type) {
case 'updated':
for (let i = 0; i < data.entities.length; i++) {
const values = data.entities[i];
this.updateResult(this.results, values);
this.updateResult(this.viewer.results, values);
}
break;
case 'restored':
this.dirty = true;
this.scrollDisabled = false;
this.complete = false;
this.loadMore();
break;
case 'archived':
this.dirty = true;
this.complete = false;
for (let i = 0; i < data.entities.length; i++) {
const uid = data.entities[i];
this.removeResult(this.results, uid);
this.removeResult(this.viewer.results, uid);
this.$clipboard.removeId(uid);
}
break;
}
},
},
created() {
this.findAlbum().then(() => this.search());
this.subscriptions.push(Event.subscribe("albums.updated", (ev, data) => this.onAlbumsUpdated(ev, data)));
this.subscriptions.push(Event.subscribe("photos", (ev, data) => this.onUpdate(ev, data)));
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
this.subscriptions.push(Event.subscribe("touchmove.bottom", () => this.loadMore()));
},
destroyed() {
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
}
},
};
</script>

View file

@ -15,6 +15,10 @@
<v-icon>refresh</v-icon>
</v-btn>
<v-btn icon @click.stop="download" v-if="$config.feature('download')" class="hidden-xs-only action-download" :title="$gettext('Download')">
<v-icon>get_app</v-icon>
</v-btn>
<v-btn icon v-if="settings.view === 'cards'" @click.stop="setView('list')">
<v-icon>view_list</v-icon>
</v-btn>
@ -81,466 +85,477 @@
</template>
<script>
import Photo from "model/photo";
import Album from "model/album";
import Event from "pubsub-js";
import Thumb from "model/thumb";
import Photo from "model/photo";
import Album from "model/album";
import Event from "pubsub-js";
import Thumb from "model/thumb";
import Notify from "common/notify";
export default {
name: 'p-page-album-photos',
props: {
staticFilter: Object
},
watch: {
'$route'() {
const query = this.$route.query;
this.filter.q = query['q'] ? query['q'] : '';
this.filter.camera = query['camera'] ? parseInt(query['camera']) : 0;
this.filter.country = query['country'] ? query['country'] : '';
this.settings.view = this.viewType();
this.lastFilter = {};
this.routeName = this.$route.name;
if (this.uid !== this.$route.params.uid) {
this.uid = this.$route.params.uid;
this.findAlbum().then(() => this.search());
} else {
this.search();
}
}
},
data() {
const uid = this.$route.params.uid;
export default {
name: 'p-page-album-photos',
props: {
staticFilter: Object
},
watch: {
'$route'() {
const query = this.$route.query;
const routeName = this.$route.name;
const order = query['order'] ? query['order'] : 'oldest';
const camera = query['camera'] ? parseInt(query['camera']) : 0;
const q = query['q'] ? query['q'] : '';
const country = query['country'] ? query['country'] : '';
const view = this.viewType();
const filter = {country: country, camera: camera, order: order, q: q};
const settings = {view: view};
return {
subscriptions: [],
listen: false,
dirty: false,
complete: false,
model: new Album(),
uid: uid,
results: [],
scrollDisabled: true,
pageSize: 60,
offset: 0,
page: 0,
selection: this.$clipboard.selection,
settings: settings,
filter: filter,
lastFilter: {},
routeName: routeName,
loading: true,
token: this.$route.params.token,
viewer: {
results: [],
loading: false,
},
};
},
methods: {
setView(name) {
this.settings.view = name;
this.updateQuery();
},
viewType() {
let queryParam = this.$route.query['view'];
let defaultType = window.localStorage.getItem("photo_view_type");
let storedType = window.localStorage.getItem("album_view_type");
this.filter.q = query['q'] ? query['q'] : '';
this.filter.camera = query['camera'] ? parseInt(query['camera']) : 0;
this.filter.country = query['country'] ? query['country'] : '';
this.settings.view = this.viewType();
this.lastFilter = {};
this.routeName = this.$route.name;
if (queryParam) {
window.localStorage.setItem("album_view_type", queryParam);
return queryParam;
} else if (storedType) {
return storedType;
} else if (defaultType) {
return defaultType;
} else if (window.innerWidth < 960) {
return 'mosaic';
}
return 'cards';
},
openLocation(index) {
const photo = this.results[index];
if (photo.CellID && photo.CellID !== "zz") {
this.$router.push({name: "place", params: {q: photo.CellID}});
} else if (photo.PlaceID && photo.PlaceID !== "zz") {
this.$router.push({name: "place", params: {q: photo.PlaceID}});
} else if (photo.Country && photo.Country !== "zz") {
this.$router.push({name: "place", params: {q: "country:" + photo.Country}});
} else {
this.$notify.warn("unknown location");
}
},
editPhoto(index) {
let selection = this.results.map((p) => {
return p.getId()
});
// Open Edit Dialog
Event.publish("dialog.edit", {selection: selection, album: this.album, index: index});
},
openPhoto(index, showMerged) {
if (this.loading || this.viewer.loading || !this.results[index]) {
return false;
}
const selected = this.results[index];
if (showMerged && (selected.Type === 'video' || selected.Type === 'live')) {
if (this.results[index].isPlayable()) {
this.$modal.show('video', {video: selected, album: this.album});
} else {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
}
} else if (showMerged) {
this.$viewer.show(Thumb.fromFiles([selected]), 0)
} else {
this.viewerResults().then((results) => {
const thumbsIndex = results.findIndex(result => result.UID === selected.UID);
if(thumbsIndex < 0) {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
} else {
this.$viewer.show(Thumb.fromPhotos(results), thumbsIndex);
}
});
}
return true;
},
viewerResults() {
if (this.complete || this.loading || this.viewer.loading) {
return Promise.resolve(this.results);
}
if (this.viewer.results.length >= this.results.length) {
return Promise.resolve(this.viewer.results);
}
this.viewer.loading = true;
const count = Photo.limit();
const offset = 0;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return Photo.search(params).then(resp => {
// Success.
this.viewer.loading = false;
this.viewer.results = resp.models;
return Promise.resolve(this.viewer.results);
}, () => {
// Error.
this.viewer.loading = false;
return Promise.resolve(this.results);
});
},
loadMore() {
if (this.scrollDisabled) return;
this.scrollDisabled = true;
this.listen = false;
const count = this.dirty ? (this.page + 2) * this.pageSize : this.pageSize;
const offset = this.dirty ? 0 : this.offset;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
Photo.search(params).then(response => {
this.results = Photo.mergeResponse(this.results, response);
this.complete = (response.count < count);
this.scrollDisabled = this.complete;
if (this.complete) {
this.offset = offset;
if (this.results.length > 1) {
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} entries loaded"), {n: this.results.length}));
}
} else if (this.results.length >= Photo.limit()) {
this.offset = offset;
this.scrollDisabled = true;
this.complete = true;
this.$notify.warn(this.$gettext("Can't load more, limit reached"));
} else {
this.offset = offset + count;
this.page++;
}
}).catch(() => {
this.scrollDisabled = false;
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
if(offset === 0) {
this.viewerResults();
}
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
const len = this.filter.q.length;
if (len > 1 && len < 3) {
this.$notify.error(this.$gettext("Search term too short"));
return;
}
const query = {
view: this.settings.view
};
Object.assign(query, this.filter);
for (let key in query) {
if (query[key] === undefined || !query[key]) {
delete query[key];
}
}
if (JSON.stringify(this.$route.query) === JSON.stringify(query)) {
return
}
this.$router.replace({query: query});
},
searchParams() {
const params = {
count: this.pageSize,
offset: this.offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.filter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return params;
},
refresh() {
if (this.loading) {
return;
}
this.loading = true;
this.page = 0;
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
this.loadMore();
},
search() {
this.scrollDisabled = true;
// Don't query the same data more than once
if (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter)) {
this.$nextTick(() => this.$emit("scrollRefresh"));
return;
}
Object.assign(this.lastFilter, this.filter);
this.offset = 0;
this.page = 0;
this.loading = true;
this.listen = false;
this.complete = false;
const params = this.searchParams();
Photo.search(params).then(response => {
this.offset = this.pageSize;
this.results = response.models;
this.complete = (response.count < this.pageSize);
this.scrollDisabled = this.complete;
if (this.complete) {
if (!this.results.length) {
this.$notify.warn(this.$gettext("No entries found"));
} else if (this.results.length === 1) {
this.$notify.info(this.$gettext("One entry found"));
} else {
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} entries found"), {n: this.results.length}));
}
} else {
this.$notify.info(this.$gettext('More than 50 entries found'));
this.$nextTick(() => this.$emit("scrollRefresh"));
}
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
this.viewerResults();
});
},
findAlbum() {
return this.model.find(this.uid).then(m => {
this.model = m;
this.filter.order = m.Order;
window.document.title = this.model.Title;
return Promise.resolve(this.model)
});
},
onAlbumsUpdated(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
for (let i = 0; i < data.entities.length; i++) {
if (this.model.UID === data.entities[i].UID) {
let values = data.entities[i];
for (let key in values) {
if (values.hasOwnProperty(key)) {
this.model[key] = values[key];
}
}
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
if (this.filter.order !== this.model.Order) {
this.filter.order = this.model.Order;
this.updateQuery();
} else {
this.loadMore();
}
return;
}
}
},
updateResult(results, values) {
const model = results.find((m) => m.UID === values.UID);
if (model) {
for (let key in values) {
if (values.hasOwnProperty(key) && values[key] != null && typeof values[key] !== "object") {
model[key] = values[key];
}
}
}
},
removeResult(results, uid) {
const index = results.findIndex((m) => m.UID === uid);
if (index >= 0) {
results.splice(index, 1);
}
},
onUpdate(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
const type = ev.split('.')[1];
switch (type) {
case 'updated':
for (let i = 0; i < data.entities.length; i++) {
const values = data.entities[i];
this.updateResult(this.results, values);
this.updateResult(this.viewer.results, values);
}
break;
case 'restored':
this.dirty = true;
this.scrollDisabled = false;
this.complete = false;
this.loadMore();
break;
case 'archived':
this.dirty = true;
this.complete = false;
for (let i = 0; i < data.entities.length; i++) {
const uid = data.entities[i];
this.removeResult(this.results, uid);
this.removeResult(this.viewer.results, uid);
this.$clipboard.removeId(uid);
}
break;
}
},
},
created() {
const token = this.$route.params.token;
if (this.$session.hasToken(token)) {
if (this.uid !== this.$route.params.uid) {
this.uid = this.$route.params.uid;
this.findAlbum().then(() => this.search());
} else {
this.$session.redeemToken(token).then(() => {
this.findAlbum().then(() => this.search());
this.search();
}
}
},
data() {
const uid = this.$route.params.uid;
const query = this.$route.query;
const routeName = this.$route.name;
const order = query['order'] ? query['order'] : 'oldest';
const camera = query['camera'] ? parseInt(query['camera']) : 0;
const q = query['q'] ? query['q'] : '';
const country = query['country'] ? query['country'] : '';
const view = this.viewType();
const filter = {country: country, camera: camera, order: order, q: q};
const settings = {view: view};
return {
subscriptions: [],
listen: false,
dirty: false,
complete: false,
model: new Album(),
uid: uid,
results: [],
scrollDisabled: true,
pageSize: 60,
offset: 0,
page: 0,
selection: this.$clipboard.selection,
settings: settings,
filter: filter,
lastFilter: {},
routeName: routeName,
loading: true,
token: this.$route.params.token,
viewer: {
results: [],
loading: false,
},
};
},
methods: {
setView(name) {
this.settings.view = name;
this.updateQuery();
},
viewType() {
let queryParam = this.$route.query['view'];
let defaultType = window.localStorage.getItem("photo_view_type");
let storedType = window.localStorage.getItem("album_view_type");
if (queryParam) {
window.localStorage.setItem("album_view_type", queryParam);
return queryParam;
} else if (storedType) {
return storedType;
} else if (defaultType) {
return defaultType;
} else if (window.innerWidth < 960) {
return 'mosaic';
}
return 'cards';
},
openLocation(index) {
const photo = this.results[index];
if (photo.CellID && photo.CellID !== "zz") {
this.$router.push({name: "place", params: {q: photo.CellID}});
} else if (photo.PlaceID && photo.PlaceID !== "zz") {
this.$router.push({name: "place", params: {q: photo.PlaceID}});
} else if (photo.Country && photo.Country !== "zz") {
this.$router.push({name: "place", params: {q: "country:" + photo.Country}});
} else {
this.$notify.warn("unknown location");
}
},
editPhoto(index) {
let selection = this.results.map((p) => {
return p.getId()
});
// Open Edit Dialog
Event.publish("dialog.edit", {selection: selection, album: this.album, index: index});
},
openPhoto(index, showMerged) {
if (this.loading || this.viewer.loading || !this.results[index]) {
return false;
}
const selected = this.results[index];
if (showMerged && (selected.Type === 'video' || selected.Type === 'live')) {
if (this.results[index].isPlayable()) {
this.$modal.show('video', {video: selected, album: this.album});
} else {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
}
} else if (showMerged) {
this.$viewer.show(Thumb.fromFiles([selected]), 0)
} else {
this.viewerResults().then((results) => {
const thumbsIndex = results.findIndex(result => result.UID === selected.UID);
if (thumbsIndex < 0) {
this.$viewer.show(Thumb.fromPhotos(this.results), index);
} else {
this.$viewer.show(Thumb.fromPhotos(results), thumbsIndex);
}
});
}
this.subscriptions.push(Event.subscribe("albums.updated", (ev, data) => this.onAlbumsUpdated(ev, data)));
this.subscriptions.push(Event.subscribe("photos", (ev, data) => this.onUpdate(ev, data)));
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
this.subscriptions.push(Event.subscribe("touchmove.bottom", () => this.loadMore()));
return true;
},
destroyed() {
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
viewerResults() {
if (this.complete || this.loading || this.viewer.loading) {
return Promise.resolve(this.results);
}
if (this.viewer.results.length >= this.results.length) {
return Promise.resolve(this.viewer.results);
}
this.viewer.loading = true;
const count = Photo.limit();
const offset = 0;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return Photo.search(params).then(resp => {
// Success.
this.viewer.loading = false;
this.viewer.results = resp.models;
return Promise.resolve(this.viewer.results);
}, () => {
// Error.
this.viewer.loading = false;
return Promise.resolve(this.results);
});
},
loadMore() {
if (this.scrollDisabled) return;
this.scrollDisabled = true;
this.listen = false;
const count = this.dirty ? (this.page + 2) * this.pageSize : this.pageSize;
const offset = this.dirty ? 0 : this.offset;
const params = {
count: count,
offset: offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.lastFilter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
Photo.search(params).then(response => {
this.results = Photo.mergeResponse(this.results, response);
this.complete = (response.count < count);
this.scrollDisabled = this.complete;
if (this.complete) {
this.offset = offset;
if (this.results.length > 1) {
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} entries loaded"), {n: this.results.length}));
}
} else if (this.results.length >= Photo.limit()) {
this.offset = offset;
this.scrollDisabled = true;
this.complete = true;
this.$notify.warn(this.$gettext("Can't load more, limit reached"));
} else {
this.offset = offset + count;
this.page++;
}
}).catch(() => {
this.scrollDisabled = false;
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
if (offset === 0) {
this.viewerResults();
}
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
const len = this.filter.q.length;
if (len > 1 && len < 3) {
this.$notify.error(this.$gettext("Search term too short"));
return;
}
const query = {
view: this.settings.view
};
Object.assign(query, this.filter);
for (let key in query) {
if (query[key] === undefined || !query[key]) {
delete query[key];
}
}
if (JSON.stringify(this.$route.query) === JSON.stringify(query)) {
return
}
this.$router.replace({query: query});
},
searchParams() {
const params = {
count: this.pageSize,
offset: this.offset,
album: this.uid,
filter: this.model.Filter ? this.model.Filter : "",
merged: true,
};
Object.assign(params, this.filter);
if (this.staticFilter) {
Object.assign(params, this.staticFilter);
}
return params;
},
refresh() {
if (this.loading) {
return;
}
this.loading = true;
this.page = 0;
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
this.loadMore();
},
search() {
this.scrollDisabled = true;
// Don't query the same data more than once
if (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter)) {
this.$nextTick(() => this.$emit("scrollRefresh"));
return;
}
Object.assign(this.lastFilter, this.filter);
this.offset = 0;
this.page = 0;
this.loading = true;
this.listen = false;
this.complete = false;
const params = this.searchParams();
Photo.search(params).then(response => {
this.offset = this.pageSize;
this.results = response.models;
this.complete = (response.count < this.pageSize);
this.scrollDisabled = this.complete;
if (this.complete) {
if (!this.results.length) {
this.$notify.warn(this.$gettext("No entries found"));
} else if (this.results.length === 1) {
this.$notify.info(this.$gettext("One entry found"));
} else {
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} entries found"), {n: this.results.length}));
}
} else {
this.$notify.info(this.$gettext('More than 50 entries found'));
this.$nextTick(() => this.$emit("scrollRefresh"));
}
}).finally(() => {
this.dirty = false;
this.loading = false;
this.listen = true;
this.viewerResults();
});
},
findAlbum() {
return this.model.find(this.uid).then(m => {
this.model = m;
this.filter.order = m.Order;
window.document.title = this.model.Title;
return Promise.resolve(this.model)
});
},
onAlbumsUpdated(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
for (let i = 0; i < data.entities.length; i++) {
if (this.model.UID === data.entities[i].UID) {
let values = data.entities[i];
for (let key in values) {
if (values.hasOwnProperty(key)) {
this.model[key] = values[key];
}
}
window.document.title = `${this.$config.get("siteTitle")}: ${this.model.Title}`
this.dirty = true;
this.complete = false;
this.scrollDisabled = false;
if (this.filter.order !== this.model.Order) {
this.filter.order = this.model.Order;
this.updateQuery();
} else {
this.loadMore();
}
return;
}
}
},
};
updateResult(results, values) {
const model = results.find((m) => m.UID === values.UID);
if (model) {
for (let key in values) {
if (values.hasOwnProperty(key) && values[key] != null && typeof values[key] !== "object") {
model[key] = values[key];
}
}
}
},
removeResult(results, uid) {
const index = results.findIndex((m) => m.UID === uid);
if (index >= 0) {
results.splice(index, 1);
}
},
onUpdate(ev, data) {
if (!this.listen) return;
if (!data || !data.entities) {
return
}
const type = ev.split('.')[1];
switch (type) {
case 'updated':
for (let i = 0; i < data.entities.length; i++) {
const values = data.entities[i];
this.updateResult(this.results, values);
this.updateResult(this.viewer.results, values);
}
break;
case 'restored':
this.dirty = true;
this.scrollDisabled = false;
this.complete = false;
this.loadMore();
break;
case 'archived':
this.dirty = true;
this.complete = false;
for (let i = 0; i < data.entities.length; i++) {
const uid = data.entities[i];
this.removeResult(this.results, uid);
this.removeResult(this.viewer.results, uid);
this.$clipboard.removeId(uid);
}
break;
}
},
download() {
this.onDownload(`/api/v1/albums/${this.uid}/dl?t=${this.$config.downloadToken()}`);
},
onDownload(path) {
Notify.success(this.$gettext("Downloading…"));
const link = document.createElement('a')
link.href = path;
link.download = "album.zip";
link.click();
},
},
created() {
const token = this.$route.params.token;
if (this.$session.hasToken(token)) {
this.findAlbum().then(() => this.search());
} else {
this.$session.redeemToken(token).then(() => {
this.findAlbum().then(() => this.search());
});
}
this.subscriptions.push(Event.subscribe("albums.updated", (ev, data) => this.onAlbumsUpdated(ev, data)));
this.subscriptions.push(Event.subscribe("photos", (ev, data) => this.onUpdate(ev, data)));
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
this.subscriptions.push(Event.subscribe("touchmove.bottom", () => this.loadMore()));
},
destroyed() {
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
}
},
};
</script>