Frontend: Limit clipboard size to 500 items #481
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
b39d660686
commit
a845ebd4ce
8 changed files with 1744 additions and 1731 deletions
214
frontend/package-lock.json
generated
214
frontend/package-lock.json
generated
|
@ -46,15 +46,15 @@
|
|||
}
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz",
|
||||
"integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==",
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz",
|
||||
"integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.0",
|
||||
"@babel/generator": "^7.11.4",
|
||||
"@babel/helper-module-transforms": "^7.11.0",
|
||||
"@babel/helpers": "^7.10.4",
|
||||
"@babel/parser": "^7.11.1",
|
||||
"@babel/parser": "^7.11.4",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/traverse": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
|
@ -68,18 +68,20 @@
|
|||
"source-map": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"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==",
|
||||
"@babel/generator": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz",
|
||||
"integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0"
|
||||
"@babel/types": "^7.11.0",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
|
||||
"integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA=="
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz",
|
||||
"integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA=="
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.10.4",
|
||||
|
@ -91,32 +93,6 @@
|
|||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
|
||||
"integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.0",
|
||||
"@babel/helper-function-name": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/parser": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0",
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
|
@ -7130,9 +7106,9 @@
|
|||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||
},
|
||||
"hls.js": {
|
||||
"version": "0.14.9",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.9.tgz",
|
||||
"integrity": "sha512-5j1ONTvIzcIxCtg2eafikFbZ3b/9fDhR6u871LmK7jZ44/Qdc2G4xaSBCwcVK61gz7kTyiobaAhB++2M4J58rQ==",
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.14.10.tgz",
|
||||
"integrity": "sha512-muBt5Gxhapgk2uV8aEKRYe9GJ6AI5niFfDuOd8ZXHga519RNJ0+QAeRPdEpl1QKMqj1lT/6r/WKVnLL+ePB6ow==",
|
||||
"requires": {
|
||||
"eventemitter3": "^4.0.3",
|
||||
"url-toolkit": "^2.1.6"
|
||||
|
@ -8589,9 +8565,9 @@
|
|||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.24.1.tgz",
|
||||
"integrity": "sha512-CgnIMKAWT0ghcuWFfCWBnWGOddM0zu6c4wZAWmD0NN7MZTnro0+833DF6tJep+xlxRPg4KtsYEHYLfTMBQKwYg=="
|
||||
"version": "1.25.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.25.0.tgz",
|
||||
"integrity": "sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ=="
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "2.1.0",
|
||||
|
@ -8953,22 +8929,22 @@
|
|||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"mocha": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz",
|
||||
"integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==",
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz",
|
||||
"integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==",
|
||||
"requires": {
|
||||
"ansi-colors": "4.1.1",
|
||||
"browser-stdout": "1.3.1",
|
||||
"chokidar": "3.3.1",
|
||||
"debug": "3.2.6",
|
||||
"chokidar": "3.4.2",
|
||||
"debug": "4.1.1",
|
||||
"diff": "4.0.2",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"find-up": "4.1.0",
|
||||
"escape-string-regexp": "4.0.0",
|
||||
"find-up": "5.0.0",
|
||||
"glob": "7.1.6",
|
||||
"growl": "1.10.5",
|
||||
"he": "1.2.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"log-symbols": "3.0.0",
|
||||
"js-yaml": "3.14.0",
|
||||
"log-symbols": "4.0.0",
|
||||
"minimatch": "3.0.4",
|
||||
"ms": "2.1.2",
|
||||
"object.assign": "4.1.0",
|
||||
|
@ -9011,30 +8987,10 @@
|
|||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
|
||||
"integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
|
||||
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
|
||||
"requires": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
|
@ -9043,17 +8999,22 @@
|
|||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.3.0"
|
||||
"readdirp": "~3.4.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
|
@ -9063,11 +9024,11 @@
|
|||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"locate-path": "^6.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
|
@ -9085,6 +9046,11 @@
|
|||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
|
@ -9098,29 +9064,20 @@
|
|||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
"p-locate": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
|
||||
"integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
|
||||
"integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2"
|
||||
"chalk": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
|
@ -9128,12 +9085,20 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"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==",
|
||||
"p-limit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
|
||||
"integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
|
||||
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
|
||||
"requires": {
|
||||
"p-limit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
|
@ -9142,19 +9107,11 @@
|
|||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
|
||||
"integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
|
||||
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
|
||||
"requires": {
|
||||
"picomatch": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"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"
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
|
@ -9168,13 +9125,6 @@
|
|||
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
|
@ -13629,9 +13579,9 @@
|
|||
}
|
||||
},
|
||||
"vue": {
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
|
||||
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||
},
|
||||
"vue-fullscreen": {
|
||||
"version": "2.1.6",
|
||||
|
@ -13783,9 +13733,9 @@
|
|||
}
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.6.11",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
|
||||
"integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz",
|
||||
"integrity": "sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==",
|
||||
"requires": {
|
||||
"de-indent": "^1.0.2",
|
||||
"he": "^1.1.0"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.10.5",
|
||||
"@babel/core": "^7.11.1",
|
||||
"@babel/core": "^7.11.4",
|
||||
"@babel/plugin-transform-runtime": "^7.11.0",
|
||||
"@babel/polyfill": "^7.10.4",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
|
@ -62,7 +62,7 @@
|
|||
"eventsource-polyfill": "^0.9.6",
|
||||
"file-loader": "^3.0.1",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"hls.js": "^0.14.9",
|
||||
"hls.js": "^0.14.10",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"http-proxy-middleware": "^1.0.5",
|
||||
"i18n-iso-countries": "^6.0.0",
|
||||
|
@ -74,13 +74,13 @@
|
|||
"karma-mocha": "^2.0.1",
|
||||
"karma-verbose-reporter": "^0.0.6",
|
||||
"karma-webpack": "^4.0.2",
|
||||
"luxon": "^1.24.1",
|
||||
"luxon": "^1.25.0",
|
||||
"mapbox-gl": "^1.12.0",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"mediaelement": "^4.2.16",
|
||||
"mini-css-extract-plugin": "^0.7.0",
|
||||
"minimist": "^1.2.5",
|
||||
"mocha": "^8.1.1",
|
||||
"mocha": "^8.1.2",
|
||||
"moment-timezone": "^0.5.31",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"ora": "^4.1.1",
|
||||
|
@ -107,7 +107,7 @@
|
|||
"tar": "^6.0.5",
|
||||
"truncate": "^2.1.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue": "^2.6.11",
|
||||
"vue": "^2.6.12",
|
||||
"vue-fullscreen": "^2.1.6",
|
||||
"vue-gettext": "^2.1.10",
|
||||
"vue-infinite-scroll": "^2.0.2",
|
||||
|
@ -116,7 +116,7 @@
|
|||
"vue-luxon": "^0.7.3",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vue2-filters": "^0.11.0",
|
||||
"vuelidate": "^0.7.5",
|
||||
"vuetify": "^1.5.24",
|
||||
|
|
|
@ -29,8 +29,12 @@ https://docs.photoprism.org/developer-guide/
|
|||
*/
|
||||
|
||||
import RestModel from "model/rest";
|
||||
import Notify from "common/notify";
|
||||
import {$gettext} from "./vm";
|
||||
|
||||
class Clipboard {
|
||||
export const MaxItems = 500;
|
||||
|
||||
export default class Clipboard {
|
||||
/**
|
||||
* @param {Storage} storage
|
||||
* @param {string} key
|
||||
|
@ -42,6 +46,7 @@ class Clipboard {
|
|||
this.selectionMap = {};
|
||||
this.selection = [];
|
||||
this.lastId = "";
|
||||
this.maxItems = MaxItems;
|
||||
|
||||
this.loadFromStorage();
|
||||
}
|
||||
|
@ -59,7 +64,7 @@ class Clipboard {
|
|||
}
|
||||
|
||||
toggle(model) {
|
||||
if(!model || !(model instanceof RestModel)) {
|
||||
if (!model || !(model instanceof RestModel)) {
|
||||
console.log("Clipboard::toggle() - not a model:", model);
|
||||
return;
|
||||
}
|
||||
|
@ -72,6 +77,11 @@ class Clipboard {
|
|||
const index = this.selection.indexOf(id);
|
||||
|
||||
if (index === -1) {
|
||||
if (this.selection.length >= this.maxItems) {
|
||||
Notify.warn($gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(id);
|
||||
this.selectionMap["id:" + id] = true;
|
||||
this.lastId = id;
|
||||
|
@ -85,7 +95,7 @@ class Clipboard {
|
|||
}
|
||||
|
||||
add(model) {
|
||||
if(!model || !(model instanceof RestModel)) {
|
||||
if (!model || !(model instanceof RestModel)) {
|
||||
console.log("Clipboard::add() - not a model:", model);
|
||||
return;
|
||||
}
|
||||
|
@ -96,7 +106,14 @@ class Clipboard {
|
|||
}
|
||||
|
||||
addId(id) {
|
||||
if (this.hasId(id)) return;
|
||||
if (this.hasId(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selection.length >= this.maxItems) {
|
||||
Notify.warn($gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(id);
|
||||
this.selectionMap["id:" + id] = true;
|
||||
|
@ -106,14 +123,14 @@ class Clipboard {
|
|||
}
|
||||
|
||||
addRange(rangeEnd, models) {
|
||||
if(!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("Clipboard::addRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((photo) => photo.UID === this.lastId);
|
||||
|
||||
if(rangeStart === -1) {
|
||||
if (rangeStart === -1) {
|
||||
this.toggle(models[rangeEnd]);
|
||||
return 1;
|
||||
}
|
||||
|
@ -132,7 +149,7 @@ class Clipboard {
|
|||
}
|
||||
|
||||
has(model) {
|
||||
if(!model || !(model instanceof RestModel)) {
|
||||
if (!model || !(model instanceof RestModel)) {
|
||||
console.log("Clipboard::has() - not a model:", model);
|
||||
return;
|
||||
}
|
||||
|
@ -145,7 +162,7 @@ class Clipboard {
|
|||
}
|
||||
|
||||
remove(model) {
|
||||
if(!model || !(model instanceof RestModel)) {
|
||||
if (!model || !(model instanceof RestModel)) {
|
||||
console.log("Clipboard::remove() - not a model:", model);
|
||||
return;
|
||||
}
|
||||
|
@ -188,5 +205,3 @@ class Clipboard {
|
|||
this.storage.removeItem(this.storageKey);
|
||||
}
|
||||
}
|
||||
|
||||
export default Clipboard;
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
#photoprism main .p-inline-edit a,
|
||||
#photoprism main .p-inline-edit a span {
|
||||
cursor: text;
|
||||
color: inherit;
|
||||
}
|
||||
|
|
|
@ -78,11 +78,11 @@
|
|||
</v-card>
|
||||
<v-layout row wrap class="p-album-results">
|
||||
<v-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:data-uid="album.UID"
|
||||
class="p-album"
|
||||
xs6 sm4 lg3 xl2 d-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:data-uid="album.UID"
|
||||
class="p-album"
|
||||
xs6 sm4 lg3 xl2 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card tile class="accent lighten-3"
|
||||
|
@ -93,18 +93,18 @@
|
|||
:to="{name: view, params: {uid: album.UID, slug: album.Slug, year: album.Year, month: album.Month}}"
|
||||
>
|
||||
<v-img
|
||||
:src="album.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
:src="album.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
|
@ -154,7 +154,8 @@
|
|||
</v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
<v-card-text primary-title class="pb-2 pt-0 p-photo-desc" style="user-select: none;" @click.stop.prevent="">
|
||||
<v-card-text primary-title class="pb-2 pt-0 p-photo-desc" style="user-select: none;"
|
||||
@click.stop.prevent="">
|
||||
<div class="caption mb-2" v-if="album.Description">
|
||||
<button @click.exact="edit(album)">
|
||||
{{ album.Description | truncate(100) }}
|
||||
|
@ -195,426 +196,438 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Album from "model/album";
|
||||
import {DateTime} from "luxon";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import Album from "model/album";
|
||||
import {DateTime} from "luxon";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import {MaxItems} from "../common/clipboard";
|
||||
import Notify from "../common/notify";
|
||||
|
||||
export default {
|
||||
name: 'p-page-albums',
|
||||
props: {
|
||||
staticFilter: Object,
|
||||
view: String,
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
export default {
|
||||
name: 'p-page-albums',
|
||||
props: {
|
||||
staticFilter: Object,
|
||||
view: String,
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
|
||||
this.filter.q = query["q"] ? query["q"] : "";
|
||||
this.filter.category = query["category"] ? query["category"] : "";
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query["q"] ? query["q"] : "";
|
||||
const category = query["category"] ? query["category"] : "";
|
||||
const filter = {q, category};
|
||||
const settings = {};
|
||||
this.filter.q = query["q"] ? query["q"] : "";
|
||||
this.filter.category = query["category"] ? query["category"] : "";
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query["q"] ? query["q"] : "";
|
||||
const category = query["category"] ? query["category"] : "";
|
||||
const filter = {q, category};
|
||||
const settings = {};
|
||||
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
|
||||
if (this.$config.albumCategories().length > 0) {
|
||||
categories = categories.concat(this.$config.albumCategories().map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
if (this.$config.albumCategories().length > 0) {
|
||||
categories = categories.concat(this.$config.albumCategories().map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
featureShare: this.$config.feature('share'),
|
||||
categories: categories,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
scrollDisabled: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Title too long"),
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
title: this.$gettext("Album Name"),
|
||||
category: this.$gettext("Category"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
upload: this.$gettext("Upload"),
|
||||
add: this.$gettext("Add Album"),
|
||||
},
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
dialog: {
|
||||
share: false,
|
||||
upload: false,
|
||||
edit: false,
|
||||
},
|
||||
album: new Album(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
share(album) {
|
||||
this.album = album;
|
||||
this.dialog.share = true;
|
||||
},
|
||||
edit(album) {
|
||||
this.album = album;
|
||||
this.dialog.edit = true;
|
||||
},
|
||||
webdavUpload() {
|
||||
this.dialog.share = false;
|
||||
this.dialog.upload = true;
|
||||
},
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.search();
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.filter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No albums found"));
|
||||
} else if (this.results.length === 1) {
|
||||
this.$notify.info(this.$gettext("One album found"));
|
||||
} else {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} albums found"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 albums found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
this.page = 0;
|
||||
this.dirty = true;
|
||||
this.scrollDisabled = false;
|
||||
this.loadMore();
|
||||
},
|
||||
create() {
|
||||
let title = DateTime.local().toFormat("LLLL yyyy");
|
||||
|
||||
if (this.results.findIndex(a => a.Title.startsWith(title)) !== -1) {
|
||||
const existing = this.results.filter(a => a.Title.startsWith(title));
|
||||
title = `${title} (${existing.length + 1})`
|
||||
}
|
||||
|
||||
const album = new Album({"Title": title, "Favorite": true});
|
||||
|
||||
album.save();
|
||||
},
|
||||
onSave(album) {
|
||||
album.update();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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];
|
||||
const model = this.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
|
||||
if (this.$config.albumCategories().length > 0) {
|
||||
categories = categories.concat(this.$config.albumCategories().map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
|
||||
this.categories = categories;
|
||||
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === values.UID);
|
||||
|
||||
if (index === -1 && this.staticFilter.type === values.Type) {
|
||||
this.results.unshift(new Album(values));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("albums", (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]);
|
||||
}
|
||||
},
|
||||
return {
|
||||
featureShare: this.$config.feature('share'),
|
||||
categories: categories,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
scrollDisabled: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Title too long"),
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
title: this.$gettext("Album Name"),
|
||||
category: this.$gettext("Category"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
upload: this.$gettext("Upload"),
|
||||
add: this.$gettext("Add Album"),
|
||||
},
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
dialog: {
|
||||
share: false,
|
||||
upload: false,
|
||||
edit: false,
|
||||
},
|
||||
album: new Album(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
share(album) {
|
||||
this.album = album;
|
||||
this.dialog.share = true;
|
||||
},
|
||||
edit(album) {
|
||||
this.album = album;
|
||||
this.dialog.edit = true;
|
||||
},
|
||||
webdavUpload() {
|
||||
this.dialog.share = false;
|
||||
this.dialog.upload = true;
|
||||
},
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.search();
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.filter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No albums found"));
|
||||
} else if (this.results.length === 1) {
|
||||
this.$notify.info(this.$gettext("One album found"));
|
||||
} else {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} albums found"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 albums found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
this.page = 0;
|
||||
this.dirty = true;
|
||||
this.scrollDisabled = false;
|
||||
this.loadMore();
|
||||
},
|
||||
create() {
|
||||
let title = DateTime.local().toFormat("LLLL yyyy");
|
||||
|
||||
if (this.results.findIndex(a => a.Title.startsWith(title)) !== -1) {
|
||||
const existing = this.results.filter(a => a.Title.startsWith(title));
|
||||
title = `${title} (${existing.length + 1})`
|
||||
}
|
||||
|
||||
const album = new Album({"Title": title, "Favorite": true});
|
||||
|
||||
album.save();
|
||||
},
|
||||
onSave(album) {
|
||||
album.update();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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];
|
||||
const model = this.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
|
||||
if (this.$config.albumCategories().length > 0) {
|
||||
categories = categories.concat(this.$config.albumCategories().map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
|
||||
this.categories = categories;
|
||||
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === values.UID);
|
||||
|
||||
if (index === -1 && this.staticFilter.type === values.Type) {
|
||||
this.results.unshift(new Album(values));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("albums", (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>
|
||||
|
|
|
@ -56,11 +56,11 @@
|
|||
</v-card>
|
||||
<v-layout row wrap class="p-label-results">
|
||||
<v-flex
|
||||
v-for="(label, index) in results"
|
||||
:key="index"
|
||||
class="p-label"
|
||||
:data-uid="label.UID"
|
||||
xs6 sm4 md3 lg2 d-flex
|
||||
v-for="(label, index) in results"
|
||||
:key="index"
|
||||
class="p-label"
|
||||
:data-uid="label.UID"
|
||||
xs6 sm4 md3 lg2 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card tile class="accent lighten-3"
|
||||
|
@ -70,18 +70,18 @@
|
|||
:class="selection.includes(label.UID) ? 'elevation-10 ma-0 accent darken-1 white--text' : 'elevation-0 ma-1 accent lighten-3'"
|
||||
:to="{name: 'browse', query: {q: 'label:' + (label.CustomSlug ? label.CustomSlug : label.Slug)}}">
|
||||
<v-img
|
||||
:src="label.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
:src="label.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
|
@ -109,10 +109,10 @@
|
|||
|
||||
<v-card-actions @click.stop.prevent="">
|
||||
<v-edit-dialog
|
||||
:return-value.sync="label.Name"
|
||||
lazy
|
||||
@save="onSave(label)"
|
||||
class="p-inline-edit"
|
||||
:return-value.sync="label.Name"
|
||||
lazy
|
||||
@save="onSave(label)"
|
||||
class="p-inline-edit"
|
||||
>
|
||||
<span v-if="label.Name" class="body-2 ma-0">
|
||||
{{ label.Name | capitalize }}
|
||||
|
@ -122,12 +122,12 @@
|
|||
</span>
|
||||
<template v-slot:input>
|
||||
<v-text-field
|
||||
v-model="label.Name"
|
||||
:rules="[titleRule]"
|
||||
:label="labels.name"
|
||||
color="secondary-dark"
|
||||
single-line
|
||||
autofocus
|
||||
v-model="label.Name"
|
||||
:rules="[titleRule]"
|
||||
:label="labels.name"
|
||||
color="secondary-dark"
|
||||
single-line
|
||||
autofocus
|
||||
></v-text-field>
|
||||
</template>
|
||||
</v-edit-dialog>
|
||||
|
@ -148,360 +148,372 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Label from "model/label";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import Label from "model/label";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import {MaxItems} from "../common/clipboard";
|
||||
import Notify from "../common/notify";
|
||||
|
||||
export default {
|
||||
name: 'p-page-labels',
|
||||
props: {
|
||||
staticFilter: Object
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
export default {
|
||||
name: 'p-page-labels',
|
||||
props: {
|
||||
staticFilter: Object
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
|
||||
this.filter.q = query['q'] ? query['q'] : '';
|
||||
this.filter.all = query['all'] ? query['all'] : '';
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query['q'] ? query['q'] : '';
|
||||
const all = query['all'] ? query['all'] : '';
|
||||
const filter = {q: q, all: all};
|
||||
const settings = {};
|
||||
this.filter.q = query['q'] ? query['q'] : '';
|
||||
this.filter.all = query['all'] ? query['all'] : '';
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query['q'] ? query['q'] : '';
|
||||
const all = query['all'] ? query['all'] : '';
|
||||
const filter = {q: q, all: all};
|
||||
const settings = {};
|
||||
|
||||
return {
|
||||
config: this.$config.values,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
scrollDisabled: true,
|
||||
loading: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
name: this.$gettext("Label Name"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
more: this.$gettext("Show more"),
|
||||
less: this.$gettext("Show less"),
|
||||
},
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if(this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSave(label) {
|
||||
label.update();
|
||||
},
|
||||
showAll() {
|
||||
this.filter.all = "true";
|
||||
this.updateQuery();
|
||||
},
|
||||
showImportant() {
|
||||
this.filter.all = "";
|
||||
this.updateQuery();
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.updateQuery();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Label.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} labels loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
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.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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Label.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} labels found"), {n: this.results.length}));
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 labels found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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];
|
||||
const model = this.results.find((m) => m.UID === values.UID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("labels", (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]);
|
||||
}
|
||||
},
|
||||
return {
|
||||
config: this.$config.values,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
scrollDisabled: true,
|
||||
loading: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
name: this.$gettext("Label Name"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
more: this.$gettext("Show more"),
|
||||
less: this.$gettext("Show less"),
|
||||
},
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSave(label) {
|
||||
label.update();
|
||||
},
|
||||
showAll() {
|
||||
this.filter.all = "true";
|
||||
this.updateQuery();
|
||||
},
|
||||
showImportant() {
|
||||
this.filter.all = "";
|
||||
this.updateQuery();
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.updateQuery();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Label.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} labels loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
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.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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Label.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} labels found"), {n: this.results.length}));
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 labels found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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];
|
||||
const model = this.results.find((m) => m.UID === values.UID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("labels", (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>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<router-link v-for="(item, index) in breadcrumbs" :key="index" :to="item.path">
|
||||
<v-icon>navigate_next</v-icon>
|
||||
{{item.name}}
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
</v-toolbar-title>
|
||||
|
||||
|
@ -46,11 +46,11 @@
|
|||
</v-card>
|
||||
<v-layout row wrap class="p-files-results">
|
||||
<v-flex
|
||||
v-for="(model, index) in results"
|
||||
:key="index"
|
||||
:data-uid="model.UID"
|
||||
class="p-file"
|
||||
xs6 sm4 md3 lg2 d-flex
|
||||
v-for="(model, index) in results"
|
||||
:key="index"
|
||||
:data-uid="model.UID"
|
||||
class="p-file"
|
||||
xs6 sm4 md3 lg2 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card tile class="accent lighten-3 clickable"
|
||||
|
@ -59,18 +59,18 @@
|
|||
:dark="selection.includes(model.UID)"
|
||||
:class="selection.includes(model.UID) ? 'elevation-10 ma-0 darken-1 white--text' : 'elevation-0 ma-1 lighten-3'">
|
||||
<v-img
|
||||
:src="model.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
:src="model.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
|
@ -121,362 +121,372 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import Thumb from "model/thumb";
|
||||
import {Folder} from "model/folder";
|
||||
import {Photo, TypeJpeg} from "model/photo";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import {Folder} from "model/folder";
|
||||
import Notify from "common/notify";
|
||||
import {MaxItems} from "common/clipboard";
|
||||
|
||||
export default {
|
||||
name: 'p-page-files',
|
||||
props: {
|
||||
staticFilter: Object
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
export default {
|
||||
name: 'p-page-files',
|
||||
props: {
|
||||
staticFilter: Object
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
|
||||
this.filter.q = query['q'] ? query['q'] : '';
|
||||
this.filter.all = query['all'] ? query['all'] : '';
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.path = this.$route.params.pathMatch;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query['q'] ? query['q'] : '';
|
||||
const all = query['all'] ? query['all'] : '';
|
||||
const filter = {q: q, all: all};
|
||||
const settings = {};
|
||||
this.filter.q = query['q'] ? query['q'] : '';
|
||||
this.filter.all = query['all'] ? query['all'] : '';
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.path = this.$route.params.pathMatch;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query['q'] ? query['q'] : '';
|
||||
const all = query['all'] ? query['all'] : '';
|
||||
const filter = {q: q, all: all};
|
||||
const settings = {};
|
||||
|
||||
return {
|
||||
config: this.$config.values,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
path: "",
|
||||
page: 0,
|
||||
files: {
|
||||
limit: Folder.limit(),
|
||||
offset: 0,
|
||||
},
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
name: this.$gettext("Folder Name"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
},
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
breadcrumbs: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumbs() {
|
||||
let result = [];
|
||||
let path = "/library/files";
|
||||
|
||||
const crumbs = this.path.split("/");
|
||||
|
||||
crumbs.forEach(dir => {
|
||||
if (dir) {
|
||||
path += "/" + dir
|
||||
result.push({path: path, name: dir})
|
||||
}
|
||||
})
|
||||
|
||||
return result;
|
||||
},
|
||||
openFile(index) {
|
||||
const model = this.results[index];
|
||||
|
||||
if (model.isFile()) {
|
||||
// Open Edit Dialog
|
||||
Event.publish("dialog.edit", {selection: [model.PhotoUID], album: null, index: 0});
|
||||
} else {
|
||||
this.$router.push({path: '/library/files/' + model.Path});
|
||||
}
|
||||
},
|
||||
downloadFile(index) {
|
||||
const model = this.results[index];
|
||||
const link = document.createElement('a')
|
||||
link.href = `/api/v1/dl/${model.Hash}?t=${this.$config.downloadToken()}`;
|
||||
link.download = model.Name;
|
||||
link.click()
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
} else {
|
||||
this.openFile(index);
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSave(model) {
|
||||
model.update();
|
||||
},
|
||||
showAll() {
|
||||
this.filter.all = "true";
|
||||
this.updateQuery();
|
||||
},
|
||||
showImportant() {
|
||||
this.filter.all = "";
|
||||
this.updateQuery();
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.updateQuery();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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 = {
|
||||
files: true,
|
||||
uncached: true,
|
||||
count: this.files.limit,
|
||||
offset: this.files.offset,
|
||||
};
|
||||
|
||||
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.search();
|
||||
},
|
||||
search() {
|
||||
// Don't query the same data more than once
|
||||
if (!this.dirty && (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter))) {
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(this.lastFilter, this.filter);
|
||||
|
||||
this.files.offset = 0;
|
||||
this.page = 0;
|
||||
this.loading = true;
|
||||
this.listen = false;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Folder.originals(this.path, params).then(response => {
|
||||
this.files.offset = this.files.limit;
|
||||
|
||||
this.results = response.models;
|
||||
this.breadcrumbs = this.getBreadcrumbs();
|
||||
|
||||
if (response.count === 0) {
|
||||
this.$notify.warn(this.$gettext('Folder is empty'));
|
||||
} else if (response.files === 1) {
|
||||
this.$notify.info(this.$gettext('One file found'));
|
||||
} else if (response.files === 0 && response.folders === 1) {
|
||||
this.$notify.info(this.$gettext('One folder found'));
|
||||
} else if (response.files === 0 && response.folders > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} folders found"), {n: response.folders}));
|
||||
} else if (response.files < this.files.limit) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("Folder contains %{n} files"), {n: response.files}));
|
||||
} else {
|
||||
this.$notify.warn(this.$gettextInterpolate(this.$gettext("Limit reached, showing first %{n} files"), {n: response.files}));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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];
|
||||
const model = this.results.find((m) => m.UID === values.UID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const ppid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === ppid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(ppid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.path = this.$route.params.pathMatch;
|
||||
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("folders", (ev, data) => this.onUpdate(ev, data)));
|
||||
|
||||
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
|
||||
},
|
||||
destroyed() {
|
||||
for (let i = 0; i < this.subscriptions.length; i++) {
|
||||
Event.unsubscribe(this.subscriptions[i]);
|
||||
}
|
||||
},
|
||||
return {
|
||||
config: this.$config.values,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
path: "",
|
||||
page: 0,
|
||||
files: {
|
||||
limit: Folder.limit(),
|
||||
offset: 0,
|
||||
},
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
name: this.$gettext("Folder Name"),
|
||||
},
|
||||
titles: {
|
||||
reload: this.$gettext("Reload"),
|
||||
},
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
breadcrumbs: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumbs() {
|
||||
let result = [];
|
||||
let path = "/library/files";
|
||||
|
||||
const crumbs = this.path.split("/");
|
||||
|
||||
crumbs.forEach(dir => {
|
||||
if (dir) {
|
||||
path += "/" + dir
|
||||
result.push({path: path, name: dir})
|
||||
}
|
||||
})
|
||||
|
||||
return result;
|
||||
},
|
||||
openFile(index) {
|
||||
const model = this.results[index];
|
||||
|
||||
if (model.isFile()) {
|
||||
// Open Edit Dialog
|
||||
Event.publish("dialog.edit", {selection: [model.PhotoUID], album: null, index: 0});
|
||||
} else {
|
||||
this.$router.push({path: '/library/files/' + model.Path});
|
||||
}
|
||||
},
|
||||
downloadFile(index) {
|
||||
const model = this.results[index];
|
||||
const link = document.createElement('a')
|
||||
link.href = `/api/v1/dl/${model.Hash}?t=${this.$config.downloadToken()}`;
|
||||
link.download = model.Name;
|
||||
link.click()
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
} else {
|
||||
this.openFile(index);
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSave(model) {
|
||||
model.update();
|
||||
},
|
||||
showAll() {
|
||||
this.filter.all = "true";
|
||||
this.updateQuery();
|
||||
},
|
||||
showImportant() {
|
||||
this.filter.all = "";
|
||||
this.updateQuery();
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.updateQuery();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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 = {
|
||||
files: true,
|
||||
uncached: true,
|
||||
count: this.files.limit,
|
||||
offset: this.files.offset,
|
||||
};
|
||||
|
||||
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.search();
|
||||
},
|
||||
search() {
|
||||
// Don't query the same data more than once
|
||||
if (!this.dirty && (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter))) {
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(this.lastFilter, this.filter);
|
||||
|
||||
this.files.offset = 0;
|
||||
this.page = 0;
|
||||
this.loading = true;
|
||||
this.listen = false;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Folder.originals(this.path, params).then(response => {
|
||||
this.files.offset = this.files.limit;
|
||||
|
||||
this.results = response.models;
|
||||
this.breadcrumbs = this.getBreadcrumbs();
|
||||
|
||||
if (response.count === 0) {
|
||||
this.$notify.warn(this.$gettext('Folder is empty'));
|
||||
} else if (response.files === 1) {
|
||||
this.$notify.info(this.$gettext('One file found'));
|
||||
} else if (response.files === 0 && response.folders === 1) {
|
||||
this.$notify.info(this.$gettext('One folder found'));
|
||||
} else if (response.files === 0 && response.folders > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} folders found"), {n: response.folders}));
|
||||
} else if (response.files < this.files.limit) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("Folder contains %{n} files"), {n: response.files}));
|
||||
} else {
|
||||
this.$notify.warn(this.$gettextInterpolate(this.$gettext("Limit reached, showing first %{n} files"), {n: response.files}));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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];
|
||||
const model = this.results.find((m) => m.UID === values.UID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const ppid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === ppid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(ppid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.path = this.$route.params.pathMatch;
|
||||
|
||||
this.search();
|
||||
|
||||
this.subscriptions.push(Event.subscribe("folders", (ev, data) => this.onUpdate(ev, data)));
|
||||
|
||||
this.subscriptions.push(Event.subscribe("touchmove.top", () => this.refresh()));
|
||||
},
|
||||
destroyed() {
|
||||
for (let i = 0; i < this.subscriptions.length; i++) {
|
||||
Event.unsubscribe(this.subscriptions[i]);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
</v-card>
|
||||
<v-layout row wrap class="p-album-results">
|
||||
<v-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:data-uid="album.UID"
|
||||
class="p-album"
|
||||
xs6 sm4 lg3 xl2 d-flex
|
||||
v-for="(album, index) in results"
|
||||
:key="index"
|
||||
:data-uid="album.UID"
|
||||
class="p-album"
|
||||
xs6 sm4 lg3 xl2 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card tile class="accent lighten-3"
|
||||
|
@ -41,18 +41,18 @@
|
|||
:to="{name: view, params: {uid: album.UID, slug: album.Slug, year: album.Year, month: album.Month}}"
|
||||
>
|
||||
<v-img
|
||||
:src="album.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
:src="album.thumbnailUrl('tile_500')"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click="onClick($event, index)"
|
||||
aspect-ratio="1"
|
||||
class="accent lighten-2"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
|
@ -94,399 +94,411 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Album from "model/album";
|
||||
import {DateTime} from "luxon";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import Album from "model/album";
|
||||
import {DateTime} from "luxon";
|
||||
import Event from "pubsub-js";
|
||||
import RestModel from "model/rest";
|
||||
import {MaxItems} from "../common/clipboard";
|
||||
import Notify from "../common/notify";
|
||||
|
||||
export default {
|
||||
name: 'p-page-albums',
|
||||
props: {
|
||||
staticFilter: Object,
|
||||
view: String,
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
export default {
|
||||
name: 'p-page-albums',
|
||||
props: {
|
||||
staticFilter: Object,
|
||||
view: String,
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
|
||||
this.filter.q = query["q"] ? query["q"] : "";
|
||||
this.filter.category = query["category"] ? query["category"] : "";
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query["q"] ? query["q"] : "";
|
||||
const category = query["category"] ? query["category"] : "";
|
||||
const filter = {q, category};
|
||||
const settings = {};
|
||||
this.filter.q = query["q"] ? query["q"] : "";
|
||||
this.filter.category = query["category"] ? query["category"] : "";
|
||||
this.lastFilter = {};
|
||||
this.routeName = this.$route.name;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const query = this.$route.query;
|
||||
const routeName = this.$route.name;
|
||||
const q = query["q"] ? query["q"] : "";
|
||||
const category = query["category"] ? query["category"] : "";
|
||||
const filter = {q, category};
|
||||
const settings = {};
|
||||
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
|
||||
|
||||
if (this.$config.values.albumCategories) {
|
||||
categories = categories.concat(this.$config.values.albumCategories.map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
if (this.$config.values.albumCategories) {
|
||||
categories = categories.concat(this.$config.values.albumCategories.map(cat => {
|
||||
return {"value": cat, "text": cat};
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
categories: categories,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
scrollDisabled: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Title too long"),
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
title: this.$gettext("Album Name"),
|
||||
category: this.$gettext("Category"),
|
||||
},
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.search();
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.filter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No albums found"));
|
||||
} else if (this.results.length === 1) {
|
||||
this.$notify.info(this.$gettext("One album found"));
|
||||
} else {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} albums found"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 albums found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
this.page = 0;
|
||||
this.dirty = true;
|
||||
this.scrollDisabled = false;
|
||||
this.loadMore();
|
||||
},
|
||||
create() {
|
||||
let title = DateTime.local().toFormat("LLLL yyyy");
|
||||
|
||||
if (this.results.findIndex(a => a.Title.startsWith(title)) !== -1) {
|
||||
const existing = this.results.filter(a => a.Title.startsWith(title));
|
||||
title = `${title} (${existing.length + 1})`
|
||||
}
|
||||
|
||||
const album = new Album({"Title": title, "Favorite": true});
|
||||
|
||||
album.save();
|
||||
},
|
||||
onSave(album) {
|
||||
album.update();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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];
|
||||
const model = this.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === values.UID);
|
||||
|
||||
if (index === -1 && this.staticFilter.type === values.Type) {
|
||||
this.results.unshift(new Album(values));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const token = this.$route.params.token;
|
||||
|
||||
if (this.$session.hasToken(token)) {
|
||||
this.search();
|
||||
} else {
|
||||
this.$session.redeemToken(token).then(() => {
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
|
||||
this.subscriptions.push(Event.subscribe("albums", (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]);
|
||||
}
|
||||
},
|
||||
return {
|
||||
categories: categories,
|
||||
subscriptions: [],
|
||||
listen: false,
|
||||
dirty: false,
|
||||
results: [],
|
||||
loading: true,
|
||||
scrollDisabled: true,
|
||||
pageSize: 24,
|
||||
offset: 0,
|
||||
page: 0,
|
||||
selection: [],
|
||||
settings: settings,
|
||||
filter: filter,
|
||||
lastFilter: {},
|
||||
routeName: routeName,
|
||||
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Title too long"),
|
||||
labels: {
|
||||
search: this.$gettext("Search"),
|
||||
title: this.$gettext("Album Name"),
|
||||
category: this.$gettext("Category"),
|
||||
},
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
timeStamp: -1,
|
||||
},
|
||||
lastId: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showUpload() {
|
||||
Event.publish("dialog.upload");
|
||||
},
|
||||
selectRange(rangeEnd, models) {
|
||||
if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) {
|
||||
console.warn("selectRange() - invalid arguments:", rangeEnd, models);
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeStart = models.findIndex((m) => m.getId() === this.lastId);
|
||||
|
||||
if (rangeStart === -1) {
|
||||
this.toggleSelection(models[rangeEnd].getId());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rangeStart > rangeEnd) {
|
||||
const newEnd = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = newEnd;
|
||||
}
|
||||
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
this.addSelection(models[i].getId());
|
||||
}
|
||||
|
||||
return (rangeEnd - rangeStart) + 1;
|
||||
},
|
||||
onSelect(ev, index) {
|
||||
if (ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
},
|
||||
onMouseDown(ev, index) {
|
||||
this.mouseDown.index = index;
|
||||
this.mouseDown.timeStamp = ev.timeStamp;
|
||||
},
|
||||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index, this.results);
|
||||
} else {
|
||||
this.toggleSelection(this.results[index].getId());
|
||||
}
|
||||
}
|
||||
},
|
||||
onContextMenu(ev, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.results[index]) {
|
||||
this.selectRange(index, this.results);
|
||||
}
|
||||
}
|
||||
},
|
||||
clearQuery() {
|
||||
this.filter.q = '';
|
||||
this.search();
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.lastFilter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.results = this.dirty ? response.models : this.results.concat(response.models);
|
||||
|
||||
this.scrollDisabled = (response.models.length < count);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
this.offset = offset;
|
||||
|
||||
if (this.results.length > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.offset = offset + count;
|
||||
this.page++;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.scrollDisabled = false;
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
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,
|
||||
};
|
||||
|
||||
Object.assign(params, this.filter);
|
||||
|
||||
if (this.staticFilter) {
|
||||
Object.assign(params, this.staticFilter);
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
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;
|
||||
|
||||
const params = this.searchParams();
|
||||
|
||||
Album.search(params).then(response => {
|
||||
this.offset = this.pageSize;
|
||||
|
||||
this.results = response.models;
|
||||
|
||||
this.scrollDisabled = (response.models.length < this.pageSize);
|
||||
|
||||
if (this.scrollDisabled) {
|
||||
if (!this.results.length) {
|
||||
this.$notify.warn(this.$gettext("No albums found"));
|
||||
} else if (this.results.length === 1) {
|
||||
this.$notify.info(this.$gettext("One album found"));
|
||||
} else {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} albums found"), {n: this.results.length}));
|
||||
}
|
||||
} else {
|
||||
this.$notify.info(this.$gettext('More than 20 albums found'));
|
||||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
this.loading = false;
|
||||
this.listen = true;
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
if (this.loading) return;
|
||||
this.loading = true;
|
||||
this.page = 0;
|
||||
this.dirty = true;
|
||||
this.scrollDisabled = false;
|
||||
this.loadMore();
|
||||
},
|
||||
create() {
|
||||
let title = DateTime.local().toFormat("LLLL yyyy");
|
||||
|
||||
if (this.results.findIndex(a => a.Title.startsWith(title)) !== -1) {
|
||||
const existing = this.results.filter(a => a.Title.startsWith(title));
|
||||
title = `${title} (${existing.length + 1})`
|
||||
}
|
||||
|
||||
const album = new Album({"Title": title, "Favorite": true});
|
||||
|
||||
album.save();
|
||||
},
|
||||
onSave(album) {
|
||||
album.update();
|
||||
},
|
||||
addSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos === -1) {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid)
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
toggleSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
} else {
|
||||
if (this.selection.length >= MaxItems) {
|
||||
Notify.warn(this.$gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.push(uid);
|
||||
this.lastId = uid;
|
||||
}
|
||||
},
|
||||
removeSelection(uid) {
|
||||
const pos = this.selection.indexOf(uid);
|
||||
|
||||
if (pos !== -1) {
|
||||
this.selection.splice(pos, 1);
|
||||
this.lastId = "";
|
||||
}
|
||||
},
|
||||
clearSelection() {
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.lastId = "";
|
||||
},
|
||||
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];
|
||||
const model = this.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'deleted':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === uid);
|
||||
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
|
||||
this.removeSelection(uid)
|
||||
}
|
||||
|
||||
break;
|
||||
case 'created':
|
||||
this.dirty = true;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.UID === values.UID);
|
||||
|
||||
if (index === -1 && this.staticFilter.type === values.Type) {
|
||||
this.results.unshift(new Album(values));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const token = this.$route.params.token;
|
||||
|
||||
if (this.$session.hasToken(token)) {
|
||||
this.search();
|
||||
} else {
|
||||
this.$session.redeemToken(token).then(() => {
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
|
||||
this.subscriptions.push(Event.subscribe("albums", (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>
|
||||
|
|
Loading…
Reference in a new issue