diff --git a/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala b/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala index 03d5cffa..531ed9bf 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala @@ -26,7 +26,8 @@ case class CookieData(auth: AuthToken) { domain = None, path = Some(path.asString), httpOnly = true, - secure = sec + secure = sec, + sameSite = Some(SameSite.Strict) ) } diff --git a/modules/restserver/src/main/scala/docspell/restserver/auth/RememberCookieData.scala b/modules/restserver/src/main/scala/docspell/restserver/auth/RememberCookieData.scala index 65b4ed9b..b8d2dee1 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/auth/RememberCookieData.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/auth/RememberCookieData.scala @@ -24,7 +24,8 @@ case class RememberCookieData(token: RememberToken) { path = Some(path.asString), httpOnly = true, secure = sec, - maxAge = Some(config.valid.seconds) + maxAge = Some(config.valid.seconds), + sameSite = Some(SameSite.Strict) ) } diff --git a/modules/restserver/src/main/scala/docspell/restserver/auth/ShareCookieData.scala b/modules/restserver/src/main/scala/docspell/restserver/auth/ShareCookieData.scala index 0c3b0bdf..beedc54e 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/auth/ShareCookieData.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/auth/ShareCookieData.scala @@ -26,7 +26,8 @@ final case class ShareCookieData(token: ShareToken) { httpOnly = true, secure = sec, maxAge = None, - expires = None + expires = None, + sameSite = Some(SameSite.Strict) ) } diff --git a/modules/webapp/package-lock.json b/modules/webapp/package-lock.json index f2b3cf0c..9d46f6f6 100644 --- a/modules/webapp/package-lock.json +++ b/modules/webapp/package-lock.json @@ -425,57 +425,57 @@ "dev": true }, "cssnano": { - "version": "5.0.16", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.16.tgz", - "integrity": "sha512-ryhRI9/B9VFCwPbb1z60LLK5/ldoExi7nwdnJzpkLZkm2/r7j2X3jfY+ZvDVJhC/0fPZlrAguYdHNFg0iglPKQ==", + "version": "5.0.17", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.17.tgz", + "integrity": "sha512-fmjLP7k8kL18xSspeXTzRhaFtRI7DL9b8IcXR80JgtnWBpvAzHT7sCR/6qdn0tnxIaINUN6OEQu83wF57Gs3Xw==", "dev": true, "requires": { - "cssnano-preset-default": "^5.1.11", + "cssnano-preset-default": "^5.1.12", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "cssnano-preset-default": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.11.tgz", - "integrity": "sha512-ETet5hqHxmzQq2ynXMOQofKuLm7VOjMiOB7E2zdtm/hSeCKlD9fabzIUV4GoPcRyJRHi+4kGf0vsfGYbQ4nmPw==", + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.12.tgz", + "integrity": "sha512-rO/JZYyjW1QNkWBxMGV28DW7d98UDLaF759frhli58QFehZ+D/LSmwQ2z/ylBAe2hUlsIWTq6NYGfQPq65EF9w==", "dev": true, "requires": { "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.0.2", "postcss-calc": "^8.2.0", - "postcss-colormin": "^5.2.4", - "postcss-convert-values": "^5.0.3", - "postcss-discard-comments": "^5.0.2", - "postcss-discard-duplicates": "^5.0.2", - "postcss-discard-empty": "^5.0.2", - "postcss-discard-overridden": "^5.0.3", - "postcss-merge-longhand": "^5.0.5", - "postcss-merge-rules": "^5.0.5", - "postcss-minify-font-values": "^5.0.3", - "postcss-minify-gradients": "^5.0.5", - "postcss-minify-params": "^5.0.4", - "postcss-minify-selectors": "^5.1.2", - "postcss-normalize-charset": "^5.0.2", - "postcss-normalize-display-values": "^5.0.2", - "postcss-normalize-positions": "^5.0.3", - "postcss-normalize-repeat-style": "^5.0.3", - "postcss-normalize-string": "^5.0.3", - "postcss-normalize-timing-functions": "^5.0.2", - "postcss-normalize-unicode": "^5.0.3", - "postcss-normalize-url": "^5.0.4", - "postcss-normalize-whitespace": "^5.0.3", - "postcss-ordered-values": "^5.0.4", - "postcss-reduce-initial": "^5.0.2", - "postcss-reduce-transforms": "^5.0.3", - "postcss-svgo": "^5.0.3", - "postcss-unique-selectors": "^5.0.3" + "postcss-colormin": "^5.2.5", + "postcss-convert-values": "^5.0.4", + "postcss-discard-comments": "^5.0.3", + "postcss-discard-duplicates": "^5.0.3", + "postcss-discard-empty": "^5.0.3", + "postcss-discard-overridden": "^5.0.4", + "postcss-merge-longhand": "^5.0.6", + "postcss-merge-rules": "^5.0.6", + "postcss-minify-font-values": "^5.0.4", + "postcss-minify-gradients": "^5.0.6", + "postcss-minify-params": "^5.0.5", + "postcss-minify-selectors": "^5.1.3", + "postcss-normalize-charset": "^5.0.3", + "postcss-normalize-display-values": "^5.0.3", + "postcss-normalize-positions": "^5.0.4", + "postcss-normalize-repeat-style": "^5.0.4", + "postcss-normalize-string": "^5.0.4", + "postcss-normalize-timing-functions": "^5.0.3", + "postcss-normalize-unicode": "^5.0.4", + "postcss-normalize-url": "^5.0.5", + "postcss-normalize-whitespace": "^5.0.4", + "postcss-ordered-values": "^5.0.5", + "postcss-reduce-initial": "^5.0.3", + "postcss-reduce-transforms": "^5.0.4", + "postcss-svgo": "^5.0.4", + "postcss-unique-selectors": "^5.0.4" } }, "cssnano-utils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.1.tgz", - "integrity": "sha512-VNCHL364lh++/ono+S3j9NlUK+d97KNkxI77NlqZU2W3xd2/qmyN61dsa47pTpb55zuU4G4lI7qFjAXZJH1OAQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.2.tgz", + "integrity": "sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ==", "dev": true }, "csso": { @@ -979,9 +979,9 @@ "dev": true }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "node-releases": { @@ -1090,24 +1090,42 @@ "dev": true }, "postcss": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", - "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.7.tgz", + "integrity": "sha512-L9Ye3r6hkkCeOETQX6iOaWZgjp3LL6Lpqm6EtgbKrgqGGteRMNb9vzBfRL96YOSu8o7x3MfIH9Mo5cPJFGrW6A==", "dev": true, "requires": { - "nanoid": "^3.2.0", + "nanoid": "^3.3.1", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "postcss-calc": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.3.tgz", - "integrity": "sha512-EGM2EBBWqP57N0E7N7WOLT116PJ39dwHVU01WO4XPPQLJfkL2xVgkMZ+TZvCfapj/uJH07UEfKHQNPHzSw/14Q==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + } } }, "postcss-cli": { @@ -1131,9 +1149,9 @@ } }, "postcss-colormin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.4.tgz", - "integrity": "sha512-rYlC5015aNqVQt/B6Cy156g7sH5tRUJGmT9xeagYthtKehetbKx7jHxhyLpulP4bs4vbp8u/B2rac0J7S7qPQg==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.5.tgz", + "integrity": "sha512-+X30aDaGYq81mFqwyPpnYInsZQnNpdxMX0ajlY7AExCexEFkPVV+KrO7kXwayqEWL2xwEbNQ4nUO0ZsRWGnevg==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -1151,9 +1169,9 @@ } }, "postcss-convert-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.3.tgz", - "integrity": "sha512-fVkjHm2T0PSMqXUCIhHNWVGjhB9mHEWX2GboVs7j3iCgr6FpIl9c/IdXy0PHWZSQ9LFTRgmj98amxJE6KOnlsA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.4.tgz", + "integrity": "sha512-bugzSAyjIexdObovsPZu/sBCTHccImJxLyFgeV0MmNBm/Lw5h5XnjfML6gzEmJ3A6nyfCW7hb1JXzcsA4Zfbdw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1168,27 +1186,27 @@ } }, "postcss-discard-comments": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.2.tgz", - "integrity": "sha512-6VQ3pYTsJHEsN2Bic88Aa7J/Brn4Bv8j/rqaFQZkH+pcVkKYwxCIvoMQkykEW7fBjmofdTnQgcivt5CCBJhtrg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.3.tgz", + "integrity": "sha512-6W5BemziRoqIdAKT+1QjM4bNcJAQ7z7zk073730NHg4cUXh3/rQHHj7pmYxUB9aGhuRhBiUf0pXvIHkRwhQP0Q==", "dev": true }, "postcss-discard-duplicates": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.2.tgz", - "integrity": "sha512-LKY81YjUjc78p6rbXIsnppsaFo8XzCoMZkXVILJU//sK0DgPkPSpuq/cZvHss3EtdKvWNYgWzQL+wiJFtEET4g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.3.tgz", + "integrity": "sha512-vPtm1Mf+kp7iAENTG7jI1MN1lk+fBqL5y+qxyi4v3H+lzsXEdfS3dwUZD45KVhgzDEgduur8ycB4hMegyMTeRw==", "dev": true }, "postcss-discard-empty": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.2.tgz", - "integrity": "sha512-SxBsbTjlsKUvZLL+dMrdWauuNZU8TBq5IOL/DHa6jBUSXFEwmDqeXRfTIK/FQpPTa8MJMxEHjSV3UbiuyLARPQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.3.tgz", + "integrity": "sha512-xGJugpaXKakwKI7sSdZjUuN4V3zSzb2Y0LOlmTajFbNinEjTfVs9PFW2lmKBaC/E64WwYppfqLD03P8l9BuueA==", "dev": true }, "postcss-discard-overridden": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.3.tgz", - "integrity": "sha512-yRTXknIZA4k8Yo4FiF1xbsLj/VBxfXEWxJNIrtIy6HC9KQ4xJxcPtoaaskh6QptCGrrcGnhKsTsENTRPZOBu4g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.4.tgz", + "integrity": "sha512-3j9QH0Qh1KkdxwiZOW82cId7zdwXVQv/gRXYDnwx5pBtR1sTkU4cXRK9lp5dSdiM0r0OICO/L8J6sV1/7m0kHg==", "dev": true }, "postcss-import": { @@ -1223,13 +1241,13 @@ } }, "postcss-merge-longhand": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.5.tgz", - "integrity": "sha512-R2BCPJJ/U2oh1uTWEYn9CcJ7MMcQ1iIbj9wfr2s/zHu5om5MP/ewKdaunpfJqR1WYzqCsgnXuRoVXPAzxdqy8g==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.6.tgz", + "integrity": "sha512-rkmoPwQO6ymJSmWsX6l2hHeEBQa7C4kJb9jyi5fZB1sE8nSCv7sqchoYPixRwX/yvLoZP2y6FA5kcjiByeJqDg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.0.2" + "stylehacks": "^5.0.3" }, "dependencies": { "postcss-value-parser": { @@ -1241,21 +1259,21 @@ } }, "postcss-merge-rules": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.5.tgz", - "integrity": "sha512-3Oa26/Pb9VOFVksJjFG45SNoe4nhGvJ2Uc6TlRimqF8uhfOCEhVCaJ3rvEat5UFOn2UZqTY5Da8dFgCh3Iq0Ug==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.6.tgz", + "integrity": "sha512-nzJWJ9yXWp8AOEpn/HFAW72WKVGD2bsLiAmgw4hDchSij27bt6TF+sIK0cJUBAYT3SGcjtGGsOR89bwkkMuMgQ==", "dev": true, "requires": { "browserslist": "^4.16.6", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.0.2", "postcss-selector-parser": "^6.0.5" } }, "postcss-minify-font-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.3.tgz", - "integrity": "sha512-bC45rVzEwsLhv/cL1eCjoo2OOjbSk9I7HKFBYnBvtyuIZlf7uMipMATXtA0Fc3jwPo3wuPIW1jRJWKzflMh1sA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.4.tgz", + "integrity": "sha512-RN6q3tyuEesvyCYYFCRGJ41J1XFvgV+dvYGHr0CeHv8F00yILlN8Slf4t8XW4IghlfZYCeyRrANO6HpJ948ieA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1270,13 +1288,13 @@ } }, "postcss-minify-gradients": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.5.tgz", - "integrity": "sha512-/YjvXs8PepsoiZAIpjstOO4IHKwFAqYNqbA1yVdqklM84tbUUneh6omJxGlRlF3mi6K5Pa067Mg6IwqEnYC8Zg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.6.tgz", + "integrity": "sha512-E/dT6oVxB9nLGUTiY/rG5dX9taugv9cbLNTFad3dKxOO+BQg25Q/xo2z2ddG+ZB1CbkZYaVwx5blY8VC7R/43A==", "dev": true, "requires": { "colord": "^2.9.1", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.0.2", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -1289,13 +1307,13 @@ } }, "postcss-minify-params": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.4.tgz", - "integrity": "sha512-Z0vjod9lRZEmEPfEmA2sCfjbfEEFKefMD3RDIQSUfXK4LpCyWkX1CniUgyNvnjJFLDPSxtgKzozhHhPHKoeGkg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.5.tgz", + "integrity": "sha512-YBNuq3Rz5LfLFNHb9wrvm6t859b8qIqfXsWeK7wROm3jSKNpO1Y5e8cOyBv6Acji15TgSrAwb3JkVNCqNyLvBg==", "dev": true, "requires": { "browserslist": "^4.16.6", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.0.2", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -1308,9 +1326,9 @@ } }, "postcss-minify-selectors": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.2.tgz", - "integrity": "sha512-gpn1nJDMCf3g32y/7kl+jsdamhiYT+/zmEt57RoT9GmzlixBNRPohI7k8UIHelLABhdLf3MSZhtM33xuH5eQOQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.3.tgz", + "integrity": "sha512-9RJfTiQEKA/kZhMaEXND893nBqmYQ8qYa/G+uPdVnXF6D/FzpfI6kwBtWEcHx5FqDbA79O9n6fQJfrIj6M8jvQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.5" @@ -1326,15 +1344,15 @@ } }, "postcss-normalize-charset": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.2.tgz", - "integrity": "sha512-fEMhYXzO8My+gC009qDc/3bgnFP8Fv1Ic8uw4ec4YTlhIOw63tGPk1YFd7fk9bZUf1DAbkhiL/QPWs9JLqdF2g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.3.tgz", + "integrity": "sha512-iKEplDBco9EfH7sx4ut7R2r/dwTnUqyfACf62Unc9UiyFuI7uUqZZtY+u+qp7g8Qszl/U28HIfcsI3pEABWFfA==", "dev": true }, "postcss-normalize-display-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz", - "integrity": "sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.3.tgz", + "integrity": "sha512-FIV5FY/qs4Ja32jiDb5mVj5iWBlS3N8tFcw2yg98+8MkRgyhtnBgSC0lxU+16AMHbjX5fbSJgw5AXLMolonuRQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1349,9 +1367,9 @@ } }, "postcss-normalize-positions": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.3.tgz", - "integrity": "sha512-U+rmhjrNBvIGYqr/1tD4wXPFFMKUbXsYXvlUCzLi0tOCUS6LoeEAnmVXXJY/MEB/1CKZZwBSs2tmzGawcygVBA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.4.tgz", + "integrity": "sha512-qynirjBX0Lc73ROomZE3lzzmXXTu48/QiEzKgMeqh28+MfuHLsuqC9po4kj84igZqqFGovz8F8hf44hA3dPYmQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1366,9 +1384,9 @@ } }, "postcss-normalize-repeat-style": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.3.tgz", - "integrity": "sha512-uk1+xYx0AMbA3nLSNhbDrqbf/rx+Iuq5tVad2VNyaxxJzx79oGieJ6D9F6AfOL2GtiIbP7vTYlpYHtG+ERFXTg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.4.tgz", + "integrity": "sha512-Innt+wctD7YpfeDR7r5Ik6krdyppyAg2HBRpX88fo5AYzC1Ut/l3xaxACG0KsbX49cO2n5EB13clPwuYVt8cMA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1383,9 +1401,9 @@ } }, "postcss-normalize-string": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.3.tgz", - "integrity": "sha512-Mf2V4JbIDboNGQhW6xW0YREDiYXoX3WrD3EjKkjvnpAJ6W4qqjLnK/c9aioyVFaWWHVdP5zVRw/9DI5S3oLDFw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.4.tgz", + "integrity": "sha512-Dfk42l0+A1CDnVpgE606ENvdmksttLynEqTQf5FL3XGQOyqxjbo25+pglCUvziicTxjtI2NLUR6KkxyUWEVubQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1400,9 +1418,9 @@ } }, "postcss-normalize-timing-functions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz", - "integrity": "sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.3.tgz", + "integrity": "sha512-QRfjvFh11moN4PYnJ7hia4uJXeFotyK3t2jjg8lM9mswleGsNw2Lm3I5wO+l4k1FzK96EFwEVn8X8Ojrp2gP4g==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1417,9 +1435,9 @@ } }, "postcss-normalize-unicode": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.3.tgz", - "integrity": "sha512-uNC7BmS/7h6to2UWa4RFH8sOTzu2O9dVWPE/F9Vm9GdhONiD/c1kNaCLbmsFHlKWcEx7alNUChQ+jH/QAlqsQw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.4.tgz", + "integrity": "sha512-W79Regn+a+eXTzB+oV/8XJ33s3pDyFTND2yDuUCo0Xa3QSy1HtNIfRVPXNubHxjhlqmMFADr3FSCHT84ITW3ig==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -1435,9 +1453,9 @@ } }, "postcss-normalize-url": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz", - "integrity": "sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.5.tgz", + "integrity": "sha512-Ws3tX+PcekYlXh+ycAt0wyzqGthkvVtZ9SZLutMVvHARxcpu4o7vvXcNoiNKyjKuWecnjS6HDI3fjBuDr5MQxQ==", "dev": true, "requires": { "normalize-url": "^6.0.1", @@ -1453,9 +1471,9 @@ } }, "postcss-normalize-whitespace": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.3.tgz", - "integrity": "sha512-333JWRnX655fSoUbufJ10HJop3c8mrpKkCCUnEmgz/Cb/QEtW+/TMZwDAUt4lnwqP6tCCk0x0b58jqvDgiQm/A==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.4.tgz", + "integrity": "sha512-wsnuHolYZjMwWZJoTC9jeI2AcjA67v4UuidDrPN9RnX8KIZfE+r2Nd6XZRwHVwUiHmRvKQtxiqo64K+h8/imaw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1470,12 +1488,12 @@ } }, "postcss-ordered-values": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.4.tgz", - "integrity": "sha512-taKtGDZtyYUMVYkg+MuJeBUiTF6cGHZmo/qcW7ibvW79UlyKuSHbo6dpCIiqI+j9oJsXWzP+ovIxoyLDOeQFdw==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.5.tgz", + "integrity": "sha512-mfY7lXpq+8bDEHfP+muqibDPhZ5eP9zgBEF9XRvoQgXcQe2Db3G1wcvjbnfjXG6wYsl+0UIjikqq4ym1V2jGMQ==", "dev": true, "requires": { - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.0.2", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -1603,9 +1621,9 @@ } }, "postcss-reduce-initial": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz", - "integrity": "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.3.tgz", + "integrity": "sha512-c88TkSnQ/Dnwgb4OZbKPOBbCaauwEjbECP5uAuFPOzQ+XdjNjRH7SG0dteXrpp1LlIFEKK76iUGgmw2V0xeieA==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -1613,9 +1631,9 @@ } }, "postcss-reduce-transforms": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.3.tgz", - "integrity": "sha512-yDnTUab5i7auHiNwdcL1f+pBnqQFf+7eC4cbC7D8Lc1FkvNZhtpkdad+9U4wDdFb84haupMf0rA/Zc5LcTe/3A==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.4.tgz", + "integrity": "sha512-VIJB9SFSaL8B/B7AXb7KHL6/GNNbbCHslgdzS9UDfBZYIA2nx8NLY7iD/BXFSO/1sRUILzBTfHCoW5inP37C5g==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -1654,19 +1672,27 @@ } }, "postcss-svgo": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", - "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.4.tgz", + "integrity": "sha512-yDKHvULbnZtIrRqhZoA+rxreWpee28JSRH/gy9727u0UCgtpv1M/9WEWY3xySlFa0zQJcqf6oCBJPR5NwkmYpg==", "dev": true, "requires": { - "postcss-value-parser": "^4.1.0", + "postcss-value-parser": "^4.2.0", "svgo": "^2.7.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + } } }, "postcss-unique-selectors": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.3.tgz", - "integrity": "sha512-V5tX2hadSSn+miVCluuK1IDGy+7jAXSOfRZ2DQ+s/4uQZb/orDYBjH0CHgFrXsRw78p4QTuEFA9kI6C956UnHQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.4.tgz", + "integrity": "sha512-5ampwoSDJCxDPoANBIlMgoBcYUHnhaiuLYJR5pj1DLnYQvMRVyFuTA5C3Bvt+aHtiqWpJkD/lXT50Vo1D0ZsAQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.5" @@ -1796,9 +1822,9 @@ } }, "stylehacks": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.2.tgz", - "integrity": "sha512-114zeJdOpTrbQYRD4OU5UWJ99LKUaqCPJTU1HQ/n3q3BwmllFN8kHENaLnOeqVq6AhXrWfxHNZTl33iJ4oy3cQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.3.tgz", + "integrity": "sha512-ENcUdpf4yO0E1rubu8rkxI+JGQk4CgjchynZ4bDBJDfqdy+uhTRSWb8/F3Jtu+Bw5MW45Po3/aQGeIyyxgQtxg==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -1836,9 +1862,9 @@ } }, "tailwindcss": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.18.tgz", - "integrity": "sha512-ihPTpEyA5ANgZbwKlgrbfnzOp9R5vDHFWmqxB1PT8NwOGCOFVVMl+Ps1cQQ369acaqqf1BEF77roCwK0lvNmTw==", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.23.tgz", + "integrity": "sha512-+OZOV9ubyQ6oI2BXEhzw4HrqvgcARY38xv3zKcjnWtMIZstEsXdI9xftd1iB7+RbOnj2HOEzkA0OyB5BaSxPQA==", "dev": true, "requires": { "arg": "^5.0.1", @@ -1854,13 +1880,14 @@ "is-glob": "^4.0.3", "normalize-path": "^3.0.0", "object-hash": "^2.2.0", + "postcss": "^8.4.6", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.0", "postcss-nested": "5.0.6", "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0", "quick-lru": "^5.1.1", - "resolve": "^1.21.0" + "resolve": "^1.22.0" }, "dependencies": { "chokidar": { diff --git a/modules/webapp/package.json b/modules/webapp/package.json index d35b280e..d3ae6a08 100644 --- a/modules/webapp/package.json +++ b/modules/webapp/package.json @@ -7,12 +7,12 @@ "@fortawesome/fontawesome-free": "^6.0.0", "@tailwindcss/forms": "^0.4.0", "autoprefixer": "^10.4.2", - "cssnano": "^5.0.16", + "cssnano": "^5.0.17", "flag-icon-css": "^3.5.0", - "postcss": "^8.4.6", + "postcss": "^8.4.7", "postcss-cli": "^9.1.0", "postcss-import": "^14.0.2", "postcss-purgecss": "^2.0.3", - "tailwindcss": "^3.0.18" + "tailwindcss": "^3.0.23" } } diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index 9489e8aa..13249814 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -1727,42 +1727,42 @@ mergeItems flags items receive = reprocessMultiple : Flags - -> Set String + -> List String -> (Result Http.Error BasicResult -> msg) -> Cmd msg reprocessMultiple flags items receive = Http2.authPost { url = flags.config.baseUrl ++ "/api/v1/sec/items/reprocess" , account = getAccount flags - , body = Http.jsonBody (Api.Model.IdList.encode (Set.toList items |> IdList)) + , body = Http.jsonBody (Api.Model.IdList.encode (IdList items)) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } confirmMultiple : Flags - -> Set String + -> List String -> (Result Http.Error BasicResult -> msg) -> Cmd msg confirmMultiple flags ids receive = Http2.authPut { url = flags.config.baseUrl ++ "/api/v1/sec/items/confirm" , account = getAccount flags - , body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids))) + , body = Http.jsonBody (Api.Model.IdList.encode (IdList ids)) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } unconfirmMultiple : Flags - -> Set String + -> List String -> (Result Http.Error BasicResult -> msg) -> Cmd msg unconfirmMultiple flags ids receive = Http2.authPut { url = flags.config.baseUrl ++ "/api/v1/sec/items/unconfirm" , account = getAccount flags - , body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids))) + , body = Http.jsonBody (Api.Model.IdList.encode (IdList ids)) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } @@ -1937,28 +1937,28 @@ setConcEquipmentMultiple flags data receive = deleteAllItems : Flags - -> Set String + -> List String -> (Result Http.Error BasicResult -> msg) -> Cmd msg deleteAllItems flags ids receive = Http2.authPost { url = flags.config.baseUrl ++ "/api/v1/sec/items/deleteAll" , account = getAccount flags - , body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids))) + , body = Http.jsonBody (Api.Model.IdList.encode (IdList ids)) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } restoreAllItems : Flags - -> Set String + -> List String -> (Result Http.Error BasicResult -> msg) -> Cmd msg restoreAllItems flags ids receive = Http2.authPost { url = flags.config.baseUrl ++ "/api/v1/sec/items/restoreAll" , account = getAccount flags - , body = Http.jsonBody (Api.Model.IdList.encode (IdList (Set.toList ids))) + , body = Http.jsonBody (Api.Model.IdList.encode (IdList ids)) , expect = Http.expectJson receive Api.Model.BasicResult.decoder } diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index ca2b362c..83cb0854 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -19,6 +19,7 @@ import Api.Model.VersionInfo exposing (VersionInfo) import Browser exposing (UrlRequest) import Browser.Navigation exposing (Key) import Data.Flags exposing (Flags) +import Data.ItemIds exposing (ItemIds) import Data.ServerEvent exposing (ServerEvent) import Data.UiSettings exposing (UiSettings) import Data.UiTheme exposing (UiTheme) @@ -69,6 +70,7 @@ type alias Model = , langMenuOpen : Bool , showNewItemsArrived : Bool , jobsWaiting : Int + , selectedItems : ItemIds } @@ -133,6 +135,7 @@ init key url flags_ settings = , langMenuOpen = False , showNewItemsArrived = False , jobsWaiting = 0 + , selectedItems = Data.ItemIds.empty } , Cmd.batch [ Cmd.map DashboardMsg dbc diff --git a/modules/webapp/src/main/elm/App/Update.elm b/modules/webapp/src/main/elm/App/Update.elm index 6da2ac12..d2cc9b5f 100644 --- a/modules/webapp/src/main/elm/App/Update.elm +++ b/modules/webapp/src/main/elm/App/Update.elm @@ -15,7 +15,9 @@ import App.Data exposing (..) import Browser exposing (UrlRequest(..)) import Browser.Navigation as Nav import Data.AppEvent exposing (AppEvent(..)) +import Data.Environment as Env import Data.Flags +import Data.ItemIds exposing (ItemIds) import Data.ServerEvent exposing (ServerEvent(..)) import Data.UiSettings exposing (UiSettings) import Data.UiTheme @@ -346,6 +348,15 @@ updateWithSub msg model = ) +modelEnv : Model -> Env.Update +modelEnv model = + { key = model.key + , selectedItems = model.selectedItems + , flags = model.flags + , settings = model.uiSettings + } + + applyClientSettings : Messages -> Model -> UiSettings -> ( Model, Cmd Msg, Sub Msg ) applyClientSettings texts model settings = let @@ -368,6 +379,18 @@ applyClientSettings texts model settings = { model | uiSettings = settings } +applySelectionChange : Messages -> Model -> ItemIds -> ( Model, Cmd Msg, Sub Msg ) +applySelectionChange texts model newSelection = + if model.selectedItems == newSelection then + ( { model | selectedItems = newSelection }, Cmd.none, Sub.none ) + + else + Util.Update.andThen2 + [ updateSearch texts Page.Search.Data.ItemSelectionChanged + ] + { model | selectedItems = newSelection } + + updateDashboard : Messages -> Page.Dashboard.Data.Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) updateDashboard texts lmsg model = let @@ -427,17 +450,16 @@ updateItemDetail texts lmsg model = result = Page.ItemDetail.Update.update - model.key - model.flags inav - model.uiSettings + (modelEnv model) lmsg model.itemDetailModel - model_ = - { model - | itemDetailModel = result.model - } + ( model_, cmd_, sub_ ) = + applySelectionChange + texts + { model | itemDetailModel = result.model } + result.selectedItems ( hm, hc, hs ) = updateSearch texts (Page.Search.Data.SetLinkTarget result.linkTarget) model_ @@ -451,8 +473,8 @@ updateItemDetail texts lmsg model = ( hm, hc, hs ) in ( hm1 - , Cmd.batch [ Cmd.map ItemDetailMsg result.cmd, hc, hc1 ] - , Sub.batch [ Sub.map ItemDetailMsg result.sub, hs, hs1 ] + , Cmd.batch [ Cmd.map ItemDetailMsg result.cmd, hc, hc1, cmd_ ] + , Sub.batch [ Sub.map ItemDetailMsg result.sub, hs, hs1, sub_ ] ) @@ -576,7 +598,7 @@ updateLogin lmsg model = updateSearch : Messages -> Page.Search.Data.Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) updateSearch texts lmsg model = let - ( mid, bmId ) = + ( lastViewItemId, bookmarkId ) = case model.page of SearchPage bId -> ( Util.Maybe.fromString model.itemDetailModel.detail.item.id, bId ) @@ -584,11 +606,11 @@ updateSearch texts lmsg model = _ -> ( Nothing, Nothing ) - result = - Page.Search.Update.update bmId mid model.key model.flags texts.search model.uiSettings lmsg model.searchModel + env = + modelEnv model - model_ = - { model | searchModel = result.model } + result = + Page.Search.Update.update texts.search bookmarkId lastViewItemId env lmsg model.searchModel lc = case result.appEvent of @@ -597,15 +619,13 @@ updateSearch texts lmsg model = AppNothing -> Cmd.none + + ( model_, cmd_, sub_ ) = + applySelectionChange texts { model | searchModel = result.model } result.selectedItems in ( model_ - , Cmd.batch - [ Cmd.map SearchMsg result.cmd - , lc - ] - , Sub.batch - [ Sub.map SearchMsg result.sub - ] + , Cmd.batch [ Cmd.map SearchMsg result.cmd, lc, cmd_ ] + , Sub.batch [ Sub.map SearchMsg result.sub, sub_ ] ) diff --git a/modules/webapp/src/main/elm/App/View2.elm b/modules/webapp/src/main/elm/App/View2.elm index 09e7697d..6913438e 100644 --- a/modules/webapp/src/main/elm/App/View2.elm +++ b/modules/webapp/src/main/elm/App/View2.elm @@ -10,6 +10,7 @@ module App.View2 exposing (view) import Api.Model.AuthResult exposing (AuthResult) import App.Data exposing (..) import Comp.Basic as B +import Data.Environment as Env import Data.Flags import Data.Icons as Icons import Data.UiSettings @@ -485,6 +486,15 @@ dropdownMenu = " absolute right-0 bg-white dark:bg-slate-800 border dark:border-slate-700 dark:text-slate-300 shadow-lg opacity-1 transition duration-200 min-w-max " +modelEnv : Model -> Env.View +modelEnv model = + { sidebarVisible = model.sidebarVisible + , flags = model.flags + , settings = model.uiSettings + , selectedItems = model.selectedItems + } + + viewDashboard : Messages -> Model -> List (Html Msg) viewDashboard texts model = [ Html.map DashboardMsg @@ -549,17 +559,18 @@ viewShareDetail texts shareId itemId model = viewSearch : Messages -> Maybe String -> Model -> List (Html Msg) viewSearch texts bmId model = + let + env = + modelEnv model + in [ Html.map SearchMsg (Search.viewSidebar texts.search - model.sidebarVisible - model.flags - model.uiSettings + env model.searchModel ) , Html.map SearchMsg (Search.viewContent texts.search - model.flags - model.uiSettings + env model.searchModel ) ] @@ -685,19 +696,19 @@ viewItemDetail texts id model = let inav = Page.Search.Data.itemNav id model.searchModel + + env = + modelEnv model in [ Html.map ItemDetailMsg (ItemDetail.viewSidebar texts.itemDetail - model.sidebarVisible - model.flags - model.uiSettings + env model.itemDetailModel ) , Html.map ItemDetailMsg (ItemDetail.viewContent texts.itemDetail inav - model.flags - model.uiSettings + env model.itemDetailModel ) ] diff --git a/modules/webapp/src/main/elm/Comp/ItemCard.elm b/modules/webapp/src/main/elm/Comp/ItemCard.elm index 0af6039e..d3369257 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCard.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCard.elm @@ -24,6 +24,7 @@ import Data.Fields import Data.Flags exposing (Flags) import Data.Icons as Icons import Data.ItemArrange exposing (ItemArrange) +import Data.ItemIds exposing (ItemIdChange, ItemIds) import Data.ItemSelection exposing (ItemSelection) import Data.ItemTemplate as IT import Data.UiSettings exposing (UiSettings) @@ -49,7 +50,7 @@ type alias Model = type Msg = CyclePreview ItemLight - | ToggleSelectItem (Set String) String + | ToggleSelectItem ItemIds String | ItemDDMsg DD.Msg | SetLinkTarget LinkTarget | ToggleRowOpen String @@ -70,7 +71,7 @@ type alias ViewConfig = type alias UpdateResult = { model : Model , dragModel : DD.Model - , selection : ItemSelection + , selection : ItemIdChange , linkTarget : LinkTarget , toggleRow : Maybe String } @@ -117,25 +118,21 @@ update : DD.Model -> Msg -> Model -> UpdateResult update ddm msg model = case msg of ToggleRowOpen id -> - UpdateResult model ddm Data.ItemSelection.Inactive LinkNone (Just id) + UpdateResult model ddm Data.ItemIds.noChange LinkNone (Just id) ItemDDMsg lm -> let ddd = DD.update lm ddm in - UpdateResult model ddd.model Data.ItemSelection.Inactive LinkNone Nothing + UpdateResult model ddd.model Data.ItemIds.noChange LinkNone Nothing ToggleSelectItem ids id -> let newSet = - if Set.member id ids then - Set.remove id ids - - else - Set.insert id ids + Data.ItemIds.toggle ids id in - UpdateResult model ddm (Data.ItemSelection.Active newSet) LinkNone Nothing + UpdateResult model ddm newSet LinkNone Nothing CyclePreview item -> let @@ -147,12 +144,12 @@ update ddm msg model = in UpdateResult { model | previewAttach = next } ddm - Data.ItemSelection.Inactive + Data.ItemIds.noChange LinkNone Nothing SetLinkTarget target -> - UpdateResult model ddm Data.ItemSelection.Inactive target Nothing + UpdateResult model ddm Data.ItemIds.noChange target Nothing @@ -532,7 +529,7 @@ viewCard texts cfg settings flags model item = mkCardAction : Texts -> ViewConfig -> UiSettings -> ItemLight -> List (Attribute Msg) -mkCardAction texts cfg settings item = +mkCardAction texts cfg _ item = case cfg.selection of Data.ItemSelection.Inactive -> case cfg.arrange of @@ -977,7 +974,7 @@ isSelected : ViewConfig -> String -> Bool isSelected cfg id = case cfg.selection of Data.ItemSelection.Active ids -> - Set.member id ids + Data.ItemIds.isMember ids id Data.ItemSelection.Inactive -> False diff --git a/modules/webapp/src/main/elm/Comp/ItemCardList.elm b/modules/webapp/src/main/elm/Comp/ItemCardList.elm index 7d37664a..a5577054 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCardList.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCardList.elm @@ -25,6 +25,7 @@ import Comp.ItemCard import Comp.LinkTarget exposing (LinkTarget) import Data.Flags exposing (Flags) import Data.ItemArrange exposing (ItemArrange) +import Data.ItemIds exposing (ItemIdChange) import Data.ItemSelection exposing (ItemSelection) import Data.Items import Data.UiSettings exposing (UiSettings) @@ -83,7 +84,7 @@ type alias UpdateResult = { model : Model , cmd : Cmd Msg , dragModel : DD.Model - , selection : ItemSelection + , selection : ItemIdChange , linkTarget : LinkTarget , toggleOpenRow : Maybe String } @@ -105,7 +106,7 @@ updateDrag dm _ msg model = UpdateResult newModel Cmd.none dm - Data.ItemSelection.Inactive + Data.ItemIds.noChange Comp.LinkTarget.LinkNone Nothing @@ -114,7 +115,7 @@ updateDrag dm _ msg model = UpdateResult model Cmd.none dm - Data.ItemSelection.Inactive + Data.ItemIds.noChange Comp.LinkTarget.LinkNone Nothing @@ -126,7 +127,7 @@ updateDrag dm _ msg model = UpdateResult newModel Cmd.none dm - Data.ItemSelection.Inactive + Data.ItemIds.noChange Comp.LinkTarget.LinkNone Nothing @@ -153,7 +154,7 @@ updateDrag dm _ msg model = UpdateResult { model | results = removeItemById id model.results } Cmd.none dm - Data.ItemSelection.Inactive + Data.ItemIds.noChange Comp.LinkTarget.LinkNone Nothing diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail.elm b/modules/webapp/src/main/elm/Comp/ItemDetail.elm index 9be2dce5..7096d7f4 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail.elm @@ -12,10 +12,10 @@ module Comp.ItemDetail exposing , view2 ) -import Browser.Navigation as Nav import Comp.ItemDetail.Model exposing (Msg(..), UpdateResult) import Comp.ItemDetail.Update import Comp.ItemDetail.View2 +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.ItemNav exposing (ItemNav) import Data.UiSettings exposing (UiSettings) @@ -33,11 +33,11 @@ emptyModel = Comp.ItemDetail.Model.emptyModel -update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> UpdateResult +update : ItemNav -> Env.Update -> Msg -> Model -> UpdateResult update = Comp.ItemDetail.Update.update -view2 : Texts -> Flags -> ItemNav -> UiSettings -> Model -> Html Msg +view2 : Texts -> ItemNav -> Env.View -> Model -> Html Msg view2 = Comp.ItemDetail.View2.view diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm index 655940be..4a32ae05 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/FormChange.elm @@ -24,8 +24,8 @@ import Api.Model.ItemsAndRefs exposing (ItemsAndRefs) import Api.Model.ReferenceList exposing (ReferenceList) import Data.Direction exposing (Direction) import Data.Flags exposing (Flags) +import Data.ItemIds exposing (ItemIds) import Http -import Set exposing (Set) type FormChange @@ -49,14 +49,14 @@ type FormChange multiUpdate : Flags - -> Set String + -> ItemIds -> FormChange -> (Result Http.Error BasicResult -> msg) -> Cmd msg multiUpdate flags ids change receive = let items = - Set.toList ids + Data.ItemIds.toList ids in case change of CustomValueChange field value -> @@ -159,10 +159,10 @@ multiUpdate flags ids change receive = ConfirmChange flag -> if flag then - Api.confirmMultiple flags ids receive + Api.confirmMultiple flags items receive else - Api.unconfirmMultiple flags ids receive + Api.unconfirmMultiple flags items receive NoFormChange -> Cmd.none diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm index b3a58c4a..b0f577be 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm @@ -57,6 +57,7 @@ import Comp.SimpleTextInput import Comp.TagDropdown import Data.Direction exposing (Direction) import Data.Fields exposing (Field) +import Data.ItemIds exposing (ItemIdChange) import DatePicker exposing (DatePicker) import Dict exposing (Dict) import File exposing (File) @@ -367,6 +368,7 @@ type Msg | ToggleShowQrAttach String | PrintElement String | SetNameMsg Comp.SimpleTextInput.Msg + | ToggleSelectItem type SaveNameState @@ -381,22 +383,23 @@ type alias UpdateResult = , sub : Sub Msg , linkTarget : LinkTarget , removedItem : Maybe String + , selectionChange : ItemIdChange } resultModel : Model -> UpdateResult resultModel model = - UpdateResult model Cmd.none Sub.none Comp.LinkTarget.LinkNone Nothing + UpdateResult model Cmd.none Sub.none Comp.LinkTarget.LinkNone Nothing Data.ItemIds.noChange resultModelCmd : ( Model, Cmd Msg ) -> UpdateResult resultModelCmd ( model, cmd ) = - UpdateResult model cmd Sub.none Comp.LinkTarget.LinkNone Nothing + UpdateResult model cmd Sub.none Comp.LinkTarget.LinkNone Nothing Data.ItemIds.noChange resultModelCmdSub : ( Model, Cmd Msg, Sub Msg ) -> UpdateResult resultModelCmdSub ( model, cmd, sub ) = - UpdateResult model cmd sub Comp.LinkTarget.LinkNone Nothing + UpdateResult model cmd sub Comp.LinkTarget.LinkNone Nothing Data.ItemIds.noChange personMatchesOrg : Model -> Bool diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index 39bbee54..fa5ac06a 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -20,7 +20,6 @@ import Api.Model.OptionalDate exposing (OptionalDate) import Api.Model.OptionalId exposing (OptionalId) import Api.Model.OptionalText exposing (OptionalText) import Api.Model.StringList exposing (StringList) -import Browser.Navigation as Nav import Comp.AttachmentMeta import Comp.CustomFieldMultiInput import Comp.DatePicker @@ -58,10 +57,12 @@ import Comp.SimpleTextInput import Comp.TagDropdown import Data.CustomFieldChange exposing (CustomFieldChange(..)) import Data.Direction +import Data.Environment as Env import Data.EquipmentOrder import Data.Fields exposing (Field) import Data.Flags exposing (Flags) import Data.FolderOrder +import Data.ItemIds import Data.ItemNav exposing (ItemNav) import Data.PersonOrder import Data.PersonUse @@ -74,7 +75,6 @@ import Http import Page exposing (Page(..)) import Ports import Set exposing (Set) -import Time import Util.File exposing (makeFileId) import Util.List import Util.Maybe @@ -82,8 +82,8 @@ import Util.String import Util.Tag -update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> UpdateResult -update key flags inav settings msg model = +update : ItemNav -> Env.Update -> Msg -> Model -> UpdateResult +update inav env msg model = case msg of Init -> let @@ -91,10 +91,10 @@ update key flags inav settings msg model = Comp.DatePicker.init ( im, ic ) = - Comp.ItemMail.init flags + Comp.ItemMail.init env.flags ( cm, cc ) = - Comp.CustomFieldMultiInput.init flags + Comp.CustomFieldMultiInput.init env.flags in resultModelCmd ( { model @@ -106,30 +106,26 @@ update key flags inav settings msg model = , customFieldsModel = cm } , Cmd.batch - [ getOptions flags + [ getOptions env.flags , Cmd.map ItemDatePickerMsg dpc , Cmd.map DueDatePickerMsg dpc , Cmd.map ItemMailMsg ic , Cmd.map CustomFieldMsg cc - , Api.getSentMails flags model.item.id SentMailsResp + , Api.getSentMails env.flags model.item.id SentMailsResp ] ) SetItem item -> let res1 = - update key - flags - inav - settings + update inav + env (TagDropdownMsg (Comp.TagDropdown.setSelected item.tags)) model res2 = - update key - flags - inav - settings + update inav + env (DirDropdownMsg (Comp.Dropdown.SetSelection (Data.Direction.fromString item.direction @@ -141,10 +137,8 @@ update key flags inav settings msg model = res1.model res3 = - update key - flags - inav - settings + update inav + env (OrgDropdownMsg (Comp.Dropdown.SetSelection (item.corrOrg @@ -156,10 +150,8 @@ update key flags inav settings msg model = res2.model res4 = - update key - flags - inav - settings + update inav + env (CorrPersonMsg (Comp.Dropdown.SetSelection (item.corrPerson @@ -171,10 +163,8 @@ update key flags inav settings msg model = res3.model res5 = - update key - flags - inav - settings + update inav + env (ConcPersonMsg (Comp.Dropdown.SetSelection (item.concPerson @@ -186,10 +176,8 @@ update key flags inav settings msg model = res4.model res6 = - update key - flags - inav - settings + update inav + env (ConcEquipMsg (Comp.Dropdown.SetSelection (item.concEquipment @@ -201,13 +189,11 @@ update key flags inav settings msg model = res5.model res7 = - update key flags inav settings AddFilesReset res6.model + update inav env AddFilesReset res6.model res8 = - update key - flags - inav - settings + update inav + env (FolderDropdownMsg (Comp.Dropdown.SetSelection (item.folder @@ -219,16 +205,14 @@ update key flags inav settings msg model = res7.model res9 = - update key - flags - inav - settings + update inav + env (CustomFieldMsg (Comp.CustomFieldMultiInput.setValues item.customfields)) res8.model proposalCmd = if item.state == "created" then - Api.getItemProposals flags item.id GetProposalResp + Api.getItemProposals env.flags item.id GetProposalResp else Cmd.none @@ -265,11 +249,11 @@ update key flags inav settings msg model = , res7.cmd , res8.cmd , res9.cmd - , getOptions flags + , getOptions env.flags , proposalCmd - , Api.getSentMails flags item.id SentMailsResp - , Api.getPersons flags "" Data.PersonOrder.NameAsc GetPersonResp - , Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd flags) + , Api.getSentMails env.flags item.id SentMailsResp + , Api.getPersons env.flags "" Data.PersonOrder.NameAsc GetPersonResp + , Cmd.map CustomFieldMsg (Comp.CustomFieldMultiInput.initCmd env.flags) ] , sub = Sub.batch @@ -285,6 +269,7 @@ update key flags inav settings msg model = ] , linkTarget = Comp.LinkTarget.LinkNone , removedItem = Nothing + , selectionChange = Data.ItemIds.noChange } SetActiveAttachment pos -> @@ -322,7 +307,7 @@ update key flags inav settings msg model = resultModel model else - resultModelCmd ( model, Api.itemDetail flags model.item.id GetItemResp ) + resultModelCmd ( model, Api.itemDetail env.flags model.item.id GetItemResp ) FolderDropdownMsg m -> let @@ -337,7 +322,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setFolder flags newModel idref + setFolder env.flags newModel idref else Cmd.none @@ -354,7 +339,7 @@ update key flags inav settings msg model = save = if Comp.TagDropdown.isChangeMsg m then - saveTags flags newModel + saveTags env.flags newModel else Cmd.none @@ -371,7 +356,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setDirection flags newModel + setDirection env.flags newModel else Cmd.none @@ -393,7 +378,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setCorrOrg flags newModel idref + setCorrOrg env.flags newModel idref else Cmd.none @@ -413,7 +398,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setCorrPerson flags newModel idref + setCorrPerson env.flags newModel idref else Cmd.none @@ -433,7 +418,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setConcPerson flags newModel idref + setConcPerson env.flags newModel idref else Cmd.none @@ -453,7 +438,7 @@ update key flags inav settings msg model = save = if isDropdownChangeMsg m then - setConcEquip flags newModel idref + setConcEquip env.flags newModel idref else Cmd.none @@ -468,7 +453,7 @@ update key flags inav settings msg model = ( setter, value, save ) = case result.change of Comp.SimpleTextInput.ValueUpdated v -> - ( setName flags { model | nameModel = Maybe.withDefault "" v }, v, Saving ) + ( setName env.flags { model | nameModel = Maybe.withDefault "" v }, v, Saving ) Comp.SimpleTextInput.ValueUnchanged -> ( Cmd.none, Nothing, model.nameState ) @@ -478,6 +463,7 @@ update key flags inav settings msg model = , sub = Sub.map SetNameMsg result.sub , linkTarget = Comp.LinkTarget.LinkNone , removedItem = Nothing + , selectionChange = Data.ItemIds.noChange } SetNotes str -> @@ -509,22 +495,22 @@ update key flags inav settings msg model = resultModel model SaveNotes -> - resultModelCmd ( model, setNotes flags model ) + resultModelCmd ( model, setNotes env.flags model ) ConfirmItem -> let resetCmds = - resetHiddenFields settings flags model.item.id ResetHiddenMsg + resetHiddenFields env.settings env.flags model.item.id ResetHiddenMsg in resultModelCmd ( { model | mobileItemMenuOpen = False } - , Cmd.batch (Api.setConfirmed flags model.item.id SaveResp :: resetCmds) + , Cmd.batch (Api.setConfirmed env.flags model.item.id SaveResp :: resetCmds) ) UnconfirmItem -> resultModelCmd ( { model | mobileItemMenuOpen = False } - , Api.setUnconfirmed flags model.item.id SaveResp + , Api.setUnconfirmed env.flags model.item.id SaveResp ) ItemDatePickerMsg m -> @@ -538,13 +524,13 @@ update key flags inav settings msg model = newModel = { model | itemDatePicker = dp, itemDate = Just (Comp.DatePicker.midOfDay date) } in - resultModelCmd ( newModel, setDate flags newModel newModel.itemDate ) + resultModelCmd ( newModel, setDate env.flags newModel newModel.itemDate ) _ -> resultModel { model | itemDatePicker = dp } RemoveDate -> - resultModelCmd ( { model | itemDate = Nothing }, setDate flags model Nothing ) + resultModelCmd ( { model | itemDate = Nothing }, setDate env.flags model Nothing ) DueDatePickerMsg m -> let @@ -557,18 +543,18 @@ update key flags inav settings msg model = newModel = { model | dueDatePicker = dp, dueDate = Just (Comp.DatePicker.midOfDay date) } in - resultModelCmd ( newModel, setDueDate flags newModel newModel.dueDate ) + resultModelCmd ( newModel, setDueDate env.flags newModel newModel.dueDate ) _ -> resultModel { model | dueDatePicker = dp } RemoveDueDate -> - resultModelCmd ( { model | dueDate = Nothing }, setDueDate flags model Nothing ) + resultModelCmd ( { model | dueDate = Nothing }, setDueDate env.flags model Nothing ) DeleteItemConfirmed -> let cmd = - Api.deleteItem flags model.item.id (DeleteResp model.item.id) + Api.deleteItem env.flags model.item.id (DeleteResp model.item.id) in resultModelCmd ( { model | itemModal = Nothing }, cmd ) @@ -583,22 +569,22 @@ update key flags inav settings msg model = } SetCorrOrgSuggestion idname -> - resultModelCmd ( model, setCorrOrg flags model (Just idname) ) + resultModelCmd ( model, setCorrOrg env.flags model (Just idname) ) SetCorrPersonSuggestion idname -> - resultModelCmd ( model, setCorrPerson flags model (Just idname) ) + resultModelCmd ( model, setCorrPerson env.flags model (Just idname) ) SetConcPersonSuggestion idname -> - resultModelCmd ( model, setConcPerson flags model (Just idname) ) + resultModelCmd ( model, setConcPerson env.flags model (Just idname) ) SetConcEquipSuggestion idname -> - resultModelCmd ( model, setConcEquip flags model (Just idname) ) + resultModelCmd ( model, setConcEquip env.flags model (Just idname) ) SetItemDateSuggestion date -> - resultModelCmd ( model, setDate flags model (Just date) ) + resultModelCmd ( model, setDate env.flags model (Just date) ) SetDueDateSuggestion date -> - resultModelCmd ( model, setDueDate flags model (Just date) ) + resultModelCmd ( model, setDueDate env.flags model (Just date) ) GetFolderResp (Ok fs) -> let @@ -613,7 +599,7 @@ update key flags inav settings msg model = |> List.map mkIdName |> Comp.Dropdown.SetOptions in - update key flags inav settings (FolderDropdownMsg opts) model_ + update inav env (FolderDropdownMsg opts) model_ GetFolderResp (Err _) -> resultModel model @@ -636,7 +622,7 @@ update key flags inav settings msg model = opts = Comp.Dropdown.SetOptions orgs.items in - update key flags inav settings (OrgDropdownMsg opts) model + update inav env (OrgDropdownMsg opts) model GetOrgResp (Err _) -> resultModel model @@ -673,18 +659,14 @@ update key flags inav settings msg model = { model | allPersons = personDict } res1 = - update key - flags - inav - settings + update inav + env (CorrPersonMsg (Comp.Dropdown.SetOptions corrRefs)) model_ res2 = - update key - flags - inav - settings + update inav + env (ConcPersonMsg (Comp.Dropdown.SetOptions concRefs)) res1.model in @@ -693,6 +675,7 @@ update key flags inav settings msg model = , sub = Sub.batch [ res1.sub, res2.sub ] , linkTarget = Comp.LinkTarget.LinkNone , removedItem = Nothing + , selectionChange = Data.ItemIds.noChange } GetPersonResp (Err _) -> @@ -706,14 +689,14 @@ update key flags inav settings msg model = equips.items ) in - update key flags inav settings (ConcEquipMsg opts) model + update inav env (ConcEquipMsg opts) model GetEquipResp (Err _) -> resultModel model SaveResp (Ok res) -> if res.success then - resultModelCmd ( model, Api.itemDetail flags model.item.id GetItemResp ) + resultModelCmd ( model, Api.itemDetail env.flags model.item.id GetItemResp ) else resultModel model @@ -742,10 +725,10 @@ update key flags inav settings msg model = result_ = case inav.next of Just id -> - resultModelCmd ( model, Page.set key (ItemDetailPage id) ) + resultModelCmd ( model, Page.set env.key (ItemDetailPage id) ) Nothing -> - resultModelCmd ( model, Page.set key (SearchPage Nothing) ) + resultModelCmd ( model, Page.set env.key (SearchPage Nothing) ) in { result_ | removedItem = Just removedId } @@ -756,7 +739,7 @@ update key flags inav settings msg model = resultModel model GetItemResp (Ok item) -> - update key flags inav settings (SetItem item) model + update inav env (SetItem item) model GetItemResp (Err _) -> resultModel model @@ -770,7 +753,7 @@ update key flags inav settings msg model = ItemMailMsg m -> let ( im, ic, fa ) = - Comp.ItemMail.update flags m model.itemMail + Comp.ItemMail.update env.flags m model.itemMail in case fa of Comp.ItemMail.FormNone -> @@ -798,7 +781,7 @@ update key flags inav settings msg model = ( { model | mailSending = True } , Cmd.batch [ Cmd.map ItemMailMsg ic - , Api.sendMail flags mail SendMailResp + , Api.sendMail env.flags mail SendMailResp ] ) @@ -850,7 +833,7 @@ update key flags inav settings msg model = MailSendFailed br.message } , if br.success then - Api.itemDetail flags model.item.id GetItemResp + Api.itemDetail env.flags model.item.id GetItemResp else Cmd.none @@ -895,7 +878,7 @@ update key flags inav settings msg model = Nothing -> let ( am, ac ) = - Comp.AttachmentMeta.init flags id + Comp.AttachmentMeta.init env.flags id nextMeta = Dict.insert id am model.attachMeta @@ -925,7 +908,7 @@ update key flags inav settings msg model = DeleteAttachConfirmed attachId -> let cmd = - Api.deleteAttachment flags attachId DeleteAttachResp + Api.deleteAttachment env.flags attachId DeleteAttachResp in resultModelCmd ( { model | attachModal = Nothing }, cmd ) @@ -934,7 +917,7 @@ update key flags inav settings msg model = DeleteAttachResp (Ok res) -> if res.success then - update key flags inav settings ReloadItem model + update inav env ReloadItem model else resultModel model @@ -980,7 +963,7 @@ update key flags inav settings msg model = SelectView svm -> let cmd = - Api.deleteAttachments flags svm.ids DeleteAttachResp + Api.deleteAttachments env.flags svm.ids DeleteAttachResp in resultModelCmd ( { model | attachModal = Nothing, viewMode = SimpleView }, cmd ) @@ -1029,7 +1012,7 @@ update key flags inav settings msg model = List.map makeFileId model.selectedFiles uploads = - Cmd.batch (Api.uploadAmend flags model.item.id model.selectedFiles AddFilesUploadResp) + Cmd.batch (Api.uploadAmend env.flags model.item.id model.selectedFiles AddFilesUploadResp) tracker = Sub.batch <| List.map (\id -> Http.track id (AddFilesProgress id)) fileids @@ -1112,7 +1095,7 @@ update key flags inav settings msg model = case result of Just ( src, trg, _ ) -> if src /= trg then - Api.moveAttachmentBefore flags + Api.moveAttachmentBefore env.flags model.item.id (MoveAttachment src trg) SaveResp @@ -1130,7 +1113,7 @@ update key flags inav settings msg model = Just mm -> let ( mm_, mc_, mv ) = - Comp.DetailEdit.update flags lm mm + Comp.DetailEdit.update env.flags lm mm ( model_, cmd_ ) = case mv of @@ -1138,7 +1121,7 @@ update key flags inav settings msg model = ( { model | modalEdit = Nothing }, Cmd.none ) Just _ -> - ( model, Api.itemDetail flags model.item.id GetItemResp ) + ( model, Api.itemDetail env.flags model.item.id GetItemResp ) Nothing -> ( { model | modalEdit = Just mm_ }, Cmd.none ) @@ -1180,7 +1163,7 @@ update key flags inav settings msg model = Just oid -> let ( m, c ) = - Comp.DetailEdit.editOrg flags oid Comp.OrgForm.emptyModel + Comp.DetailEdit.editOrg env.flags oid Comp.OrgForm.emptyModel in resultModelCmd ( { model | modalEdit = Just m }, Cmd.map ModalEditMsg c ) @@ -1198,7 +1181,7 @@ update key flags inav settings msg model = Just eid -> let ( m, c ) = - Comp.DetailEdit.editEquip flags eid Comp.EquipmentForm.emptyModel + Comp.DetailEdit.editEquip env.flags eid Comp.EquipmentForm.emptyModel in resultModelCmd ( { model | modalEdit = Just m }, Cmd.map ModalEditMsg c ) @@ -1209,7 +1192,7 @@ update key flags inav settings msg model = let ( pm, pc ) = Comp.DetailEdit.initCorrPerson - flags + env.flags model.item.id Comp.PersonForm.emptyModel in @@ -1222,7 +1205,7 @@ update key flags inav settings msg model = let ( p, c ) = Comp.DetailEdit.initConcPerson - flags + env.flags model.item.id Comp.PersonForm.emptyModel in @@ -1242,7 +1225,7 @@ update key flags inav settings msg model = Just pid -> let ( m, c ) = - Comp.DetailEdit.editPerson flags pid Comp.PersonForm.emptyModel + Comp.DetailEdit.editPerson env.flags pid Comp.PersonForm.emptyModel in resultModelCmd ( { model | modalEdit = Just m }, Cmd.map ModalEditMsg c ) @@ -1318,7 +1301,7 @@ update key flags inav settings msg model = resultModelCmd ( model , Api.setAttachmentName - flags + env.flags m.id (Util.Maybe.fromString m.newName) EditAttachNameResp @@ -1370,15 +1353,15 @@ update key flags inav settings msg model = in if keys == Just Comp.KeyInput.ctrlC then if model.item.state == "created" then - update key flags inav settings ConfirmItem model_ + update inav env ConfirmItem model_ else - update key flags inav settings UnconfirmItem model_ + update inav env UnconfirmItem model_ else if keys == Just Comp.KeyInput.ctrlPoint then case inav.next of Just id -> - resultModelCmd ( model_, Page.set key (ItemDetailPage id) ) + resultModelCmd ( model_, Page.set env.key (ItemDetailPage id) ) Nothing -> resultModel model_ @@ -1386,7 +1369,7 @@ update key flags inav settings msg model = else if keys == Just Comp.KeyInput.ctrlComma then case inav.prev of Just id -> - resultModelCmd ( model_, Page.set key (ItemDetailPage id) ) + resultModelCmd ( model_, Page.set env.key (ItemDetailPage id) ) Nothing -> resultModel model_ @@ -1405,7 +1388,7 @@ update key flags inav settings msg model = let model_ = { model - | menuOpen = settings.sideMenuVisible + | menuOpen = env.settings.sideMenuVisible } in resultModel model_ @@ -1416,12 +1399,13 @@ update key flags inav settings msg model = , sub = Sub.none , linkTarget = lt , removedItem = Nothing + , selectionChange = Data.ItemIds.noChange } CustomFieldMsg lm -> let result = - Comp.CustomFieldMultiInput.update flags lm model.customFieldsModel + Comp.CustomFieldMultiInput.update env.flags lm model.customFieldsModel cmd_ = Cmd.map CustomFieldMsg result.cmd @@ -1438,7 +1422,7 @@ update key flags inav settings msg model = ( Cmd.none, model.customFieldSavingIcon ) FieldValueRemove field -> - ( Api.deleteCustomValue flags + ( Api.deleteCustomValue env.flags model.item.id field.id (CustomFieldRemoveResp field.id) @@ -1446,7 +1430,7 @@ update key flags inav settings msg model = ) FieldValueChange field value -> - ( Api.putCustomValue flags + ( Api.putCustomValue env.flags model.item.id (CustomFieldValue field.id value) (CustomFieldSaveResp field value) @@ -1497,7 +1481,7 @@ update key flags inav settings msg model = if res.success then resultModelCmd ( model_ - , Api.itemDetail flags model.item.id GetItemResp + , Api.itemDetail env.flags model.item.id GetItemResp ) else @@ -1551,7 +1535,7 @@ update key flags inav settings msg model = ReprocessFileConfirmed id -> let cmd = - Api.reprocessItem flags model.item.id [ id ] ReprocessFileResp + Api.reprocessItem env.flags model.item.id [ id ] ReprocessFileResp in resultModelCmd ( { model | attachModal = Nothing }, cmd ) @@ -1572,7 +1556,7 @@ update key flags inav settings msg model = ReprocessItemConfirmed -> let cmd = - Api.reprocessItem flags model.item.id [] ReprocessFileResp + Api.reprocessItem env.flags model.item.id [] ReprocessFileResp in resultModelCmd ( { model | itemModal = Nothing }, cmd ) @@ -1594,9 +1578,9 @@ update key flags inav settings msg model = ) RestoreItem -> - resultModelCmd ( model, Api.restoreItem flags model.item.id SaveResp ) + resultModelCmd ( model, Api.restoreItem env.flags model.item.id SaveResp ) - ToggleShowQrItem id -> + ToggleShowQrItem _ -> let sqm = model.showQrModel @@ -1606,7 +1590,7 @@ update key flags inav settings msg model = in resultModel { model | showQrModel = next, mobileItemMenuOpen = False } - ToggleShowQrAttach id -> + ToggleShowQrAttach _ -> let sqm = model.showQrModel @@ -1619,6 +1603,16 @@ update key flags inav settings msg model = PrintElement id -> resultModelCmd ( model, Ports.printElement id ) + ToggleSelectItem -> + let + res = + resultModel { model | mobileItemMenuOpen = False } + + newSelection = + Data.ItemIds.toggle env.selectedItems model.item.id + in + { res | selectionChange = newSelection } + --- Helper diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm index a321744b..40094489 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm @@ -27,8 +27,10 @@ import Comp.ItemDetail.SingleAttachment import Comp.ItemMail import Comp.MenuBar as MB import Comp.SentMails +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.Icons as Icons +import Data.ItemIds import Data.ItemNav exposing (ItemNav) import Data.UiSettings exposing (UiSettings) import Html exposing (..) @@ -39,13 +41,13 @@ import Page exposing (Page(..)) import Styles as S -view : Texts -> Flags -> ItemNav -> UiSettings -> Model -> Html Msg -view texts flags inav settings model = +view : Texts -> ItemNav -> Env.View -> Model -> Html Msg +view texts inav env model = div [ class "flex flex-col h-full" ] - [ header texts settings inav model + [ header texts inav env model -- , menuBar texts inav settings model - , body texts flags inav settings model + , body texts env.flags inav env.settings model , itemModal texts model ] @@ -60,25 +62,35 @@ itemModal texts model = span [ class "hidden" ] [] -header : Texts -> UiSettings -> ItemNav -> Model -> Html Msg -header texts settings inav model = +header : Texts -> ItemNav -> Env.View -> Model -> Html Msg +header texts inav env model = div [ class "my-3" ] [ Comp.ItemDetail.ItemInfoHeader.view texts.itemInfoHeader - settings + env.settings model - (menuBar texts inav settings model) + (menuBar texts inav env model) ] -menuBar : Texts -> ItemNav -> UiSettings -> Model -> Html Msg -menuBar texts inav settings model = +menuBar : Texts -> ItemNav -> Env.View -> Model -> Html Msg +menuBar texts inav env model = let keyDescr name = - if settings.itemDetailShortcuts && model.menuOpen then + if env.settings.itemDetailShortcuts && model.menuOpen then " " ++ texts.key ++ "'" ++ name ++ "'." else "" + + isSelected = + Data.ItemIds.isMember env.selectedItems model.item.id + + foldSelected fsel funsel = + if isSelected then + fsel + + else + funsel in MB.view { start = @@ -210,7 +222,18 @@ menuBar texts inav settings model = , toggleMenu = ToggleMobileItemMenu , menuOpen = model.mobileItemMenuOpen , items = - [ { icon = i [ class "fa fa-envelope font-thin" ] [] + [ { icon = + foldSelected + (i [ class "fa fa-check-square dark:text-lime-400 text-lime-600" ] []) + (i [ class "fa fa-tasks" ] []) + , label = foldSelected texts.deselectItem texts.selectItem + , disabled = False + , attrs = + [ href "#" + , onClick ToggleSelectItem + ] + } + , { icon = i [ class "fa fa-envelope font-thin" ] [] , label = texts.sendMail , disabled = False , attrs = @@ -281,13 +304,28 @@ menuBar texts inav settings model = ] , end = [ MB.CustomElement <| + a + [ href "#" + , onClick ToggleSelectItem + , title (foldSelected texts.deselectItem texts.selectItem) + , class "hidden md:flex flex-row items-center h-full " + , classList + [ ( S.greenButton, isSelected ) + , ( S.secondaryBasicButton, not isSelected ) + ] + ] + [ foldSelected + (i [ class "fa fa-square-check" ] []) + (i [ class "fa fa-list-check" ] []) + ] + , MB.CustomElement <| a [ class S.secondaryBasicButton , href "#" , onClick UnconfirmItem , title texts.unconfirmItemMetadata - , classList [ ( "hidden", model.item.state == "created" ) ] - , class "hidden md:block" + , class "hidden" + , classList [ ( "md:block", model.item.state /= "created" ) ] ] [ i [ class "fa fa-eye-slash font-thin" ] [] ] diff --git a/modules/webapp/src/main/elm/Comp/SearchMenu.elm b/modules/webapp/src/main/elm/Comp/SearchMenu.elm index 35739d6b..2937c320 100644 --- a/modules/webapp/src/main/elm/Comp/SearchMenu.elm +++ b/modules/webapp/src/main/elm/Comp/SearchMenu.elm @@ -19,6 +19,7 @@ module Comp.SearchMenu exposing , linkTargetMsg , refreshBookmarks , setFromStats + , setIncludeSelection , textSearchString , update , updateDrop @@ -52,6 +53,7 @@ import Data.EquipmentOrder import Data.EquipmentUse import Data.Fields import Data.Flags exposing (Flags) +import Data.ItemIds exposing (ItemIdChange, ItemIds) import Data.ItemQuery as Q exposing (ItemQuery) import Data.PersonOrder import Data.PersonUse @@ -102,6 +104,7 @@ type alias Model = , sourceModel : Maybe String , allBookmarks : Comp.BookmarkChooser.Model , selectedBookmarks : Comp.BookmarkChooser.Selection + , includeSelection : Bool , openTabs : Set String , searchMode : SearchMode } @@ -149,6 +152,7 @@ init flags = , sourceModel = Nothing , allBookmarks = Comp.BookmarkChooser.init Data.Bookmarks.empty , selectedBookmarks = Comp.BookmarkChooser.emptySelection + , includeSelection = False , openTabs = Set.fromList [ "Tags", "Inbox" ] , searchMode = Data.SearchMode.Normal } @@ -222,8 +226,8 @@ isNamesSearch model = True -getItemQuery : Model -> Maybe ItemQuery -getItemQuery model = +getItemQuery : ItemIds -> Model -> Maybe ItemQuery +getItemQuery selectedItems model = let when flag body = if flag then @@ -258,6 +262,11 @@ getItemQuery model = in Q.and [ when model.inboxCheckbox (Q.Inbox True) + , if model.includeSelection then + Data.ItemIds.toQuery selectedItems + + else + Nothing , whenNotEmpty (model.tagSelection.includeTags |> Set.toList) (Q.TagIds Q.AllMatch) , whenNotEmpty (model.tagSelection.excludeTags |> Set.toList) @@ -347,6 +356,7 @@ resetModel model = , customValues = Data.CustomFieldChange.emptyCollect , sourceModel = Nothing , selectedBookmarks = Comp.BookmarkChooser.emptySelection + , includeSelection = False , searchMode = Data.SearchMode.Normal } @@ -397,6 +407,8 @@ type Msg | ToggleOpenAllAkkordionTabs | AllBookmarksResp (Result Http.Error AllBookmarks) | SelectBookmarkMsg Comp.BookmarkChooser.Msg + | SetIncludeSelection Bool + | ClearSelection setFromStats : SearchStats -> Msg @@ -409,6 +421,11 @@ initFromStats stats = GetAllTagsResp (Ok stats) +setIncludeSelection : Bool -> Msg +setIncludeSelection flag = + SetIncludeSelection flag + + linkTargetMsg : LinkTarget -> Maybe Msg linkTargetMsg linkTarget = case linkTarget of @@ -449,6 +466,7 @@ type alias NextState = , sub : Sub Msg , stateChange : Bool , dragDrop : DD.DragDropData + , selectionChange : ItemIdChange } @@ -483,6 +501,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = True , dragDrop = set.dragDrop + , selectionChange = Data.ItemIds.noChange } in case msg of @@ -525,6 +544,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ResetForm -> @@ -533,6 +553,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = True , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetCorrOrg id -> @@ -555,6 +576,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetConcEquip id -> @@ -586,6 +608,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = sel /= model.selectedBookmarks , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetAllTagsResp (Ok stats) -> @@ -600,6 +623,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetAllTagsResp (Err _) -> @@ -608,6 +632,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetStatsResp (Ok stats) -> @@ -676,6 +701,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetStatsResp (Err _) -> @@ -684,6 +710,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetEquipResp (Ok equips) -> @@ -699,6 +726,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetOrgResp (Ok orgs) -> @@ -714,6 +742,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } GetPersonResp (Ok ps) -> @@ -749,6 +778,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } TagSelectMsg m -> @@ -765,6 +795,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = sel /= model.tagSelection , dragDrop = ddd + , selectionChange = Data.ItemIds.noChange } DirectionMsg m -> @@ -777,6 +808,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = isDropdownChangeMsg m , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } OrgMsg m -> @@ -789,6 +821,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = isDropdownChangeMsg m , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } CorrPersonMsg m -> @@ -801,6 +834,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = isDropdownChangeMsg m , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ConcPersonMsg m -> @@ -813,6 +847,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = isDropdownChangeMsg m , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ConcEquipmentMsg m -> @@ -825,6 +860,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = isDropdownChangeMsg m , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ToggleInbox -> @@ -837,6 +873,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = True , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ToggleSearchMode -> @@ -856,6 +893,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = True , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } FromDateMsg m -> @@ -876,6 +914,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.fromDate /= nextDate , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } UntilDateMsg m -> @@ -896,6 +935,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.untilDate /= nextDate , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } FromDueDateMsg m -> @@ -916,6 +956,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.fromDueDate /= nextDate , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } UntilDueDateMsg m -> @@ -936,6 +977,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.untilDueDate /= nextDate , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetName str -> @@ -948,6 +990,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetTextSearch str -> @@ -956,6 +999,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SwapTextSearch -> @@ -965,6 +1009,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } else @@ -973,6 +1018,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetFulltextSearch -> @@ -983,6 +1029,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } Names s -> @@ -991,6 +1038,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetNamesSearch -> @@ -1001,6 +1049,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } Names _ -> @@ -1009,6 +1058,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } KeyUpMsg (Just Enter) -> @@ -1017,6 +1067,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = True , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } KeyUpMsg _ -> @@ -1025,6 +1076,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } FolderSelectMsg lm -> @@ -1041,6 +1093,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.selectedFolder /= sel , dragDrop = ddd + , selectionChange = Data.ItemIds.noChange } CustomFieldMsg lm -> @@ -1058,6 +1111,7 @@ updateDrop ddm flags settings msg model = , stateChange = Data.CustomFieldChange.isValueChange res.result , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SetCustomField cv -> @@ -1086,6 +1140,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ResetToSource str -> @@ -1105,6 +1160,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } ToggleOpenAllAkkordionTabs -> @@ -1125,6 +1181,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } AllBookmarksResp (Ok bm) -> @@ -1133,6 +1190,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = model.allBookmarks /= Comp.BookmarkChooser.init bm , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } AllBookmarksResp (Err _) -> @@ -1141,6 +1199,7 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = False , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange } SelectBookmarkMsg lm -> @@ -1153,6 +1212,26 @@ updateDrop ddm flags settings msg model = , sub = Sub.none , stateChange = sel /= model.selectedBookmarks || model.allBookmarks /= next , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange + } + + SetIncludeSelection flag -> + { model = + { model | includeSelection = flag } + , cmd = Cmd.none + , sub = Sub.none + , stateChange = True + , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.noChange + } + + ClearSelection -> + { model = { model | includeSelection = False } + , cmd = Cmd.none + , sub = Sub.none + , stateChange = True + , dragDrop = DD.DragDropData ddm Nothing + , selectionChange = Data.ItemIds.clearAll } @@ -1162,6 +1241,7 @@ updateDrop ddm flags settings msg model = type alias ViewConfig = { overrideTabLook : SearchTab -> Comp.Tabs.Look -> Comp.Tabs.Look + , selectedItems : ItemIds } @@ -1174,7 +1254,7 @@ viewDrop2 texts ddd flags cfg settings model = Comp.Tabs.akkordion akkordionStyle (searchTabState settings cfg model) - (searchTabs texts ddd flags settings model) + (searchTabs texts ddd flags settings cfg.selectedItems model) type SearchTab @@ -1191,6 +1271,7 @@ type SearchTab | TabSource | TabDirection | TabTrashed + | TabSelection allTabs : List SearchTab @@ -1208,6 +1289,7 @@ allTabs = , TabSource , TabDirection , TabTrashed + , TabSelection ] @@ -1253,6 +1335,9 @@ tabName tab = TabTrashed -> "trashed" + TabSelection -> + "selection" + findTab : Comp.Tabs.Tab msg -> Maybe SearchTab findTab tab = @@ -1296,12 +1381,15 @@ findTab tab = "trashed" -> Just TabTrashed + "selection" -> + Just TabSelection + _ -> Nothing -tabLook : UiSettings -> Model -> SearchTab -> Comp.Tabs.Look -tabLook settings model tab = +tabLook : UiSettings -> ItemIds -> Model -> SearchTab -> Comp.Tabs.Look +tabLook settings selectedItems model tab = let isHidden f = Data.UiSettings.fieldHidden settings f @@ -1396,6 +1484,16 @@ tabLook settings model tab = TabTrashed -> activeWhen (model.searchMode == Data.SearchMode.Trashed) + TabSelection -> + if Data.ItemIds.isEmpty selectedItems then + Comp.Tabs.Hidden + + else if model.includeSelection then + Comp.Tabs.Active + + else + Comp.Tabs.Normal + _ -> Comp.Tabs.Normal @@ -1416,15 +1514,15 @@ searchTabState settings cfg model tab = state = { folded = folded , look = - Maybe.map (\t -> tabLook settings model t |> cfg.overrideTabLook t) searchTab + Maybe.map (\t -> tabLook settings cfg.selectedItems model t |> cfg.overrideTabLook t) searchTab |> Maybe.withDefault Comp.Tabs.Normal } in ( state, ToggleAkkordionTab tab.name ) -searchTabs : Texts -> DD.DragDropData -> Flags -> UiSettings -> Model -> List (Comp.Tabs.Tab Msg) -searchTabs texts ddd flags settings model = +searchTabs : Texts -> DD.DragDropData -> Flags -> UiSettings -> ItemIds -> Model -> List (Comp.Tabs.Tab Msg) +searchTabs texts ddd flags settings selectedItems model = let isHidden f = Data.UiSettings.fieldHidden settings f @@ -1477,6 +1575,40 @@ searchTabs texts ddd flags settings model = (Comp.BookmarkChooser.view texts.bookmarkChooser model.allBookmarks model.selectedBookmarks) ] } + , { name = tabName TabSelection + , title = texts.selection + , titleRight = + [ span [ class "flex items-center rounded-full bg-blue-100 dark:bg-sky-600 text-xs px-2 py-0.5 " ] + [ text (String.fromInt (Data.ItemIds.size selectedItems)) ] + ] + , info = Nothing + , body = + [ div [ class "flex flex-col ml-1" ] + [ a + [ class "flex flex-row items-center" + , class "rounded px-1 py-1 hover:bg-blue-100 dark:hover:bg-slate-600" + , href "#" + , Html.Events.onClick (SetIncludeSelection (not model.includeSelection)) + ] + [ if model.includeSelection then + i [ class "fa fa-check mr-2" ] [] + + else + i [ class "fa fa-list-check mr-2" ] [] + , text texts.showSelection + ] + , a + [ class "flex flex-row items-center" + , class "rounded px-1 py-1 hover:bg-blue-100 dark:hover:bg-slate-600" + , href "#" + , Html.Events.onClick ClearSelection + ] + [ i [ class "fa fa-times mr-2" ] [] + , text texts.clearSelection + ] + ] + ] + } , { name = tabName TabTags , title = texts.basics.tags , titleRight = [] diff --git a/modules/webapp/src/main/elm/Data/Environment.elm b/modules/webapp/src/main/elm/Data/Environment.elm new file mode 100644 index 00000000..c9d58de9 --- /dev/null +++ b/modules/webapp/src/main/elm/Data/Environment.elm @@ -0,0 +1,29 @@ +{- + Copyright 2020 Eike K. & Contributors + + SPDX-License-Identifier: AGPL-3.0-or-later +-} + + +module Data.Environment exposing (..) + +import Browser.Navigation as Nav +import Data.Flags exposing (Flags) +import Data.ItemIds exposing (ItemIds) +import Data.UiSettings exposing (UiSettings) + + +type alias Update = + { key : Nav.Key + , selectedItems : ItemIds + , flags : Flags + , settings : UiSettings + } + + +type alias View = + { flags : Flags + , sidebarVisible : Bool + , settings : UiSettings + , selectedItems : ItemIds + } diff --git a/modules/webapp/src/main/elm/Data/ItemIds.elm b/modules/webapp/src/main/elm/Data/ItemIds.elm new file mode 100644 index 00000000..54760187 --- /dev/null +++ b/modules/webapp/src/main/elm/Data/ItemIds.elm @@ -0,0 +1,154 @@ +{- + Copyright 2020 Eike K. & Contributors + + SPDX-License-Identifier: AGPL-3.0-or-later +-} + + +module Data.ItemIds exposing + ( ItemIdChange + , ItemIds + , apply + , clearAll + , combine + , combineAll + , deselect + , empty + , fromSet + , isEmpty + , isMember + , noChange + , nonEmpty + , select + , selectAll + , size + , toList + , toQuery + , toggle + , union + ) + +import Data.ItemQuery exposing (ItemQuery) +import Set exposing (Set) + + +type ItemIds + = ItemIds (Set String) + + +empty : ItemIds +empty = + ItemIds Set.empty + + +isEmpty : ItemIds -> Bool +isEmpty (ItemIds ids) = + Set.isEmpty ids + + +nonEmpty : ItemIds -> Bool +nonEmpty ids = + not (isEmpty ids) + + +isMember : ItemIds -> String -> Bool +isMember (ItemIds ids) id = + Set.member id ids + + +size : ItemIds -> Int +size (ItemIds ids) = + Set.size ids + + +fromSet : Set String -> ItemIds +fromSet ids = + ItemIds ids + + +union : ItemIds -> ItemIds -> ItemIds +union (ItemIds ids1) (ItemIds ids2) = + ItemIds (Set.union ids1 ids2) + + +toList : ItemIds -> List String +toList (ItemIds ids) = + Set.toList ids + + +toQuery : ItemIds -> Maybe ItemQuery +toQuery (ItemIds ids) = + if Set.isEmpty ids then + Nothing + + else + Just <| Data.ItemQuery.ItemIdIn (Set.toList ids) + + + +--- Change item ids + + +type ItemIdChange + = ItemIdChange + { remove : Set String + , add : Set String + , clear : Bool + } + + +apply : ItemIds -> ItemIdChange -> ItemIds +apply (ItemIds ids) (ItemIdChange { remove, add, clear }) = + if clear then + empty + + else + ItemIds (Set.diff ids remove |> Set.union add) + + +noChange : ItemIdChange +noChange = + ItemIdChange { remove = Set.empty, add = Set.empty, clear = False } + + +combine : ItemIdChange -> ItemIdChange -> ItemIdChange +combine (ItemIdChange c1) (ItemIdChange c2) = + ItemIdChange + { remove = Set.union c1.remove c2.remove + , add = Set.union c1.add c2.add + , clear = False + } + + +combineAll : List ItemIdChange -> ItemIdChange +combineAll all = + List.foldl combine noChange all + + +select : String -> ItemIdChange +select id = + ItemIdChange { add = Set.singleton id, remove = Set.empty, clear = False } + + +selectAll : Set String -> ItemIdChange +selectAll ids = + ItemIdChange { add = ids, remove = Set.empty, clear = False } + + +deselect : String -> ItemIdChange +deselect id = + ItemIdChange { add = Set.empty, remove = Set.singleton id, clear = False } + + +clearAll : ItemIdChange +clearAll = + ItemIdChange { add = Set.empty, remove = Set.empty, clear = True } + + +toggle : ItemIds -> String -> ItemIdChange +toggle ids id = + if isMember ids id then + deselect id + + else + select id diff --git a/modules/webapp/src/main/elm/Data/ItemSelection.elm b/modules/webapp/src/main/elm/Data/ItemSelection.elm index 05d8a911..e6f49776 100644 --- a/modules/webapp/src/main/elm/Data/ItemSelection.elm +++ b/modules/webapp/src/main/elm/Data/ItemSelection.elm @@ -11,12 +11,12 @@ module Data.ItemSelection exposing , isSelected ) -import Set exposing (Set) +import Data.ItemIds exposing (ItemIds) type ItemSelection = Inactive - | Active (Set String) + | Active ItemIds isSelected : String -> ItemSelection -> Bool @@ -26,7 +26,7 @@ isSelected id set = False Active ids -> - Set.member id ids + Data.ItemIds.isMember ids id isActive : ItemSelection -> Bool diff --git a/modules/webapp/src/main/elm/Messages/Comp/ItemDetail.elm b/modules/webapp/src/main/elm/Messages/Comp/ItemDetail.elm index 59af72d8..11a315e8 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/ItemDetail.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/ItemDetail.elm @@ -57,6 +57,8 @@ type alias Texts = , mailSendSuccessful : String , showQrCode : String , close : String + , selectItem : String + , deselectItem : String } @@ -93,6 +95,8 @@ gb = , mailSendSuccessful = "Mail sent." , showQrCode = "Show URL as QR code" , close = "Close" + , selectItem = "Select this item" + , deselectItem = "Deselect this item" } @@ -115,7 +119,7 @@ de = , addMoreFiles = "Diesem Dokument weitere Dateien anfügen" , confirmItemMetadata = "Metadaten bestätigen" , confirm = "Bestätige" - , unconfirmItemMetadata = "Widerrufe" + , unconfirmItemMetadata = "Widerrufe Bestätigung" , reprocessItem = "Das Dokument erneut verarbeiten" , deleteThisItem = "Das Dokument löschen" , undeleteThisItem = "Das Dokument wiederherstellen" @@ -129,4 +133,6 @@ de = , mailSendSuccessful = "E-Mail wurde versendet." , showQrCode = "Link als QR code anzeigen" , close = "Schließen" + , selectItem = "Zur Auswahl hinzufügen" + , deselectItem = "Aus Auswahl entfernen" } diff --git a/modules/webapp/src/main/elm/Messages/Comp/ItemDetail/MultiEditMenu.elm b/modules/webapp/src/main/elm/Messages/Comp/ItemDetail/MultiEditMenu.elm index b46a81d1..0cb8edfd 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/ItemDetail/MultiEditMenu.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/ItemDetail/MultiEditMenu.elm @@ -64,7 +64,7 @@ de = , chooseDirection = "Wähle eine Richtung…" , confirmUnconfirm = "Bestätige/Widerrufe Metadaten" , confirm = "Bestätige" - , unconfirm = "Widerrufe" + , unconfirm = "Widerrufe Bestätigung" , changeTagMode = "Wechsel den Änderungsmodus für Tags" , dueDateTab = "Fälligkeitsdatum" , direction = Messages.Data.Direction.de diff --git a/modules/webapp/src/main/elm/Messages/Comp/SearchMenu.elm b/modules/webapp/src/main/elm/Messages/Comp/SearchMenu.elm index e526ac19..6b301bc7 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/SearchMenu.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/SearchMenu.elm @@ -50,6 +50,9 @@ type alias Texts = , direction : Direction -> String , trashcan : String , bookmarks : String + , selection : String + , showSelection : String + , clearSelection : String } @@ -84,6 +87,9 @@ gb = , direction = Messages.Data.Direction.gb , trashcan = "Trash" , bookmarks = "Bookmarks" + , selection = "Selection" + , showSelection = "Show selection" + , clearSelection = "Clear selection" } @@ -118,4 +124,7 @@ de = , direction = Messages.Data.Direction.de , trashcan = "Papierkorb" , bookmarks = "Bookmarks" + , selection = "Auswahl" + , showSelection = "Auswahl anzeigen" + , clearSelection = "Auswahl aufheben" } diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm index 21c8939c..3d0111a4 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm @@ -17,6 +17,7 @@ import Browser.Dom as Dom import Comp.ItemDetail import Comp.ItemDetail.Model import Comp.LinkTarget exposing (LinkTarget) +import Data.ItemIds exposing (ItemIds) import Http @@ -45,4 +46,5 @@ type alias UpdateResult = , sub : Sub Msg , linkTarget : LinkTarget , removedItem : Maybe String + , selectedItems : ItemIds } diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm index 5e4e7128..f8ef0541 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm @@ -8,29 +8,26 @@ module Page.ItemDetail.Update exposing (update) import Api -import Browser.Navigation as Nav import Comp.ItemDetail import Comp.ItemDetail.Model import Comp.LinkTarget -import Data.Flags exposing (Flags) +import Data.Environment as Env +import Data.ItemIds import Data.ItemNav exposing (ItemNav) -import Data.UiSettings exposing (UiSettings) import Page exposing (Page(..)) import Page.ItemDetail.Data exposing (Model, Msg(..), UpdateResult) import Scroll import Task -update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> UpdateResult -update key flags inav settings msg model = +update : ItemNav -> Env.Update -> Msg -> Model -> UpdateResult +update inav env msg model = case msg of Init id -> let result = - Comp.ItemDetail.update key - flags - inav - settings + Comp.ItemDetail.update inav + env Comp.ItemDetail.Model.Init model.detail @@ -40,19 +37,20 @@ update key flags inav settings msg model = { model = { model | detail = result.model } , cmd = Cmd.batch - [ Api.itemDetail flags id ItemResp + [ Api.itemDetail env.flags id ItemResp , Cmd.map ItemDetailMsg result.cmd , Task.attempt ScrollResult task ] , sub = Sub.map ItemDetailMsg result.sub , linkTarget = result.linkTarget , removedItem = result.removedItem + , selectedItems = env.selectedItems } ItemDetailMsg lmsg -> let result = - Comp.ItemDetail.update key flags inav settings lmsg model.detail + Comp.ItemDetail.update inav env lmsg model.detail pageSwitch = case result.linkTarget of @@ -60,13 +58,14 @@ update key flags inav settings msg model = Cmd.none _ -> - Page.set key (SearchPage Nothing) + Page.set env.key (SearchPage Nothing) in { model = { model | detail = result.model } , cmd = Cmd.batch [ pageSwitch, Cmd.map ItemDetailMsg result.cmd ] , sub = Sub.map ItemDetailMsg result.sub , linkTarget = result.linkTarget , removedItem = result.removedItem + , selectedItems = Data.ItemIds.apply env.selectedItems result.selectionChange } ItemResp (Ok item) -> @@ -74,17 +73,28 @@ update key flags inav settings msg model = lmsg = Comp.ItemDetail.Model.SetItem item in - update key flags inav settings (ItemDetailMsg lmsg) model + update inav env (ItemDetailMsg lmsg) model ItemResp (Err _) -> - UpdateResult model Cmd.none Sub.none Comp.LinkTarget.LinkNone Nothing + unit env model ScrollResult _ -> - UpdateResult model Cmd.none Sub.none Comp.LinkTarget.LinkNone Nothing + unit env model UiSettingsUpdated -> let lmsg = ItemDetailMsg Comp.ItemDetail.Model.UiSettingsUpdated in - update key flags inav settings lmsg model + update inav env lmsg model + + +unit : Env.Update -> Model -> UpdateResult +unit env model = + { model = model + , cmd = Cmd.none + , sub = Sub.none + , linkTarget = Comp.LinkTarget.LinkNone + , removedItem = Nothing + , selectedItems = env.selectedItems + } diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/View2.elm b/modules/webapp/src/main/elm/Page/ItemDetail/View2.elm index 00ab403a..8f1c2855 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/View2.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/View2.elm @@ -12,6 +12,7 @@ import Comp.ItemDetail import Comp.ItemDetail.EditForm import Comp.ItemDetail.Model import Comp.MenuBar as MB +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.ItemNav exposing (ItemNav) import Data.UiSettings exposing (UiSettings) @@ -23,13 +24,13 @@ import Page.ItemDetail.Data exposing (..) import Styles as S -viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg -viewSidebar texts visible flags settings model = +viewSidebar : Texts -> Env.View -> Model -> Html Msg +viewSidebar texts env model = div [ id "sidebar" , class S.sidebar , class S.sidebarBg - , classList [ ( "hidden", not visible ) ] + , classList [ ( "hidden", not env.sidebarVisible ) ] ] [ div [ class S.header2 @@ -57,16 +58,16 @@ viewSidebar texts visible flags settings model = , sticky = True } , Html.map ItemDetailMsg - (Comp.ItemDetail.EditForm.view2 texts.editForm flags settings model.detail) + (Comp.ItemDetail.EditForm.view2 texts.editForm env.flags env.settings model.detail) ] -viewContent : Texts -> ItemNav -> Flags -> UiSettings -> Model -> Html Msg -viewContent texts inav flags settings model = +viewContent : Texts -> ItemNav -> Env.View -> Model -> Html Msg +viewContent texts inav env model = div [ id "content" , class S.content ] [ Html.map ItemDetailMsg - (Comp.ItemDetail.view2 texts.itemDetail flags inav settings model.detail) + (Comp.ItemDetail.view2 texts.itemDetail inav env model.detail) ] diff --git a/modules/webapp/src/main/elm/Page/Search/Data.elm b/modules/webapp/src/main/elm/Page/Search/Data.elm index ca830f94..6679ea77 100644 --- a/modules/webapp/src/main/elm/Page/Search/Data.elm +++ b/modules/webapp/src/main/elm/Page/Search/Data.elm @@ -42,6 +42,7 @@ import Comp.PublishItems import Comp.SearchMenu import Data.Flags exposing (Flags) import Data.ItemArrange exposing (ItemArrange) +import Data.ItemIds exposing (ItemIds) import Data.ItemNav exposing (ItemNav) import Data.ItemQuery as Q import Data.Items @@ -84,8 +85,7 @@ type ConfirmModalValue type alias SelectViewModel = - { ids : Set String - , action : SelectActionMode + { action : SelectActionMode , confirmModal : Maybe ConfirmModalValue , editModel : Comp.ItemDetail.MultiEditMenu.Model , mergeModel : Comp.ItemMerge.Model @@ -97,8 +97,7 @@ type alias SelectViewModel = initSelectViewModel : Flags -> SelectViewModel initSelectViewModel flags = - { ids = Set.empty - , action = NoneAction + { action = NoneAction , confirmModal = Nothing , editModel = Comp.ItemDetail.MultiEditMenu.init , mergeModel = Comp.ItemMerge.init [] @@ -235,6 +234,7 @@ type Msg | ToggleExpandCollapseRows | ToggleBookmarkCurrentQueryView | BookmarkQueryMsg Comp.BookmarkQueryManage.Msg + | ItemSelectionChanged type SearchType @@ -258,6 +258,7 @@ type alias SearchParam = , pageSize : Int , offset : Int , scroll : Bool + , selectedItems : ItemIds } @@ -276,7 +277,7 @@ doSearchDefaultCmd param model = let smask = Q.request model.searchMenuModel.searchMode <| - createQuery model + createQuery param.selectedItems model mask = { smask @@ -294,10 +295,10 @@ doSearchDefaultCmd param model = Api.itemSearch param.flags mask ItemSearchAddResp -createQuery : Model -> Maybe Q.ItemQuery -createQuery model = +createQuery : ItemIds -> Model -> Maybe Q.ItemQuery +createQuery selectedItems model = Q.and - [ Comp.SearchMenu.getItemQuery model.searchMenuModel + [ Comp.SearchMenu.getItemQuery selectedItems model.searchMenuModel , Maybe.map Q.Fragment (Comp.PowerSearchInput.getSearchString model.powerSearchInput) ] diff --git a/modules/webapp/src/main/elm/Page/Search/SideMenu.elm b/modules/webapp/src/main/elm/Page/Search/SideMenu.elm index 556858cd..475162a7 100644 --- a/modules/webapp/src/main/elm/Page/Search/SideMenu.elm +++ b/modules/webapp/src/main/elm/Page/Search/SideMenu.elm @@ -11,6 +11,7 @@ import Comp.Basic as B import Comp.ItemDetail.MultiEditMenu import Comp.MenuBar as MB import Comp.SearchMenu +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.UiSettings exposing (UiSettings) import Html exposing (..) @@ -22,8 +23,8 @@ import Set import Styles as S -view : Texts -> Flags -> UiSettings -> Model -> Html Msg -view texts flags settings model = +view : Texts -> Env.View -> Model -> Html Msg +view texts env model = div [ class "flex flex-col" ] @@ -56,19 +57,19 @@ view texts flags settings model = SelectView svm -> case svm.action of EditSelected -> - viewEditMenu texts flags svm settings + viewEditMenu texts env.flags svm env.settings _ -> - viewSearch texts flags settings model + viewSearch texts env model _ -> - viewSearch texts flags settings model + viewSearch texts env model ) ] -viewSearch : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) -viewSearch texts flags settings model = +viewSearch : Texts -> Env.View -> Model -> List (Html Msg) +viewSearch texts env model = [ MB.viewSide { start = [ MB.CustomElement <| @@ -87,14 +88,15 @@ viewSearch texts flags settings model = , let searchMenuCfg = { overrideTabLook = \_ -> identity + , selectedItems = env.selectedItems } in Html.map SearchMenuMsg (Comp.SearchMenu.viewDrop2 texts.searchMenu model.dragDropData - flags + env.flags searchMenuCfg - settings + env.settings model.searchMenuModel ) ] diff --git a/modules/webapp/src/main/elm/Page/Search/Update.elm b/modules/webapp/src/main/elm/Page/Search/Update.elm index 73d3510e..3b1b5771 100644 --- a/modules/webapp/src/main/elm/Page/Search/Update.elm +++ b/modules/webapp/src/main/elm/Page/Search/Update.elm @@ -12,7 +12,6 @@ module Page.Search.Update exposing import Api import Api.Model.ItemLightList exposing (ItemLightList) -import Browser.Navigation as Nav import Comp.BookmarkQueryManage import Comp.ItemCardList import Comp.ItemDetail.FormChange exposing (FormChange(..)) @@ -23,19 +22,19 @@ import Comp.PowerSearchInput import Comp.PublishItems import Comp.SearchMenu import Data.AppEvent exposing (AppEvent(..)) +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.ItemArrange +import Data.ItemIds exposing (ItemIds) import Data.ItemQuery as Q -import Data.ItemSelection import Data.Items import Data.SearchMode exposing (SearchMode) -import Data.UiSettings exposing (UiSettings) import Messages.Page.Search exposing (Texts) import Page exposing (Page(..)) import Page.Search.Data exposing (..) import Process import Scroll -import Set exposing (Set) +import Set import Task import Util.Html exposing (KeyCode(..)) import Util.ItemDragDrop as DD @@ -47,56 +46,58 @@ type alias UpdateResult = , cmd : Cmd Msg , sub : Sub Msg , appEvent : AppEvent + , selectedItems : ItemIds } -update : Maybe String -> Maybe String -> Nav.Key -> Flags -> Texts -> UiSettings -> Msg -> Model -> UpdateResult -update bookmarkId mId key flags texts settings msg model = +update : Texts -> Maybe String -> Maybe String -> Env.Update -> Msg -> Model -> UpdateResult +update texts bookmarkId lastViewedItemId env msg model = case msg of Init -> let searchParam = - { flags = flags + { flags = env.flags , searchType = model.lastSearchType - , pageSize = settings.itemSearchPageSize + , pageSize = env.settings.itemSearchPageSize , offset = 0 , scroll = True + , selectedItems = env.selectedItems } setBookmark = Maybe.map (\bmId -> SearchMenuMsg <| Comp.SearchMenu.SetBookmark bmId) bookmarkId |> Maybe.withDefault DoNothing in - makeResult <| + makeResult env.selectedItems <| Util.Update.andThen3 - [ update bookmarkId mId key flags texts settings (SearchMenuMsg Comp.SearchMenu.Init) - , update bookmarkId mId key flags texts settings setBookmark - , doSearch searchParam + [ update texts bookmarkId lastViewedItemId env (SearchMenuMsg Comp.SearchMenu.Init) + , update texts bookmarkId lastViewedItemId env setBookmark + , doSearch env.selectedItems searchParam ] model DoNothing -> - UpdateResult model Cmd.none Sub.none AppNothing + UpdateResult model Cmd.none Sub.none AppNothing env.selectedItems ResetSearch -> let nm = { model | searchOffset = 0, powerSearchInput = Comp.PowerSearchInput.init } in - update bookmarkId mId key flags texts settings (SearchMenuMsg Comp.SearchMenu.ResetForm) nm + update texts bookmarkId lastViewedItemId env (SearchMenuMsg Comp.SearchMenu.ResetForm) nm SearchMenuMsg m -> let nextState = Comp.SearchMenu.updateDrop model.dragDropData.model - flags - settings + env.flags + env.settings m model.searchMenuModel dropCmd = - DD.makeUpdateCmd flags (\_ -> DoSearch model.lastSearchType) nextState.dragDrop.dropped + DD.makeUpdateCmd env.flags (\_ -> DoSearch model.lastSearchType) nextState.dragDrop.dropped newModel = { model @@ -110,12 +111,24 @@ update bookmarkId mId key flags texts settings msg model = BasicSearch } + newSelection = + Data.ItemIds.apply env.selectedItems nextState.selectionChange + + searchParam = + { flags = env.flags + , searchType = BasicSearch + , pageSize = env.settings.itemSearchPageSize + , offset = 0 + , scroll = False + , selectedItems = newSelection + } + result = if nextState.stateChange && not model.searchInProgress then - doSearch (SearchParam flags BasicSearch settings.itemSearchPageSize 0 False) newModel + doSearch env.selectedItems searchParam newModel else - resultModelCmd ( newModel, Cmd.none ) + resultModelCmd env.selectedItems ( newModel, Cmd.none ) in { result | cmd = @@ -125,21 +138,22 @@ update bookmarkId mId key flags texts settings msg model = , dropCmd ] , sub = Sub.map SearchMenuMsg nextState.sub + , selectedItems = newSelection } SetLinkTarget lt -> case linkTargetMsg lt of Just m -> - update bookmarkId mId key flags texts settings m model + update texts bookmarkId lastViewedItemId env m model Nothing -> - makeResult ( model, Cmd.none, Sub.none ) + makeResult env.selectedItems ( model, Cmd.none, Sub.none ) ItemCardListMsg m -> let result = Comp.ItemCardList.updateDrag model.dragDropData.model - flags + env.flags m model.itemListModel @@ -147,13 +161,8 @@ update bookmarkId mId key flags texts settings msg model = Maybe.map Util.Update.cmdUnit (linkTargetMsg result.linkTarget) |> Maybe.withDefault Cmd.none - nextView = - case ( model.viewMode, result.selection ) of - ( SelectView svm, Data.ItemSelection.Active ids ) -> - SelectView { svm | ids = ids } - - ( v, _ ) -> - v + itemIds = + Data.ItemIds.apply env.selectedItems result.selection itemRows = case result.toggleOpenRow of @@ -167,15 +176,17 @@ update bookmarkId mId key flags texts settings msg model = Nothing -> model.itemRowsOpen in - resultModelCmd - ( { model + { model = + { model | itemListModel = result.model - , viewMode = nextView , itemRowsOpen = itemRows , dragDropData = DD.DragDropData result.dragModel Nothing - } - , Cmd.batch [ Cmd.map ItemCardListMsg result.cmd, searchMsg ] - ) + } + , cmd = Cmd.batch [ Cmd.map ItemCardListMsg result.cmd, searchMsg ] + , sub = Sub.none + , appEvent = AppNothing + , selectedItems = itemIds + } ToggleExpandCollapseRows -> let @@ -186,12 +197,12 @@ update bookmarkId mId key flags texts settings msg model = else Set.empty in - resultModelCmd ( { model | itemRowsOpen = itemRows, viewMenuOpen = False }, Cmd.none ) + resultModelCmd env.selectedItems ( { model | itemRowsOpen = itemRows, viewMenuOpen = False }, Cmd.none ) ItemSearchResp scroll (Ok list) -> let noff = - settings.itemSearchPageSize + env.settings.itemSearchPageSize m = { model @@ -200,21 +211,21 @@ update bookmarkId mId key flags texts settings msg model = , moreAvailable = list.groups /= [] } in - makeResult <| + makeResult env.selectedItems <| Util.Update.andThen3 - [ update bookmarkId mId key flags texts settings (ItemCardListMsg (Comp.ItemCardList.SetResults list)) + [ update texts bookmarkId lastViewedItemId env (ItemCardListMsg (Comp.ItemCardList.SetResults list)) , if scroll then - scrollToCard mId + scrollToCard env.selectedItems lastViewedItemId else - \next -> makeResult ( next, Cmd.none, Sub.none ) + \next -> makeResult env.selectedItems ( next, Cmd.none, Sub.none ) ] m ItemSearchAddResp (Ok list) -> let noff = - model.searchOffset + settings.itemSearchPageSize + model.searchOffset + env.settings.itemSearchPageSize m = { model @@ -224,10 +235,10 @@ update bookmarkId mId key flags texts settings msg model = , moreAvailable = list.groups /= [] } in - update bookmarkId mId key flags texts settings (ItemCardListMsg (Comp.ItemCardList.AddResults list)) m + update texts bookmarkId lastViewedItemId env (ItemCardListMsg (Comp.ItemCardList.AddResults list)) m ItemSearchAddResp (Err _) -> - resultModelCmd + resultModelCmd env.selectedItems ( { model | moreInProgress = False } @@ -235,7 +246,7 @@ update bookmarkId mId key flags texts settings msg model = ) ItemSearchResp _ (Err _) -> - resultModelCmd + resultModelCmd env.selectedItems ( { model | searchInProgress = False } @@ -248,41 +259,43 @@ update bookmarkId mId key flags texts settings msg model = { model | searchOffset = 0 } param = - { flags = flags + { flags = env.flags , searchType = stype - , pageSize = settings.itemSearchPageSize + , pageSize = env.settings.itemSearchPageSize , offset = 0 , scroll = False + , selectedItems = env.selectedItems } in if model.searchInProgress then - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) else - doSearch param nm + doSearch env.selectedItems param nm RefreshView -> let param = - { flags = flags + { flags = env.flags , searchType = model.lastSearchType - , pageSize = settings.itemSearchPageSize + , pageSize = env.settings.itemSearchPageSize , offset = model.searchOffset , scroll = False + , selectedItems = env.selectedItems } in if model.searchInProgress then - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) else - doSearch param model + doSearch env.selectedItems param model ToggleSelectView -> let ( nextView, cmd ) = case model.viewMode of SearchView -> - ( SelectView <| initSelectViewModel flags, loadEditModel flags ) + ( SelectView <| initSelectViewModel env.flags, loadEditModel env.flags ) SelectView _ -> ( SearchView, Cmd.none ) @@ -290,92 +303,70 @@ update bookmarkId mId key flags texts settings msg model = PublishView q -> ( PublishView q, Cmd.none ) in - resultModelCmd - ( { model - | viewMode = nextView - } - , cmd - ) + resultModelCmd env.selectedItems ( { model | viewMode = nextView }, cmd ) LoadMore -> if model.moreAvailable then - doSearchMore flags settings model |> resultModelCmd + doSearchMore env model |> resultModelCmd env.selectedItems else - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) SetBasicSearch str -> let smMsg = SearchMenuMsg (Comp.SearchMenu.SetTextSearch str) in - update bookmarkId mId key flags texts settings smMsg model + update texts bookmarkId lastViewedItemId env smMsg model ToggleSearchType -> case model.searchTypeDropdownValue of BasicSearch -> - update bookmarkId mId key flags texts settings (SearchMenuMsg Comp.SearchMenu.SetFulltextSearch) model + update texts bookmarkId lastViewedItemId env (SearchMenuMsg Comp.SearchMenu.SetFulltextSearch) model ContentOnlySearch -> - update bookmarkId mId key flags texts settings (SearchMenuMsg Comp.SearchMenu.SetNamesSearch) model + update texts bookmarkId lastViewedItemId env (SearchMenuMsg Comp.SearchMenu.SetNamesSearch) model KeyUpSearchbarMsg (Just Enter) -> - update bookmarkId mId key flags texts settings (DoSearch model.searchTypeDropdownValue) model + update texts bookmarkId lastViewedItemId env (DoSearch model.searchTypeDropdownValue) model KeyUpSearchbarMsg _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) ScrollResult _ -> let cmd = Process.sleep 800 |> Task.perform (always ClearItemDetailId) in - resultModelCmd ( model, cmd ) + resultModelCmd env.selectedItems ( model, cmd ) ClearItemDetailId -> - resultModelCmd ( { model | scrollToCard = Nothing }, Cmd.none ) + resultModelCmd env.selectedItems ( { model | scrollToCard = Nothing }, Cmd.none ) SelectAllItems -> - case model.viewMode of - SelectView svm -> - let - visible = - Data.Items.idSet model.itemListModel.results + let + visible = + Data.Items.idSet model.itemListModel.results - svm_ = - { svm | ids = Set.union svm.ids visible } - in - resultModelCmd - ( { model | viewMode = SelectView svm_ } - , Cmd.none - ) + itemIds = + Data.ItemIds.apply env.selectedItems (Data.ItemIds.selectAll visible) - _ -> - resultModelCmd ( model, Cmd.none ) + res_ = + resultModelCmd env.selectedItems ( model, Cmd.none ) + in + { res_ | selectedItems = itemIds } SelectNoItems -> - case model.viewMode of - SelectView svm -> - let - svm_ = - { svm | ids = Set.empty } - in - resultModelCmd - ( { model | viewMode = SelectView svm_ } - , Cmd.none - ) - - _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd Data.ItemIds.empty ( model, Cmd.none ) DeleteSelectedConfirmed -> case model.viewMode of SelectView svm -> let cmd = - Api.deleteAllItems flags svm.ids DeleteAllResp + Api.deleteAllItems env.flags (Data.ItemIds.toList env.selectedItems) DeleteAllResp in - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView @@ -388,16 +379,16 @@ update bookmarkId mId key flags texts settings msg model = ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) RestoreSelectedConfirmed -> case model.viewMode of SelectView svm -> let cmd = - Api.restoreAllItems flags svm.ids DeleteAllResp + Api.restoreAllItems env.flags (Data.ItemIds.toList env.selectedItems) DeleteAllResp in - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView @@ -410,7 +401,7 @@ update bookmarkId mId key flags texts settings msg model = ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) DeleteAllResp (Ok res) -> if res.success then @@ -419,26 +410,27 @@ update bookmarkId mId key flags texts settings msg model = { model | viewMode = SearchView } param = - { flags = flags + { flags = env.flags , searchType = model.lastSearchType - , pageSize = settings.itemSearchPageSize + , pageSize = env.settings.itemSearchPageSize , offset = 0 , scroll = False + , selectedItems = env.selectedItems } in - doSearch param nm + doSearch env.selectedItems param nm else - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) DeleteAllResp (Err _) -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) RequestReprocessSelected -> case model.viewMode of SelectView svm -> - if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) + if Data.ItemIds.isEmpty env.selectedItems then + resultModelCmd env.selectedItems ( model, Cmd.none ) else let @@ -452,15 +444,15 @@ update bookmarkId mId key flags texts settings msg model = } } in - resultModelCmd ( model_, Cmd.none ) + resultModelCmd env.selectedItems ( model_, Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) CloseConfirmModal -> case model.viewMode of SelectView svm -> - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView { svm | confirmModal = Nothing, action = NoneAction } } @@ -468,20 +460,20 @@ update bookmarkId mId key flags texts settings msg model = ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) ReprocessSelectedConfirmed -> case model.viewMode of SelectView svm -> - if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) + if Data.ItemIds.isEmpty env.selectedItems then + resultModelCmd env.selectedItems ( model, Cmd.none ) else let cmd = - Api.reprocessMultiple flags svm.ids DeleteAllResp + Api.reprocessMultiple env.flags (Data.ItemIds.toList env.selectedItems) DeleteAllResp in - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView @@ -494,13 +486,13 @@ update bookmarkId mId key flags texts settings msg model = ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) RequestDeleteSelected -> case model.viewMode of SelectView svm -> - if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) + if Data.ItemIds.isEmpty env.selectedItems then + resultModelCmd env.selectedItems ( model, Cmd.none ) else let @@ -514,16 +506,16 @@ update bookmarkId mId key flags texts settings msg model = } } in - resultModelCmd ( model_, Cmd.none ) + resultModelCmd env.selectedItems ( model_, Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) RequestRestoreSelected -> case model.viewMode of SelectView svm -> - if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) + if Data.ItemIds.isEmpty env.selectedItems then + resultModelCmd env.selectedItems ( model, Cmd.none ) else let @@ -537,37 +529,37 @@ update bookmarkId mId key flags texts settings msg model = } } in - resultModelCmd ( model_, Cmd.none ) + resultModelCmd env.selectedItems ( model_, Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) EditSelectedItems -> case model.viewMode of SelectView svm -> if svm.action == EditSelected then - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView { svm | action = NoneAction } } , Cmd.none ) - else if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) + else if Data.ItemIds.isEmpty env.selectedItems then + resultModelCmd env.selectedItems ( model, Cmd.none ) else - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView { svm | action = EditSelected } } , Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) MergeSelectedItems -> case model.viewMode of SelectView svm -> if svm.action == MergeSelected then - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView @@ -579,38 +571,37 @@ update bookmarkId mId key flags texts settings msg model = , Cmd.none ) - else if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) - else - let - ( mm, mc ) = - Comp.ItemMerge.initQuery - flags - model.searchMenuModel.searchMode - (Q.ItemIdIn (Set.toList svm.ids)) - in - resultModelCmd - ( { model - | viewMode = - SelectView - { svm - | action = MergeSelected - , mergeModel = mm - } - } - , Cmd.map MergeItemsMsg mc - ) + case Data.ItemIds.toQuery env.selectedItems of + Just q -> + let + ( mm, mc ) = + Comp.ItemMerge.initQuery env.flags model.searchMenuModel.searchMode q + in + resultModelCmd env.selectedItems + ( { model + | viewMode = + SelectView + { svm + | action = MergeSelected + , mergeModel = mm + } + } + , Cmd.map MergeItemsMsg mc + ) + + Nothing -> + resultModelCmd env.selectedItems ( model, Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) MergeItemsMsg lmsg -> case model.viewMode of SelectView svm -> let result = - Comp.ItemMerge.update flags lmsg svm.mergeModel + Comp.ItemMerge.update env.flags lmsg svm.mergeModel nextView = case result.outcome of @@ -627,23 +618,21 @@ update bookmarkId mId key flags texts settings msg model = { model | viewMode = nextView } in if result.outcome == Comp.ItemMerge.OutcomeMerged then - update bookmarkId - mId - key - flags - texts - settings + update texts + bookmarkId + lastViewedItemId + env (DoSearch model.searchTypeDropdownValue) model_ else - resultModelCmd + resultModelCmd env.selectedItems ( model_ , Cmd.map MergeItemsMsg result.cmd ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) PublishSelectedItems -> case model.viewMode of @@ -651,9 +640,9 @@ update bookmarkId mId key flags texts settings msg model = if svm.action == PublishSelected then let ( mm, mc ) = - Comp.PublishItems.init flags + Comp.PublishItems.init env.flags in - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SelectView @@ -665,36 +654,37 @@ update bookmarkId mId key flags texts settings msg model = , Cmd.map PublishItemsMsg mc ) - else if svm.ids == Set.empty then - resultModelCmd ( model, Cmd.none ) - else - let - ( mm, mc ) = - Comp.PublishItems.initQuery flags - (Q.ItemIdIn (Set.toList svm.ids)) - in - resultModelCmd - ( { model - | viewMode = - SelectView - { svm - | action = PublishSelected - , publishModel = mm - } - } - , Cmd.map PublishItemsMsg mc - ) + case Data.ItemIds.toQuery env.selectedItems of + Just q -> + let + ( mm, mc ) = + Comp.PublishItems.initQuery env.flags q + in + resultModelCmd env.selectedItems + ( { model + | viewMode = + SelectView + { svm + | action = PublishSelected + , publishModel = mm + } + } + , Cmd.map PublishItemsMsg mc + ) + + Nothing -> + resultModelCmd env.selectedItems ( model, Cmd.none ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) PublishItemsMsg lmsg -> case model.viewMode of SelectView svm -> let result = - Comp.PublishItems.update texts.publishItems flags lmsg svm.publishModel + Comp.PublishItems.update texts.publishItems env.flags lmsg svm.publishModel nextView = case result.outcome of @@ -708,30 +698,28 @@ update bookmarkId mId key flags texts settings msg model = { model | viewMode = nextView } in if result.outcome == Comp.PublishItems.OutcomeDone then - update bookmarkId - mId - key - flags - texts - settings + update texts + bookmarkId + lastViewedItemId + env (DoSearch model.searchTypeDropdownValue) model_ else - resultModelCmd + resultModelCmd env.selectedItems ( model_ , Cmd.map PublishItemsMsg result.cmd ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) EditMenuMsg lmsg -> case model.viewMode of SelectView svm -> let res = - Comp.ItemDetail.MultiEditMenu.update flags lmsg svm.editModel + Comp.ItemDetail.MultiEditMenu.update env.flags lmsg svm.editModel svm_ = { svm @@ -762,19 +750,19 @@ update bookmarkId mId key flags texts settings msg model = Sub.map EditMenuMsg res.sub upCmd = - Comp.ItemDetail.FormChange.multiUpdate flags - svm.ids + Comp.ItemDetail.FormChange.multiUpdate env.flags + env.selectedItems res.change (MultiUpdateResp res.change) in - makeResult + makeResult env.selectedItems ( { model | viewMode = SelectView svm_ } , Cmd.batch [ cmd_, upCmd ] , sub_ ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) MultiUpdateResp change (Ok res) -> let @@ -782,29 +770,24 @@ update bookmarkId mId key flags texts settings msg model = updateSelectViewNameState res.success model change in if res.success then - case model.viewMode of - SelectView svm -> - -- replace changed items in the view - resultModelCmd ( nm, loadChangedItems flags model.searchMenuModel.searchMode svm.ids ) - - _ -> - resultModelCmd ( nm, Cmd.none ) + -- replace changed items in the view + resultModelCmd env.selectedItems ( nm, loadChangedItems env.flags model.searchMenuModel.searchMode env.selectedItems ) else - resultModelCmd ( nm, Cmd.none ) + resultModelCmd env.selectedItems ( nm, Cmd.none ) MultiUpdateResp change (Err _) -> - makeResult + makeResult env.selectedItems ( updateSelectViewNameState False model change , Cmd.none , Sub.none ) ReplaceChangedItemsResp (Ok items) -> - resultModelCmd ( replaceItems model items, Cmd.none ) + resultModelCmd env.selectedItems ( replaceItems model items, Cmd.none ) ReplaceChangedItemsResp (Err _) -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) UiSettingsUpdated -> let @@ -822,7 +805,7 @@ update bookmarkId mId key flags texts settings msg model = model_ = { model | viewMode = viewMode } in - update bookmarkId mId key flags texts settings (DoSearch model.lastSearchType) model_ + update texts bookmarkId lastViewedItemId env (DoSearch model.lastSearchType) model_ SearchStatsResp result -> let @@ -832,17 +815,17 @@ update bookmarkId mId key flags texts settings msg model = stats = Result.withDefault model.searchStats result in - update bookmarkId mId key flags texts settings lm { model | searchStats = stats } + update texts bookmarkId lastViewedItemId env lm { model | searchStats = stats } TogglePreviewFullWidth -> let newSettings s = - { s | cardPreviewFullWidth = Just (not settings.cardPreviewFullWidth) } + { s | cardPreviewFullWidth = Just (not env.settings.cardPreviewFullWidth) } cmd = - Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp + Api.saveUserClientSettingsBy env.flags newSettings ClientSettingsSaveResp in - resultModelCmd ( { model | viewMenuOpen = False }, cmd ) + resultModelCmd env.selectedItems ( { model | viewMenuOpen = False }, cmd ) ClientSettingsSaveResp (Ok res) -> if res.success then @@ -850,13 +833,14 @@ update bookmarkId mId key flags texts settings msg model = , cmd = Cmd.none , sub = Sub.none , appEvent = AppReloadUiSettings + , selectedItems = env.selectedItems } else - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) ClientSettingsSaveResp (Err _) -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) PowerSearchMsg lm -> let @@ -871,58 +855,61 @@ update bookmarkId mId key flags texts settings msg model = in case result.action of Comp.PowerSearchInput.NoAction -> - makeResult ( model_, cmd_, Sub.map PowerSearchMsg result.subs ) + makeResult env.selectedItems ( model_, cmd_, Sub.map PowerSearchMsg result.subs ) Comp.PowerSearchInput.SubmitSearch -> - update bookmarkId mId key flags texts settings (DoSearch model_.searchTypeDropdownValue) model_ + update texts bookmarkId lastViewedItemId env (DoSearch model_.searchTypeDropdownValue) model_ KeyUpPowerSearchbarMsg (Just Enter) -> - update bookmarkId mId key flags texts settings (DoSearch model.searchTypeDropdownValue) model + update texts bookmarkId lastViewedItemId env (DoSearch model.searchTypeDropdownValue) model KeyUpPowerSearchbarMsg _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) RemoveItem id -> - update bookmarkId mId key flags texts settings (ItemCardListMsg (Comp.ItemCardList.RemoveItem id)) model + update texts bookmarkId lastViewedItemId env (ItemCardListMsg (Comp.ItemCardList.RemoveItem id)) model TogglePublishCurrentQueryView -> - case createQuery model of + case createQuery env.selectedItems model of Just q -> let ( pm, pc ) = - Comp.PublishItems.initQuery flags q + Comp.PublishItems.initQuery env.flags q in - resultModelCmd ( { model | viewMode = PublishView pm, viewMenuOpen = False }, Cmd.map PublishViewMsg pc ) + resultModelCmd env.selectedItems + ( { model | viewMode = PublishView pm, viewMenuOpen = False } + , Cmd.map PublishViewMsg pc + ) Nothing -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) ToggleBookmarkCurrentQueryView -> - case createQuery model of + case createQuery env.selectedItems model of Just q -> case model.topWidgetModel of BookmarkQuery _ -> - resultModelCmd ( { model | topWidgetModel = TopWidgetHidden, viewMenuOpen = False }, Cmd.none ) + resultModelCmd env.selectedItems ( { model | topWidgetModel = TopWidgetHidden, viewMenuOpen = False }, Cmd.none ) TopWidgetHidden -> let ( qm, qc ) = Comp.BookmarkQueryManage.init (Q.render q) in - resultModelCmd + resultModelCmd env.selectedItems ( { model | topWidgetModel = BookmarkQuery qm, viewMenuOpen = False } , Cmd.map BookmarkQueryMsg qc ) Nothing -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) BookmarkQueryMsg lm -> case model.topWidgetModel of BookmarkQuery bm -> let res = - Comp.BookmarkQueryManage.update flags lm bm + Comp.BookmarkQueryManage.update env.flags lm bm nextModel = if @@ -938,12 +925,12 @@ update bookmarkId mId key flags texts settings msg model = refreshCmd = if res.outcome == Comp.BookmarkQueryManage.Done then - Cmd.map SearchMenuMsg (Comp.SearchMenu.refreshBookmarks flags) + Cmd.map SearchMenuMsg (Comp.SearchMenu.refreshBookmarks env.flags) else Cmd.none in - makeResult + makeResult env.selectedItems ( { model | topWidgetModel = nextModel } , Cmd.batch [ Cmd.map BookmarkQueryMsg res.cmd @@ -953,43 +940,43 @@ update bookmarkId mId key flags texts settings msg model = ) TopWidgetHidden -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) PublishViewMsg lmsg -> case model.viewMode of PublishView inPM -> let result = - Comp.PublishItems.update texts.publishItems flags lmsg inPM + Comp.PublishItems.update texts.publishItems env.flags lmsg inPM in case result.outcome of Comp.PublishItems.OutcomeInProgress -> - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = PublishView result.model } , Cmd.map PublishViewMsg result.cmd ) Comp.PublishItems.OutcomeDone -> - resultModelCmd + resultModelCmd env.selectedItems ( { model | viewMode = SearchView } - , Cmd.map SearchMenuMsg (Comp.SearchMenu.refreshBookmarks flags) + , Cmd.map SearchMenuMsg (Comp.SearchMenu.refreshBookmarks env.flags) ) _ -> - resultModelCmd ( model, Cmd.none ) + resultModelCmd env.selectedItems ( model, Cmd.none ) ToggleViewMenu -> - resultModelCmd ( { model | viewMenuOpen = not model.viewMenuOpen }, Cmd.none ) + resultModelCmd env.selectedItems ( { model | viewMenuOpen = not model.viewMenuOpen }, Cmd.none ) ToggleShowGroups -> let newSettings s = - { s | itemSearchShowGroups = Just (not settings.itemSearchShowGroups) } + { s | itemSearchShowGroups = Just (not env.settings.itemSearchShowGroups) } cmd = - Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp + Api.saveUserClientSettingsBy env.flags newSettings ClientSettingsSaveResp in - resultModelCmd ( { model | viewMenuOpen = False }, cmd ) + resultModelCmd env.selectedItems ( { model | viewMenuOpen = False }, cmd ) ToggleArrange am -> let @@ -997,9 +984,21 @@ update bookmarkId mId key flags texts settings msg model = { s | itemSearchArrange = Data.ItemArrange.asString am |> Just } cmd = - Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp + Api.saveUserClientSettingsBy env.flags newSettings ClientSettingsSaveResp in - resultModelCmd ( { model | viewMenuOpen = False }, cmd ) + resultModelCmd env.selectedItems ( { model | viewMenuOpen = False }, cmd ) + + ItemSelectionChanged -> + if Data.ItemIds.isEmpty env.selectedItems then + update texts + bookmarkId + lastViewedItemId + env + (SearchMenuMsg <| Comp.SearchMenu.setIncludeSelection False) + model + + else + resultModelCmd env.selectedItems ( model, Cmd.none ) @@ -1063,37 +1062,37 @@ replaceItems model newItems = { model | itemListModel = newList } -loadChangedItems : Flags -> SearchMode -> Set String -> Cmd Msg +loadChangedItems : Flags -> SearchMode -> ItemIds -> Cmd Msg loadChangedItems flags smode ids = - if Set.isEmpty ids then + if Data.ItemIds.isEmpty ids then Cmd.none else let idList = - Set.toList ids + Data.ItemIds.toList ids searchInit = Q.request smode (Just <| Q.ItemIdIn idList) search = { searchInit - | limit = Just <| Set.size ids + | limit = Just <| Data.ItemIds.size ids } in Api.itemSearch flags search ReplaceChangedItemsResp -scrollToCard : Maybe String -> Model -> UpdateResult -scrollToCard mId model = +scrollToCard : ItemIds -> Maybe String -> Model -> UpdateResult +scrollToCard selection lastViewedItemId model = let scroll id = Scroll.scrollElementY "item-card-list" id 0.5 0.5 in - makeResult <| - case mId of + makeResult selection <| + case lastViewedItemId of Just id -> - ( { model | scrollToCard = mId } + ( { model | scrollToCard = lastViewedItemId } , Task.attempt ScrollResult (scroll id) , Sub.none ) @@ -1107,8 +1106,8 @@ loadEditModel flags = Cmd.map EditMenuMsg (Comp.ItemDetail.MultiEditMenu.loadModel flags) -doSearch : SearchParam -> Model -> UpdateResult -doSearch param model = +doSearch : ItemIds -> SearchParam -> Model -> UpdateResult +doSearch selection param model = let param_ = { param | offset = 0 } @@ -1116,7 +1115,7 @@ doSearch param model = searchCmd = doSearchCmd param_ model in - resultModelCmd + resultModelCmd selection ( { model | searchInProgress = True , searchOffset = 0 @@ -1131,15 +1130,16 @@ linkTargetMsg linkTarget = Maybe.map SearchMenuMsg (Comp.SearchMenu.linkTargetMsg linkTarget) -doSearchMore : Flags -> UiSettings -> Model -> ( Model, Cmd Msg ) -doSearchMore flags settings model = +doSearchMore : Env.Update -> Model -> ( Model, Cmd Msg ) +doSearchMore env model = let param = - { flags = flags + { flags = env.flags , searchType = model.lastSearchType - , pageSize = settings.itemSearchPageSize + , pageSize = env.settings.itemSearchPageSize , offset = model.searchOffset , scroll = False + , selectedItems = env.selectedItems } cmd = @@ -1150,15 +1150,16 @@ doSearchMore flags settings model = ) -resultModelCmd : ( Model, Cmd Msg ) -> UpdateResult -resultModelCmd ( m, c ) = - makeResult ( m, c, Sub.none ) +resultModelCmd : ItemIds -> ( Model, Cmd Msg ) -> UpdateResult +resultModelCmd selection ( m, c ) = + makeResult selection ( m, c, Sub.none ) -makeResult : ( Model, Cmd Msg, Sub Msg ) -> UpdateResult -makeResult ( m, c, s ) = +makeResult : ItemIds -> ( Model, Cmd Msg, Sub Msg ) -> UpdateResult +makeResult selection ( m, c, s ) = { model = m , cmd = c , sub = s , appEvent = AppNothing + , selectedItems = selection } diff --git a/modules/webapp/src/main/elm/Page/Search/View2.elm b/modules/webapp/src/main/elm/Page/Search/View2.elm index 34ecd498..30461f18 100644 --- a/modules/webapp/src/main/elm/Page/Search/View2.elm +++ b/modules/webapp/src/main/elm/Page/Search/View2.elm @@ -18,9 +18,11 @@ import Comp.PowerSearchInput import Comp.PublishItems import Comp.SearchMenu import Comp.SearchStatsView +import Data.Environment as Env import Data.Flags exposing (Flags) import Data.Icons as Icons import Data.ItemArrange +import Data.ItemIds exposing (ItemIds) import Data.ItemSelection import Data.SearchMode import Data.UiSettings exposing (UiSettings) @@ -36,27 +38,27 @@ import Styles as S import Util.Html -viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg -viewSidebar texts visible flags settings model = +viewSidebar : Texts -> Env.View -> Model -> Html Msg +viewSidebar texts env model = div [ id "sidebar" , class S.sidebar , class S.sidebarBg - , classList [ ( "hidden", not visible ) ] + , classList [ ( "hidden", not env.sidebarVisible ) ] ] - [ Page.Search.SideMenu.view texts.sideMenu flags settings model + [ Page.Search.SideMenu.view texts.sideMenu env model ] -viewContent : Texts -> Flags -> UiSettings -> Model -> Html Msg -viewContent texts flags settings model = +viewContent : Texts -> Env.View -> Model -> Html Msg +viewContent texts env model = div [ id "item-card-list" -- this id is used in scroll-to-card , class S.content ] - (searchStats texts flags settings model - ++ itemsBar texts flags settings model - ++ mainView texts flags settings model + (searchStats texts env.flags env.settings model + ++ itemsBar texts env model + ++ mainView texts env model ++ confirmModal texts model ) @@ -65,8 +67,8 @@ viewContent texts flags settings model = --- Helpers -mainView : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) -mainView texts flags settings model = +mainView : Texts -> Env.View -> Model -> List (Html Msg) +mainView texts env model = let otherView = case model.viewMode of @@ -75,13 +77,13 @@ mainView texts flags settings model = MergeSelected -> Just [ div [ class "sm:relative mb-2" ] - (itemMergeView texts settings svm) + (itemMergeView texts env.settings svm) ] PublishSelected -> Just [ div [ class "sm:relative mb-2" ] - (itemPublishView texts settings flags svm) + (itemPublishView texts env.settings env.flags svm) ] _ -> @@ -90,7 +92,7 @@ mainView texts flags settings model = PublishView pm -> Just [ div [ class "sm:relative mb-2" ] - (publishResults texts settings flags model pm) + (publishResults texts env.settings env.flags model pm) ] SearchView -> @@ -101,8 +103,8 @@ mainView texts flags settings model = body Nothing -> - bookmarkQueryWidget texts settings flags model - ++ itemCardList texts flags settings model + bookmarkQueryWidget texts env.settings env.flags model + ++ itemCardList texts env model bookmarkQueryWidget : Texts -> UiSettings -> Flags -> Model -> List (Html Msg) @@ -182,21 +184,21 @@ confirmModal texts model = [] -itemsBar : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) -itemsBar texts flags settings model = +itemsBar : Texts -> Env.View -> Model -> List (Html Msg) +itemsBar texts env model = case model.viewMode of SearchView -> - [ defaultMenuBar texts flags settings model ] + [ defaultMenuBar texts env model ] SelectView svm -> - [ editMenuBar texts model svm ] + [ editMenuBar texts model env.selectedItems svm ] PublishView _ -> - [ defaultMenuBar texts flags settings model ] + [ defaultMenuBar texts env model ] -defaultMenuBar : Texts -> Flags -> UiSettings -> Model -> Html Msg -defaultMenuBar texts flags settings model = +defaultMenuBar : Texts -> Env.View -> Model -> Html Msg +defaultMenuBar texts env model = let btnStyle = S.secondaryBasicButton ++ " text-sm" @@ -224,7 +226,7 @@ defaultMenuBar texts flags settings model = |> Maybe.withDefault (value "") , class (String.replace "rounded" "" S.textInput) , class "py-2 text-sm" - , if flags.config.fullTextSearchEnabled then + , if env.flags.config.fullTextSearchEnabled then class " border-r-0 rounded-l" else @@ -235,7 +237,7 @@ defaultMenuBar texts flags settings model = [ class S.secondaryBasicButtonPlain , class "text-sm px-4 py-2 border rounded-r" , classList - [ ( "hidden", not flags.config.fullTextSearchEnabled ) + [ ( "hidden", not env.flags.config.fullTextSearchEnabled ) ] , href "#" , onClick ToggleSearchType @@ -258,10 +260,10 @@ defaultMenuBar texts flags settings model = ] isCardView = - settings.itemSearchArrange == Data.ItemArrange.Cards + env.settings.itemSearchArrange == Data.ItemArrange.Cards isListView = - settings.itemSearchArrange == Data.ItemArrange.List + env.settings.itemSearchArrange == Data.ItemArrange.List menuSep = { icon = i [] [] @@ -274,7 +276,7 @@ defaultMenuBar texts flags settings model = MB.view { start = [ MB.CustomElement <| - if settings.powerSearchEnabled then + if env.settings.powerSearchEnabled then powerSearchBar else @@ -314,7 +316,7 @@ defaultMenuBar texts flags settings model = , menuOpen = model.viewMenuOpen , items = [ { icon = - if settings.itemSearchShowGroups then + if env.settings.itemSearchShowGroups then i [ class "fa fa-check-square font-thin" ] [] else @@ -368,16 +370,16 @@ defaultMenuBar texts flags settings model = , menuSep , { label = texts.shareResults , icon = Icons.shareIcon "" - , disabled = createQuery model == Nothing + , disabled = createQuery env.selectedItems model == Nothing , attrs = [ title <| - if createQuery model == Nothing then + if createQuery env.selectedItems model == Nothing then texts.nothingSelectedToShare else texts.publishCurrentQueryTitle , href "#" - , if createQuery model == Nothing then + , if createQuery env.selectedItems model == Nothing then class "" else @@ -386,16 +388,16 @@ defaultMenuBar texts flags settings model = } , { label = texts.bookmarkQuery , icon = i [ class "fa fa-bookmark" ] [] - , disabled = createQuery model == Nothing + , disabled = createQuery env.selectedItems model == Nothing , attrs = [ title <| - if createQuery model == Nothing then + if createQuery env.selectedItems model == Nothing then texts.nothingToBookmark else texts.bookmarkQuery , href "#" - , if createQuery model == Nothing then + , if createQuery env.selectedItems model == Nothing then class "" else @@ -403,7 +405,7 @@ defaultMenuBar texts flags settings model = ] } , { label = - if settings.cardPreviewFullWidth then + if env.settings.cardPreviewFullWidth then texts.fullHeightPreviewTitle else @@ -415,7 +417,7 @@ defaultMenuBar texts flags settings model = , onClick TogglePreviewFullWidth , classList [ ( "hidden sm:inline-block", False ) - , ( "bg-gray-200 dark:bg-slate-600", settings.cardPreviewFullWidth ) + , ( "bg-gray-200 dark:bg-slate-600", env.settings.cardPreviewFullWidth ) ] ] } @@ -427,11 +429,11 @@ defaultMenuBar texts flags settings model = } -editMenuBar : Texts -> Model -> SelectViewModel -> Html Msg -editMenuBar texts model svm = +editMenuBar : Texts -> Model -> ItemIds -> SelectViewModel -> Html Msg +editMenuBar texts model selectedItems svm = let selectCount = - Set.size svm.ids + Data.ItemIds.size selectedItems btnStyle = S.secondaryBasicButton ++ " text-sm" @@ -565,8 +567,8 @@ searchStats texts _ settings model = [] -itemCardList : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) -itemCardList texts flags settings model = +itemCardList : Texts -> Env.View -> Model -> List (Html Msg) +itemCardList texts env model = let previewUrl attach = Api.attachmentPreviewURL attach.id @@ -581,15 +583,15 @@ itemCardList texts flags settings model = , previewUrlFallback = previewUrlFallback , attachUrl = .id >> Api.fileURL , detailPage = .id >> ItemDetailPage - , arrange = settings.itemSearchArrange - , showGroups = settings.itemSearchShowGroups + , arrange = env.settings.itemSearchArrange + , showGroups = env.settings.itemSearchShowGroups , rowOpen = \id -> Set.member "all" model.itemRowsOpen || Set.member id model.itemRowsOpen } itemViewCfg = case model.viewMode of - SelectView svm -> - viewCfg (Data.ItemSelection.Active svm.ids) + SelectView _ -> + viewCfg (Data.ItemSelection.Active env.selectedItems) _ -> viewCfg Data.ItemSelection.Inactive @@ -597,11 +599,11 @@ itemCardList texts flags settings model = [ Html.map ItemCardListMsg (Comp.ItemCardList.view texts.itemCardList itemViewCfg - settings - flags + env.settings + env.flags model.itemListModel ) - , loadMore texts settings model + , loadMore texts env.settings model ] diff --git a/modules/webapp/src/main/elm/Page/Share/Sidebar.elm b/modules/webapp/src/main/elm/Page/Share/Sidebar.elm index 8fbcef90..c9e48f2f 100644 --- a/modules/webapp/src/main/elm/Page/Share/Sidebar.elm +++ b/modules/webapp/src/main/elm/Page/Share/Sidebar.elm @@ -10,6 +10,7 @@ module Page.Share.Sidebar exposing (..) import Comp.SearchMenu import Comp.Tabs import Data.Flags exposing (Flags) +import Data.ItemIds import Html exposing (..) import Html.Attributes exposing (..) import Messages.Page.Share exposing (Texts) @@ -30,6 +31,7 @@ view texts flags model = searchMenuCfg = { overrideTabLook = hideTrashTab + , selectedItems = Data.ItemIds.empty } in div diff --git a/modules/webapp/src/main/elm/Page/Share/Update.elm b/modules/webapp/src/main/elm/Page/Share/Update.elm index 85f8008c..0fa94402 100644 --- a/modules/webapp/src/main/elm/Page/Share/Update.elm +++ b/modules/webapp/src/main/elm/Page/Share/Update.elm @@ -14,6 +14,7 @@ import Comp.PowerSearchInput import Comp.SearchMenu import Comp.SharePasswordForm import Data.Flags exposing (Flags) +import Data.ItemIds import Data.ItemQuery as Q import Data.SearchMode import Data.UiSettings exposing (UiSettings) @@ -262,7 +263,7 @@ makeSearchCmd flags doInit model = let xq = Q.and - [ Comp.SearchMenu.getItemQuery model.searchMenuModel + [ Comp.SearchMenu.getItemQuery Data.ItemIds.empty model.searchMenuModel , Maybe.map Q.Fragment <| case model.searchMode of SearchBarNormal -> diff --git a/modules/webapp/src/main/elm/Styles.elm b/modules/webapp/src/main/elm/Styles.elm index 88fd0438..894526c2 100644 --- a/modules/webapp/src/main/elm/Styles.elm +++ b/modules/webapp/src/main/elm/Styles.elm @@ -189,6 +189,26 @@ secondaryButtonHover = " hover:bg-gray-400 dark:hover:bg-slate-300 " + +--- Secondary Basic Button + + +secondaryBasicButtonNoColor : String +secondaryBasicButtonNoColor = + " my-auto whitespace-nowrap text-center shadow-none focus:outline-none focus:ring focus:ring-opacity-75 " + + +secondaryBasicButtonMain : String +secondaryBasicButtonMain = + secondaryBasicButtonNoColor + ++ " border-gray-500 dark:border-slate-500 text-gray-500 dark:text-slate-400 " + + +secondaryBasicButtonHover : String +secondaryBasicButtonHover = + " hover:bg-gray-600 hover:text-white dark:hover:text-white dark:hover:bg-slate-500 dark:hover:text-slate-100 " + + secondaryBasicButton : String secondaryBasicButton = secondaryBasicButtonRounded ++ secondaryBasicButtonPlain @@ -205,20 +225,6 @@ secondaryBasicButtonRounded = ---- Secondary Basic Button - - -secondaryBasicButtonMain : String -secondaryBasicButtonMain = - " my-auto whitespace-nowrap border-gray-500 dark:border-slate-500 text-gray-500 dark:text-slate-400 text-center shadow-none focus:outline-none focus:ring focus:ring-opacity-75 " - - -secondaryBasicButtonHover : String -secondaryBasicButtonHover = - " hover:bg-gray-600 hover:text-white dark:hover:text-white dark:hover:bg-slate-500 dark:hover:text-slate-100 " - - - --- Delete Button @@ -248,6 +254,18 @@ deleteLabel = +--- Green Button + + +greenButton : String +greenButton = + secondaryBasicButtonNoColor + ++ secondaryBasicButtonRounded + ++ " dark:bg-lime-600 dark:bg-opacity-30 dark:border-lime-600 dark:text-lime-400 " + ++ " bg-lime-600 border-lime-600 text-white" + + + --- Others