From e48dd8b3089b7e5ec1624212bb06d0d461cb21e7 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Tue, 13 Apr 2021 22:07:12 +0530 Subject: [PATCH] chore: applies elm-format on all files --- package-lock.json | 1096 ++++------------------------- src/elm/Api.elm | 151 ++-- src/elm/Api/Input.elm | 43 +- src/elm/Api/Output.elm | 16 +- src/elm/Data.elm | 31 +- src/elm/Data/Comment.elm | 43 +- src/elm/Data/Cuid.elm | 3 +- src/elm/Data/RemoteUser.elm | 49 +- src/elm/Data/SimpleWebData.elm | 17 +- src/elm/ErrorReporting.elm | 32 +- src/elm/Main.elm | 102 +-- src/elm/Model.elm | 268 +++---- src/elm/UI/AppShell.elm | 8 +- src/elm/UI/AuthenticationInfo.elm | 57 +- src/elm/UI/Comment.elm | 172 +++-- src/elm/UI/TextArea.elm | 52 +- src/elm/Utils.elm | 77 +- 17 files changed, 732 insertions(+), 1485 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08d6c27..35ada81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,32 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -61,12 +35,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==" }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -316,16 +284,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", @@ -395,15 +353,6 @@ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "dev": true }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - } - }, "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", @@ -557,12 +506,6 @@ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -1084,12 +1027,6 @@ "get-intrinsic": "^1.0.0" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, "camel-case": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", @@ -1438,37 +1375,12 @@ "source-map": "~0.6.0" } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, "cli-boxes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, "clipboardy": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz", @@ -1782,19 +1694,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -1915,12 +1814,6 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -2637,15 +2530,6 @@ "prr": "~1.0.1" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", @@ -2993,15 +2877,6 @@ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -3730,12 +3605,6 @@ } } }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4163,12 +4032,6 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" }, - "husky": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz", - "integrity": "sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==", - "dev": true - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4188,24 +4051,6 @@ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -4221,12 +4066,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", @@ -4315,12 +4154,6 @@ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", "dev": true }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -4397,12 +4230,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-glob": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", @@ -4434,12 +4261,6 @@ } } }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -4481,12 +4302,6 @@ "has": "^1.0.1" } }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -4538,12 +4353,6 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -4555,12 +4364,6 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4620,581 +4423,137 @@ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, - "lint-staged": { - "version": "10.5.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", - "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", - "dev": true, + "loglevel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", + "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==", + "dev": true + }, + "lower-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", + "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", "requires": { - "chalk": "^4.1.0", - "cli-truncate": "^2.1.0", - "commander": "^6.2.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.2.0", - "dedent": "^0.7.0", - "enquirer": "^2.3.6", - "execa": "^4.1.0", - "listr2": "^3.2.2", - "log-symbols": "^4.0.0", - "micromatch": "^4.0.2", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "^3.3.0" + "tslib": "^1.10.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "listr2": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.4.3.tgz", - "integrity": "sha512-wZmkzNiuinOfwrGqAwTCcPw6aKQGTAMGXwG5xeU1WpDjJNeBA35jGBeWxR3OF+R6Yl5Y3dRG+3vE8t6PDcSNHA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "cli-truncate": "^2.1.0", - "figures": "^3.2.0", - "indent-string": "^4.0.0", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^6.6.6", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "loglevel": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", - "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==", - "dev": true - }, - "lower-case": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", - "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", - "requires": { - "tslib": "^1.10.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.1.0" } } } @@ -5922,15 +5281,6 @@ "tslib": "^1.10.0" } }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, "parse-asn1": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", @@ -5943,18 +5293,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -6030,12 +5368,6 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", @@ -6097,15 +5429,6 @@ } } }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -6620,16 +5943,6 @@ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -6666,15 +5979,6 @@ "aproba": "^1.1.1" } }, - "rxjs": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", - "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6723,12 +6027,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -6995,43 +6293,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -7468,46 +6729,6 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "string.prototype.trimend": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", @@ -7639,17 +6860,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -7983,12 +7193,6 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -8818,58 +8022,6 @@ "errno": "~0.1.7" } }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8900,12 +8052,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true - }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/src/elm/Api.elm b/src/elm/Api.elm index af3ba9d..c6f440a 100644 --- a/src/elm/Api.elm +++ b/src/elm/Api.elm @@ -2,15 +2,15 @@ module Api exposing ( ApiClient , ApiRequestOutcome , getApiClient - , reportError + , reportError ) +import Api.Input as Input import Api.Output as Output import Data exposing (ApiToken(..), Interactions, User(..), UserInfo, UserInfoWithToken, VoteAction(..)) import Data.Comment exposing (Comment, CommentTree) import Data.Cuid exposing (Cuid) import Http exposing (Body, Header) -import Api.Input as Input import Json.Decode as D exposing (Decoder) import Json.Encode as E import Task exposing (Task) @@ -28,7 +28,7 @@ type alias Api = type alias ApiClient = { getPostComments : GetPostComments , getRepliesForComment : GetRepliesForComment - , addComment : AddComment + , addComment : AddComment , reportError : ReportError , userLogIn : LogIn , userSignUp : SignUp @@ -38,13 +38,15 @@ type alias ApiClient = } -type alias ApiRequestOutcome a = Result Http.Error a +type alias ApiRequestOutcome a = + Result Http.Error a -getApiClient : Url -> Url -> ApiClient +getApiClient : Url -> Url -> ApiClient getApiClient baseUrl siteUrl = let - api = Api baseUrl siteUrl + api = + Api baseUrl siteUrl in { getPostComments = getPostComments api , getRepliesForComment = getRepliesForComment api @@ -58,13 +60,14 @@ getApiClient baseUrl siteUrl = } - noParams : List QueryParameter -noParams = [] +noParams = + [] noHeaders : List Header -noHeaders = [] +noHeaders = + [] authHeader : ApiToken -> Header @@ -72,30 +75,30 @@ authHeader (ApiToken token) = Http.header "Authorization" token - requestResolver : Decoder a -> Http.Resolver Http.Error a requestResolver decoder = - Http.stringResolver (\response -> - case response of - Http.BadUrl_ url -> - Err (Http.BadUrl url) + Http.stringResolver + (\response -> + case response of + Http.BadUrl_ url -> + Err (Http.BadUrl url) - Http.Timeout_ -> - Err Http.Timeout + Http.Timeout_ -> + Err Http.Timeout - Http.NetworkError_ -> - Err Http.NetworkError + Http.NetworkError_ -> + Err Http.NetworkError - Http.BadStatus_ metadata _ -> - Err (Http.BadStatus metadata.statusCode) + Http.BadStatus_ metadata _ -> + Err (Http.BadStatus metadata.statusCode) - Http.GoodStatus_ _ body -> - case D.decodeString decoder body of - Ok value -> - Ok value + Http.GoodStatus_ _ body -> + case D.decodeString decoder body of + Ok value -> + Ok value - Err err -> - Err (Http.BadBody (D.errorToString err)) + Err err -> + Err (Http.BadBody (D.errorToString err)) ) @@ -111,7 +114,6 @@ getTask url headers resolver = } - postTask : String -> List Header -> Body -> Http.Resolver Http.Error a -> Task Http.Error a postTask url headers body resolver = Http.task @@ -138,13 +140,12 @@ makeRequestUrl { baseUrl } routePath queryParams = else raw - pathComponents = if routePath == "/error-reporting" then [ String.dropLeft 1 routePath ] + else String.split "/" routePath - in Url.Builder.crossOrigin stringifiedUrl @@ -166,27 +167,28 @@ getSiteInfo { siteUrl } = - - - - -- Actual API Requests + type alias ErrorInfo = { ref : String , message : String } -type alias ReportError = ErrorInfo -> Task Http.Error () + +type alias ReportError = + ErrorInfo -> Task Http.Error () + reportError : Api -> ReportError reportError api { ref, message } = let - endpointPath = "/error-reporting" + endpointPath = + "/error-reporting" body = E.object - [ ( "ref", E.string ref) + [ ( "ref", E.string ref ) , ( "errorMessage", E.string message ) ] in @@ -197,17 +199,18 @@ reportError api { ref, message } = (requestResolver <| D.succeed ()) - - type alias GetPostComments = Task Http.Error CommentTree -getPostComments : Api -> GetPostComments + +getPostComments : Api -> GetPostComments getPostComments api = let - { hostname, path } = getSiteInfo api - - endpointPath = "embed/sites/" ++ hostname ++ "/comments" + { hostname, path } = + getSiteInfo api + + endpointPath = + "embed/sites/" ++ hostname ++ "/comments" decoder = Input.apiResponseDecoder Input.commentTreeDecoder @@ -221,21 +224,22 @@ getPostComments api = (requestResolver decoder) - type alias GetRepliesForComment = Cuid -> Task Http.Error CommentTree - + getRepliesForComment : Api -> GetRepliesForComment getRepliesForComment api commentId = let - { hostname, path } = getSiteInfo api - - endpointPath = "embed/sites/" ++ hostname ++ "/comments" + { hostname, path } = + getSiteInfo api + + endpointPath = + "embed/sites/" ++ hostname ++ "/comments" decoder = Input.apiResponseDecoder Input.commentTreeDecoder - + queryParams = [ Url.Builder.string "parentCommentId" commentId , Url.Builder.string "postId" path @@ -254,8 +258,9 @@ type alias AddComment = addComment : Api -> AddComment addComment api commentContents postId parentCommentId user = let - endpointPath = "embed/posts/" ++ postId ++ "/comments" - + endpointPath = + "embed/posts/" ++ postId ++ "/comments" + decoder = Input.apiResponseDecoder Input.commentDecoder @@ -282,15 +287,15 @@ addComment api commentContents postId parentCommentId user = (requestResolver decoder) - type alias LogIn = Output.LogIn -> Task Http.Error UserInfoWithToken -userLogIn : Api -> LogIn +userLogIn : Api -> LogIn userLogIn api data = let - endpointPath = "common/signin" + endpointPath = + "common/signin" signinJson = E.object @@ -312,7 +317,8 @@ type alias SignUp = userSignUp : Api -> SignUp userSignUp api data = let - endpointPath = "common/signup" + endpointPath = + "common/signup" signUpJson = E.object @@ -327,6 +333,7 @@ userSignUp api data = (Http.jsonBody signUpJson) (requestResolver Input.userAndTokenDecoder) + type alias GetUserFromSessionToken = ApiToken -> Task Http.Error UserInfo @@ -334,7 +341,8 @@ type alias GetUserFromSessionToken = getUserFromSessionToken : Api -> GetUserFromSessionToken getUserFromSessionToken api apiToken = let - endpointPath = "common/profile" + endpointPath = + "common/profile" headers = [ authHeader apiToken @@ -349,16 +357,18 @@ getUserFromSessionToken api apiToken = (requestResolver decoder) - type alias GetUserInteractions = - ApiToken -> Task Http.Error Interactions + ApiToken -> Task Http.Error Interactions + getUserInteractions : Api -> GetUserInteractions getUserInteractions api apiToken = let - endpointPath = "embed/interactions" + endpointPath = + "embed/interactions" - { path } = getSiteInfo api + { path } = + getSiteInfo api headers = [ authHeader apiToken ] @@ -378,18 +388,26 @@ getUserInteractions api apiToken = type alias SubmitVoteForComment = ApiToken -> Cuid -> VoteAction -> Task Http.Error () + submitVoteForComment : Api -> SubmitVoteForComment submitVoteForComment api apiToken commentId voteType = let - endpointPath = "embed/comments/" ++ commentId ++ "/vote" + endpointPath = + "embed/comments/" ++ commentId ++ "/vote" - headers = [ authHeader apiToken ] + headers = + [ authHeader apiToken ] - voteInt = + voteInt = case voteType of - SetUp -> 1 - SetDown -> -1 - SetNeutral -> 0 + SetUp -> + 1 + + SetDown -> + -1 + + SetNeutral -> + 0 body = E.object @@ -402,6 +420,5 @@ submitVoteForComment api apiToken commentId voteType = postTask (makeRequestUrl api endpointPath noParams) headers - (Http.jsonBody body) + (Http.jsonBody body) (requestResolver decoder) - diff --git a/src/elm/Api/Input.elm b/src/elm/Api/Input.elm index 529a4bf..3e8b6df 100644 --- a/src/elm/Api/Input.elm +++ b/src/elm/Api/Input.elm @@ -3,26 +3,26 @@ module Api.Input exposing , commentDecoder , commentTreeDecoder , interactionsDecoder - , userInfoDecoder , userAndTokenDecoder + , userInfoDecoder ) {-| Represents the Incoming data from the server. Includes JSON decoders and types. + -} -import Data exposing (Author(..), CommentVote, Interactions, UserInfo, UserInfoWithToken, ApiToken(..)) +import Data exposing (ApiToken(..), Author(..), CommentVote, Interactions, User(..), UserInfo, UserInfoWithToken) import Data.Comment exposing (Comment, CommentTree) import Json.Decode as D exposing (Decoder) -import Time exposing (Posix) -import Set import RemoteData -import Data exposing (User(..)) +import Set +import Time exposing (Posix) -map9 - : (a -> b -> c -> d -> e -> f -> g -> h -> i -> value) +map9 : + (a -> b -> c -> d -> e -> f -> g -> h -> i -> value) -> Decoder a -> Decoder b -> Decoder c @@ -34,7 +34,7 @@ map9 -> Decoder i -> Decoder value map9 f da db dc dd de df dg dh di = - (D.map2 (\f_ i -> f_ i)) + D.map2 (\f_ i -> f_ i) (D.map8 f da db dc dd de df dg dh) di @@ -49,9 +49,8 @@ timestampDecoder = D.map Time.millisToPosix D.int - -intoComment - : String +intoComment : + String -> Maybe String -> String -> String @@ -62,18 +61,22 @@ intoComment -> Comment intoComment id maybeParentCommentId anonAuthorName body replyIds isLeaf createdAt maybeUserInfo = let - replyIdSet = Set.fromList replyIds + replyIdSet = + Set.fromList replyIds commentReplyBufferState = -- if the server didn't respond back with the Comment's replies... if Set.isEmpty replyIdSet && not isLeaf then RemoteData.NotAsked + else RemoteData.Success () - textAreaVisibility = False + textAreaVisibility = + False - textAreaState = ( textAreaVisibility, "" ) + textAreaState = + ( textAreaVisibility, "" ) author = case maybeUserInfo of @@ -83,7 +86,8 @@ intoComment id maybeParentCommentId anonAuthorName body replyIds isLeaf createdA Nothing -> Anonymous_ anonAuthorName - isFolded = False + isFolded = + False in Comment id @@ -100,7 +104,7 @@ intoComment id maybeParentCommentId anonAuthorName body replyIds isLeaf createdA commentDecoder : Decoder Comment -commentDecoder = +commentDecoder = D.map8 intoComment (D.field "id" D.string) (D.field "parentCommentId" <| D.maybe D.string) @@ -117,7 +121,7 @@ commentTreeDecoder = let setDecoder = D.list D.string - |> D.map Set.fromList + |> D.map Set.fromList in D.map3 CommentTree (D.field "comments" <| D.dict commentDecoder) @@ -125,7 +129,6 @@ commentTreeDecoder = (D.field "postId" D.string) - userInfoDecoder : Decoder UserInfo userInfoDecoder = D.map4 @@ -145,7 +148,7 @@ userInfoDecoder = userAndTokenDecoder : Decoder UserInfoWithToken userAndTokenDecoder = - D.map2 (\userInfo tokenString -> (userInfo, ApiToken tokenString)) + D.map2 (\userInfo tokenString -> ( userInfo, ApiToken tokenString )) (apiResponseDecoder userInfoDecoder) (D.field "sessionToken" D.string) @@ -160,5 +163,3 @@ interactionsDecoder = in D.map Interactions (D.field "commentVotes" <| D.dict commentVotesDecoder) - - diff --git a/src/elm/Api/Output.elm b/src/elm/Api/Output.elm index 2295257..9257567 100644 --- a/src/elm/Api/Output.elm +++ b/src/elm/Api/Output.elm @@ -1,5 +1,4 @@ -module Api.Output exposing (addCommentBody, LogIn, SignUp) - +module Api.Output exposing (LogIn, SignUp, addCommentBody) {-| JSON encoders to send JSON data -} @@ -9,24 +8,25 @@ import Http exposing (Body) import Json.Encode as E exposing (Value) - encodeNullable : (a -> Value) -> Maybe a -> Value encodeNullable f = Maybe.map f - >> Maybe.withDefault E.null + >> Maybe.withDefault E.null type alias AddComment = { body : String , parentCommentId : Maybe Cuid - , authorId : Maybe Cuid + , authorId : Maybe Cuid , anonAuthorName : Maybe String } + addCommentBody : AddComment -> Body addCommentBody { body, parentCommentId, authorId, anonAuthorName } = let - maybeEncode = encodeNullable E.string + maybeEncode = + encodeNullable E.string jsonValue = E.object @@ -38,8 +38,9 @@ addCommentBody { body, parentCommentId, authorId, anonAuthorName } = in Http.jsonBody jsonValue + type alias LogIn = - { usernameOrEmail : String + { usernameOrEmail : String , password : String } @@ -49,4 +50,3 @@ type alias SignUp = , email : String , password : String } - diff --git a/src/elm/Data.elm b/src/elm/Data.elm index 458f205..ac0b498 100644 --- a/src/elm/Data.elm +++ b/src/elm/Data.elm @@ -1,26 +1,28 @@ module Data exposing ( ApiToken(..) , Author(..) + , CommentVote + , Interactions , User(..) , UserInfo , UserInfoWithToken - , VoteType(..) , VoteAction(..) - , Interactions - , CommentVote + , VoteType(..) ) import Data.Cuid exposing (Cuid) import Dict exposing (Dict) import Time -type ApiToken = ApiToken String + +type ApiToken + = ApiToken String type User = Authenticated UserInfo Interactions ApiToken - -- We may have access to a server-generated - -- random username if the user has already commented + -- We may have access to a server-generated + -- random username if the user has already commented | Anonymous (Maybe String) @@ -38,9 +40,15 @@ type alias UserInfo = } -type VoteType = Up | Down +type VoteType + = Up + | Down + -type VoteAction = SetUp | SetDown | SetNeutral +type VoteAction + = SetUp + | SetDown + | SetNeutral type alias CommentVote = @@ -49,11 +57,14 @@ type alias CommentVote = } --- data about a particular action that a user has taken + +-- data about a particular action that a user has taken + type alias Interactions = { commentVotes : Dict Cuid CommentVote } -type alias UserInfoWithToken = ( UserInfo, ApiToken ) +type alias UserInfoWithToken = + ( UserInfo, ApiToken ) diff --git a/src/elm/Data/Comment.elm b/src/elm/Data/Comment.elm index 4100d60..8be5265 100644 --- a/src/elm/Data/Comment.elm +++ b/src/elm/Data/Comment.elm @@ -3,21 +3,23 @@ module Data.Comment exposing , CommentMap , CommentTree , addNewComment + , getCommentsFromPointers , isReply - , updateComment , setComment - , getCommentsFromPointers + , updateComment ) import Data exposing (Author(..)) import Data.Cuid exposing (Cuid) import Dict exposing (Dict) -import Time exposing (Posix) import RemoteData exposing (WebData) import Set exposing (Set) +import Time exposing (Posix) + +type alias TextAreaVisibility = + Bool -type alias TextAreaVisibility = Bool type alias Comment = { id : String @@ -33,7 +35,7 @@ type alias Comment = -- CONTEXT REGARDING replyIds + remoteReplyBuffer: -- -- Comment replies could be in one of the following states: - -- 1. Comment came with all of its children replies + -- 1. Comment came with all of its children replies -- 2. Comment did not come with all of its replies (because it's so deeply nested in a comment tree) -- 3. Comment did not come with all of its replies AND we just added a new reply -- Hence you have a "partial" representation of all the replies of that comment @@ -43,13 +45,14 @@ type alias Comment = -- the ids and add them to replyIds immediately , replyIds : Set Cuid , remoteReplyBuffer : WebData () - , createdAt : Posix , textAreaState : ( TextAreaVisibility, String ) } -type alias CommentMap = Dict Cuid Comment +type alias CommentMap = + Dict Cuid Comment + type alias CommentTree = { comments : CommentMap @@ -61,10 +64,11 @@ type alias CommentTree = isReply : Comment -> Bool isReply { parentCommentId } = case parentCommentId of - Just _ -> True - Nothing -> False - + Just _ -> + True + Nothing -> + False getCommentsFromPointers : CommentMap -> Set Cuid -> List Comment @@ -74,6 +78,7 @@ getCommentsFromPointers commentMap = case Dict.get cuid commentMap of Just comment -> comment :: comments + Nothing -> comments ) @@ -89,7 +94,6 @@ updateComment f commentCuid currentTree = { currentTree | comments = newComments } - setComment : Comment -> CommentTree -> CommentTree setComment comment = updateComment (always comment) comment.id @@ -106,7 +110,7 @@ addNewComment newComment commentTree = } addReply : Cuid -> CommentTree - addReply parentCommentId = + addReply parentCommentId = let -- updates parent with the new comment's pointer updateParentComment = @@ -124,28 +128,24 @@ addNewComment newComment commentTree = } ) parentCommentId - in commentTree - |> updateParentComment - |> addNewCommentToCommentMap - + |> updateParentComment + |> addNewCommentToCommentMap -- adding an empty tuple argument to enforce lazyness addTopLevelComment : () -> CommentTree addTopLevelComment _ = let addNewCommentPointerToTopLevelComments tree = - { tree + { tree | topLevelComments = Set.insert newComment.id commentTree.topLevelComments } - in commentTree - |> addNewCommentPointerToTopLevelComments - |> addNewCommentToCommentMap - + |> addNewCommentPointerToTopLevelComments + |> addNewCommentToCommentMap in case newComment.parentCommentId of Just parentCommentId -> @@ -153,4 +153,3 @@ addNewComment newComment commentTree = Nothing -> addTopLevelComment () - diff --git a/src/elm/Data/Cuid.elm b/src/elm/Data/Cuid.elm index 4a8c909..f5e899a 100644 --- a/src/elm/Data/Cuid.elm +++ b/src/elm/Data/Cuid.elm @@ -1,4 +1,5 @@ module Data.Cuid exposing (Cuid) -type alias Cuid = String +type alias Cuid = + String diff --git a/src/elm/Data/RemoteUser.elm b/src/elm/Data/RemoteUser.elm index aca1573..15b0e35 100644 --- a/src/elm/Data/RemoteUser.elm +++ b/src/elm/Data/RemoteUser.elm @@ -4,63 +4,70 @@ module Data.RemoteUser exposing , setUserInfo ) - import Api exposing (ApiRequestOutcome) import Data exposing (Interactions, User(..), UserInfoWithToken) + {-| Represents a combined async data structure - we do 2 parallel requests and combine them once they're both complete +we do 2 parallel requests and combine them once they're both complete just like Promise.all there isn't a failure state because when a http request fails, we just fall back to the anonymous user + -} type RemoteUser = AwaitingUserInfoAndInteractions | ReceivedUserInfo UserInfoWithToken | ReceivedInteractions Interactions - -- recall that User is a union type - -- and authenticated user contains UserInfo and Interactions - | UserLoaded User - + -- recall that User is a union type + -- and authenticated user contains UserInfo and Interactions + | UserLoaded User setUserInfo : Maybe String -> ApiRequestOutcome UserInfoWithToken -> RemoteUser -> RemoteUser setUserInfo fallbackAnonUsername outcome user = let - fallbackOnError = UserLoaded (Anonymous fallbackAnonUsername) + fallbackOnError = + UserLoaded (Anonymous fallbackAnonUsername) in case user of ReceivedInteractions interactions -> outcome - |> Result.map (\(userInfo, apiToken)-> UserLoaded (Authenticated userInfo interactions apiToken)) - |> Result.withDefault fallbackOnError + |> Result.map (\( userInfo, apiToken ) -> UserLoaded (Authenticated userInfo interactions apiToken)) + |> Result.withDefault fallbackOnError AwaitingUserInfoAndInteractions -> outcome - |> Result.map ReceivedUserInfo - |> Result.withDefault fallbackOnError + |> Result.map ReceivedUserInfo + |> Result.withDefault fallbackOnError + + UserLoaded loadedUser -> + UserLoaded loadedUser - UserLoaded loadedUser -> UserLoaded loadedUser - ReceivedUserInfo userInfoWithToken -> ReceivedUserInfo userInfoWithToken + ReceivedUserInfo userInfoWithToken -> + ReceivedUserInfo userInfoWithToken setInteractions : Maybe String -> ApiRequestOutcome Interactions -> RemoteUser -> RemoteUser setInteractions fallbackAnonUsername outcome user = let - fallbackOnError = UserLoaded (Anonymous fallbackAnonUsername) + fallbackOnError = + UserLoaded (Anonymous fallbackAnonUsername) in case user of - ReceivedUserInfo (userInfo, apiToken) -> + ReceivedUserInfo ( userInfo, apiToken ) -> outcome - |> Result.map (\interactions -> UserLoaded <| Authenticated userInfo interactions apiToken) - |> Result.withDefault fallbackOnError + |> Result.map (\interactions -> UserLoaded <| Authenticated userInfo interactions apiToken) + |> Result.withDefault fallbackOnError AwaitingUserInfoAndInteractions -> outcome - |> Result.map ReceivedInteractions - |> Result.withDefault fallbackOnError + |> Result.map ReceivedInteractions + |> Result.withDefault fallbackOnError - UserLoaded loadedUser -> UserLoaded loadedUser - ReceivedInteractions interactions -> ReceivedInteractions interactions + UserLoaded loadedUser -> + UserLoaded loadedUser + ReceivedInteractions interactions -> + ReceivedInteractions interactions diff --git a/src/elm/Data/SimpleWebData.elm b/src/elm/Data/SimpleWebData.elm index 17aa90b..5775d52 100644 --- a/src/elm/Data/SimpleWebData.elm +++ b/src/elm/Data/SimpleWebData.elm @@ -2,8 +2,12 @@ module Data.SimpleWebData exposing (SimpleWebData(..), mapSimpleWebData) import Http + + -- There is no such thing as "not asked" for this data type -type SimpleWebData a + + +type SimpleWebData a = Loading | Success a | Failure Http.Error @@ -12,6 +16,11 @@ type SimpleWebData a mapSimpleWebData : (a -> b) -> SimpleWebData a -> SimpleWebData b mapSimpleWebData f simpleWebData = case simpleWebData of - Success data -> Success (f data) - Loading -> Loading - Failure e -> Failure e + Success data -> + Success (f data) + + Loading -> + Loading + + Failure e -> + Failure e diff --git a/src/elm/ErrorReporting.elm b/src/elm/ErrorReporting.elm index adc62b8..ac1b268 100644 --- a/src/elm/ErrorReporting.elm +++ b/src/elm/ErrorReporting.elm @@ -1,5 +1,4 @@ -module ErrorReporting exposing (reporterFactory, ReporterClient) - +module ErrorReporting exposing (ReporterClient, reporterFactory) import Api exposing (ApiClient) import Http @@ -7,20 +6,27 @@ import Task import Time exposing (Posix) import Utils -type alias HttpResponseOutcome = Result Http.Error () -type alias Tagger msg = HttpResponseOutcome -> msg +type alias HttpResponseOutcome = + Result Http.Error () + + +type alias Tagger msg = + HttpResponseOutcome -> msg + type alias ReporterClient msg = { reportInvalidTimeStamps : ReportInvalidTimeStamps msg } + type alias Reporter msg = { apiClient : ApiClient , toMsg : Tagger msg , gitRef : Maybe String } + reporterFactory : ApiClient -> Tagger msg -> Maybe String -> ReporterClient msg reporterFactory apiClient tagger maybeGitRef = let @@ -34,12 +40,15 @@ reporterFactory apiClient tagger maybeGitRef = } -type alias ReportInvalidTimeStamps msg = Posix -> Posix -> Cmd msg +type alias ReportInvalidTimeStamps msg = + Posix -> Posix -> Cmd msg + reportInvalidTimeStamps : Reporter msg -> ReportInvalidTimeStamps msg reportInvalidTimeStamps { apiClient, toMsg, gitRef } currentTime newCommentTime = let - timeDiff = Utils.timeDiff currentTime newCommentTime + timeDiff = + Utils.timeDiff currentTime newCommentTime in -- don't do reporting in non-production environments, which shouldn't have a git ref case gitRef of @@ -47,20 +56,19 @@ reportInvalidTimeStamps { apiClient, toMsg, gitRef } currentTime newCommentTime if timeDiff < 0 then let errorMsg = - "Invalid timestamps for newly added comment. " ++ - ( "CurrentTime: " ++ (String.fromInt <| Time.posixToMillis currentTime)) ++ - " - " ++ - ( "New Comment TimeStamp " ++ (String.fromInt <| Time.posixToMillis newCommentTime)) + "Invalid timestamps for newly added comment. " + ++ ("CurrentTime: " ++ (String.fromInt <| Time.posixToMillis currentTime)) + ++ " - " + ++ ("New Comment TimeStamp " ++ (String.fromInt <| Time.posixToMillis newCommentTime)) in Task.attempt toMsg <| apiClient.reportError { ref = ref , message = errorMsg } + else Cmd.none Nothing -> Cmd.none - - diff --git a/src/elm/Main.elm b/src/elm/Main.elm index abe8a28..469266f 100644 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -3,7 +3,7 @@ module Main exposing (main) import Ant.Button as Btn exposing (button) import Ant.Form.View as FV import Ant.Modal as Modal -import Api +import Api import Browser import Css exposing (..) import Data exposing (User(..)) @@ -13,7 +13,7 @@ import Data.SimpleWebData as SimpleWebData import Html exposing (Html) import Html.Styled as Styled exposing (fromUnstyled) import Html.Styled.Attributes exposing (css) -import Model exposing (AppData, Model(..), ModalState(..), Msg(..)) +import Model exposing (AppData, ModalState(..), Model(..), Msg(..)) import Task import Time import UI.AppShell exposing (appShell) @@ -24,7 +24,7 @@ import Url import Utils -type alias Flags = +type alias Flags = { apiEndpoint : String , siteUrl : String , anonymousUsername : Maybe String @@ -34,26 +34,27 @@ type alias Flags = } -main : Program Flags Model Msg +main : Program Flags Model Msg main = - Browser.element - { init = init - , view = view - , update = Model.update - , subscriptions = subscriptions - } - + Browser.element + { init = init + , view = view + , update = Model.update + , subscriptions = subscriptions + } -- INIT -init : Flags -> (Model, Cmd Msg) + +init : Flags -> ( Model, Cmd Msg ) init flags = case ( Url.fromString flags.apiEndpoint, Url.fromString flags.siteUrl ) of ( Just apiBaseUrl, Just siteUrl ) -> let - apiClient = Api.getApiClient apiBaseUrl siteUrl + apiClient = + Api.getApiClient apiBaseUrl siteUrl user = case maybeApiToken of @@ -63,10 +64,9 @@ init flags = Just _ -> RemoteUser.AwaitingUserInfoAndInteractions - ( model, baseCmd ) = Model.intoReadyState - flags.gitRef + flags.gitRef apiClient user (Time.millisToPosix flags.currentTime) @@ -78,7 +78,7 @@ init flags = case maybeApiToken of -- if no token, then just get comments Nothing -> - ( model + ( model , baseCmd ) @@ -112,18 +112,16 @@ init flags = - - - - -- SUBSCRIPTIONS - -- Currently assuming that the first Msg emitted occurs -- after the time elapses, not immediately at time 0 + + subscriptions : Model -> Sub Msg subscriptions _ = let - fiveMinutes = 1000 * 60 * 5 + fiveMinutes = + 1000 * 60 * 5 in Time.every fiveMinutes NewCurrentTime @@ -131,9 +129,10 @@ subscriptions _ = -- VIEW + intoSubmitCommentAction : Cuid -> Maybe Cuid -> String -> RemoteUser -> Maybe Msg intoSubmitCommentAction postId parentCommentId textAreaValue remoteUser = - case remoteUser of + case remoteUser of RemoteUser.UserLoaded user -> Just (SubmitComment user postId parentCommentId textAreaValue) @@ -146,10 +145,10 @@ viewAuthenticationForm modalState anonymousUsername = let viewModal contents title = Modal.modal contents - |> Modal.withTitle title - |> Modal.withOnCancel (\_ -> ModalStateChanged Hidden) - |> Modal.toHtml True - |> fromUnstyled + |> Modal.withTitle title + |> Modal.withOnCancel (\_ -> ModalStateChanged Hidden) + |> Modal.toHtml True + |> fromUnstyled in case modalState of Hidden -> @@ -157,7 +156,8 @@ viewAuthenticationForm modalState anonymousUsername = ShowingLogInForm logInFormState -> let - formSubmitMsg = LogInRequested logInFormState anonymousUsername + formSubmitMsg = + LogInRequested logInFormState anonymousUsername logInForm = FV.toHtml @@ -173,7 +173,8 @@ viewAuthenticationForm modalState anonymousUsername = ShowingSignUpForm signUpFormState -> let - formSubmitMsg = SignUpRequested signUpFormState anonymousUsername + formSubmitMsg = + SignUpRequested signUpFormState anonymousUsername signUpForm = FV.toHtml @@ -188,7 +189,6 @@ viewAuthenticationForm modalState anonymousUsername = viewModal signUpForm "Sign Up" - viewApp : AppData -> Styled.Html Msg viewApp model = let @@ -202,18 +202,20 @@ viewApp model = SimpleWebData.Success commentTree -> let - timeStampFormatter = Utils.humanReadableTimestamp model.currentTime + timeStampFormatter = + Utils.humanReadableTimestamp model.currentTime actions = { loadRepliesForComment = LoadRepliesForCommentRequested , updateComment = CommentChanged , submitCommentVote = VoteButtonClicked - , submitReply = \commentId replyTextAreaValue -> - intoSubmitCommentAction - commentTree.postId - (Just commentId) - replyTextAreaValue - model.user + , submitReply = + \commentId replyTextAreaValue -> + intoSubmitCommentAction + commentTree.postId + (Just commentId) + replyTextAreaValue + model.user } commentsSection = @@ -231,31 +233,31 @@ viewApp model = model.textAreaValue model.user - - authenticationPrompt = viewAuthenticationInfo model.user AuthenticationButtonClicked + authenticationPrompt = + viewAuthenticationInfo model.user AuthenticationButtonClicked textArea = topLevelTextArea TextAreaValueChanged model.textAreaValue authenticationPrompt - |> TextArea.toHtml textAreaAction + |> TextArea.toHtml textAreaAction in Styled.div [] [ Styled.div [ css [ marginBottom (px 10) ] ] [ textArea ] - , commentsSection + , commentsSection ] poweredByParlezVous = let - poweredByText = button "ParlezVous" - |> Btn.onClick GoToParlezVous - |> Btn.withType Btn.Text - |> Btn.toHtml - |> fromUnstyled - + poweredByText = + button "ParlezVous" + |> Btn.onClick GoToParlezVous + |> Btn.withType Btn.Text + |> Btn.toHtml + |> fromUnstyled in Styled.div [ css - [ marginTop (px 25) + [ marginTop (px 25) , textAlign center ] ] @@ -266,7 +268,7 @@ viewApp model = modal = case model.user of RemoteUser.UserLoaded user -> - case user of + case user of Anonymous maybeAnonymousUsername -> viewAuthenticationForm model.modal maybeAnonymousUsername @@ -283,7 +285,6 @@ viewApp model = ] - view : Model -> Html Msg view model = let @@ -296,4 +297,3 @@ view model = viewApp appData in appShell contents - diff --git a/src/elm/Model.elm b/src/elm/Model.elm index 70503e6..a7e2028 100644 --- a/src/elm/Model.elm +++ b/src/elm/Model.elm @@ -1,17 +1,16 @@ module Model exposing ( AppData + , ModalState(..) , Model(..) , Msg(..) - , ModalState(..) , intoReadyState , update ) - import Ant.Form.View as FV import Api exposing (ApiClient, ApiRequestOutcome) import Browser.Navigation as Nav -import Data exposing (ApiToken, User(..), UserInfo, UserInfoWithToken, VoteType(..), VoteAction(..)) +import Data exposing (ApiToken, Interactions, User(..), UserInfo, UserInfoWithToken, VoteAction(..), VoteType(..)) import Data.Comment as Comment exposing (Comment, CommentTree, updateComment) import Data.Cuid exposing (Cuid) import Data.RemoteUser as RemoteUser exposing (RemoteUser) @@ -25,11 +24,14 @@ import Task import Time import UI.AuthenticationInfo as AuthenticationInfo import Utils -import Data exposing (Interactions) -type alias LogInFormState = FV.Model AuthenticationInfo.LogInValues -type alias SignUpFormState = FV.Model AuthenticationInfo.SignUpValues +type alias LogInFormState = + FV.Model AuthenticationInfo.LogInValues + + +type alias SignUpFormState = + FV.Model AuthenticationInfo.SignUpValues type ModalState @@ -38,19 +40,14 @@ type ModalState | ShowingSignUpForm SignUpFormState - - - - type alias AppData = { textAreaValue : String , commentTree : SimpleWebData CommentTree , currentTime : Time.Posix , apiClient : Api.ApiClient , user : RemoteUser - , modal : ModalState - + -- for telemetry , reporter : ReporterClient Msg } @@ -61,9 +58,6 @@ type Model | Failed String - - - type Msg = TextAreaValueChanged String | SubmitComment User Cuid (Maybe Cuid) String @@ -71,22 +65,17 @@ type Msg | GoToParlezVous | NewCurrentTime Time.Posix | VoteButtonClicked Cuid VoteType - - -- Authentication Stuff + -- Authentication Stuff | AuthenticationButtonClicked AuthenticationInfo.AuthenticationRequest | ModalStateChanged ModalState | LogInRequested LogInFormState (Maybe String) String String | SignUpRequested SignUpFormState (Maybe String) String String String - - - -- comments have internal state - -- (currently text area visibility and text area value) - -- this msg represents changes in both of these values + -- comments have internal state + -- (currently text area visibility and text area value) + -- this msg represents changes in both of these values | CommentChanged Comment - - - -- Api outcomes - | CommentSubmitted User (ApiRequestOutcome (Time.Posix, Comment)) + -- Api outcomes + | CommentSubmitted User (ApiRequestOutcome ( Time.Posix, Comment )) | InitialPostCommentsFetched (ApiRequestOutcome CommentTree) | RepliesForCommentFetched Cuid (ApiRequestOutcome CommentTree) | ErrorReportSubmitted (ApiRequestOutcome ()) @@ -96,21 +85,22 @@ type Msg | ReceivedVoteResponse (ApiRequestOutcome ()) --- handler functions exported for testing +-- handler functions exported for testing -- handleCommentSubmitted : ( Time.Posix, Comment ) -> {-| This function gets called once we receive the FIRST NewCurrentTime msg. This function should never be called again. --} -intoReadyState - : Maybe String - -> ApiClient - -> RemoteUser - -> Time.Posix - -> ( Model, Cmd Msg ) + +-} +intoReadyState : + Maybe String + -> ApiClient + -> RemoteUser + -> Time.Posix + -> ( Model, Cmd Msg ) intoReadyState gitRef apiClient user time = let initialAppData = @@ -123,14 +113,14 @@ intoReadyState gitRef apiClient user time = , user = user } - apiRequest = Task.attempt InitialPostCommentsFetched apiClient.getPostComments + apiRequest = + Task.attempt InitialPostCommentsFetched apiClient.getPostComments in ( Ready initialAppData , apiRequest ) - update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case ( model, msg ) of @@ -141,7 +131,6 @@ update msg model = updateReadyModel msg embedModel - setFormSubmittingState : (FV.Model a -> ModalState) -> FV.Model a -> AppData -> AppData setFormSubmittingState intoModalState formState appData = { appData @@ -161,17 +150,17 @@ setAuthModalState authType appData = logInFormState = FV.idle - { usernameOrEmail = "" - , password = emptyPasswordField - } + { usernameOrEmail = "" + , password = emptyPasswordField + } signUpFormState = FV.idle - { username = "" - , email = "" - , password = emptyPasswordField - , passwordConfirm = emptyPasswordField - } + { username = "" + , email = "" + , password = emptyPasswordField + , passwordConfirm = emptyPasswordField + } modalState = case authType of @@ -180,7 +169,8 @@ setAuthModalState authType appData = AuthenticationInfo.SignUp -> ShowingSignUpForm signUpFormState - in { appData | modal = modalState } + in + { appData | modal = modalState } updateReadyModel : Msg -> AppData -> ( Model, Cmd Msg ) @@ -188,7 +178,7 @@ updateReadyModel msg model = Tuple.mapFirst Ready <| case msg of NewCurrentTime time -> - Utils.simpleUpdate + Utils.simpleUpdate { model | currentTime = time } GoToParlezVous -> @@ -200,33 +190,48 @@ updateReadyModel msg model = let outgoingVote interactions clickedVote = let - setUp = ( SetUp, 1 ) - setDown = ( SetDown, -1 ) - setNeutral = ( SetNeutral, 0 ) + setUp = + ( SetUp, 1 ) + + setDown = + ( SetDown, -1 ) + + setNeutral = + ( SetNeutral, 0 ) defaultVoteAction = case voteType of - Up -> setUp - Down -> setDown + Up -> + setUp + Down -> + setDown in Dict.get commentId interactions.commentVotes - |> Maybe.map - (\{ value } -> - if value > 0 then - case clickedVote of - Up -> setNeutral -- cancel upvote - Down -> setDown - else if value < 0 then - case clickedVote of - Up -> setUp - Down -> setNeutral -- cancel downvote - else - defaultVoteAction - ) - |> Maybe.withDefault defaultVoteAction - - + |> Maybe.map + (\{ value } -> + if value > 0 then + case clickedVote of + Up -> + setNeutral + + -- cancel upvote + Down -> + setDown + + else if value < 0 then + case clickedVote of + Up -> + setUp + + Down -> + setNeutral + -- cancel downvote + + else + defaultVoteAction + ) + |> Maybe.withDefault defaultVoteAction in case model.user of RemoteUser.UserLoaded user -> @@ -247,18 +252,19 @@ updateReadyModel msg model = { commentId = commentId, value = voteValue } interactions.commentVotes in - { interactions | commentVotes = newCommentVotes + { interactions + | commentVotes = newCommentVotes } - task = model.apiClient.submitVoteForComment apiToken commentId voteAction in - ( { model | user = - RemoteUser.UserLoaded + ( { model + | user = + RemoteUser.UserLoaded (Authenticated userInfo newInteractions apiToken) } , Task.attempt @@ -286,7 +292,7 @@ updateReadyModel msg model = (UserLoggedIn maybeAnonymousUsername) (model.apiClient.userLogIn logInData) in - ( setFormSubmittingState ShowingLogInForm logInFormState model + ( setFormSubmittingState ShowingLogInForm logInFormState model , logInCmd ) @@ -307,7 +313,6 @@ updateReadyModel msg model = , signUpCmd ) - TextAreaValueChanged newValue -> Utils.simpleUpdate { model | textAreaValue = newValue } @@ -331,8 +336,7 @@ updateReadyModel msg model = Err e -> Utils.simpleUpdate newModel - - Ok (_, (Data.ApiToken token) as apiToken) -> + Ok ( _, (Data.ApiToken token) as apiToken ) -> let getInteractionsCmd = Task.attempt @@ -342,25 +346,27 @@ updateReadyModel msg model = ( newModel , Cmd.batch [ Utils.writeToLocalStorage - ( "sessionToken", token + ( "sessionToken" + , token ) - , getInteractionsCmd + , getInteractionsCmd ] ) - InitialPostCommentsFetched httpRequestResult -> case httpRequestResult of Err e -> Utils.simpleUpdate - { model | commentTree = - SimpleWebData.Failure e + { model + | commentTree = + SimpleWebData.Failure e } Ok initialCommentResponse -> Utils.simpleUpdate - { model | commentTree = - SimpleWebData.Success initialCommentResponse + { model + | commentTree = + SimpleWebData.Success initialCommentResponse } ReceivedVoteResponse httpRequestResult -> @@ -369,9 +375,9 @@ updateReadyModel msg model = ReceivedSessionResponse apiToken cachedMaybeAnonUsername httpRequestResult -> let resultWithToken = - Result.map (\user -> (user, apiToken)) httpRequestResult + Result.map (\user -> ( user, apiToken )) httpRequestResult - cmd = + cmd = case httpRequestResult of Ok _ -> Cmd.none @@ -381,17 +387,18 @@ updateReadyModel msg model = Http.BadStatus statusValue -> if statusValue == 401 then Utils.removeSessionToken () + else -- TODO: report unexpected HTTP status code Cmd.none - _ -> + _ -> -- TODO: handle other HTTP errors -- https://package.elm-lang.org/packages/elm/http/latest/Http#Error Cmd.none in ( { model - | user = + | user = RemoteUser.setUserInfo cachedMaybeAnonUsername resultWithToken @@ -402,19 +409,21 @@ updateReadyModel msg model = ReceivedInteractionsResponse fallbackAnonUsername interactionsHttpResult -> Utils.simpleUpdate - { model | user = - RemoteUser.setInteractions - fallbackAnonUsername - interactionsHttpResult - model.user + { model + | user = + RemoteUser.setInteractions + fallbackAnonUsername + interactionsHttpResult + model.user } RepliesForCommentFetched commentCuid httpRequestResult -> case httpRequestResult of Err e -> Utils.simpleUpdate - { model | commentTree = - SimpleWebData.Failure e + { model + | commentTree = + SimpleWebData.Failure e } -- 1. update this specific comment's reply list @@ -425,12 +434,13 @@ updateReadyModel msg model = -- direct children of the parent comment in question. -- all other comments are 2nd or 3rd level descendants -- i.e. replies to other replies in this api response - directRepliesToComment = subCommentTree.topLevelComments + directRepliesToComment = + subCommentTree.topLevelComments -- update the comment in question -- with the list of children reply ids treeStateUpdate = - updateComment + updateComment (\comment -> { comment | replyIds = Set.union comment.replyIds directRepliesToComment @@ -442,25 +452,28 @@ updateReadyModel msg model = newCommentTree = mapSimpleWebData (\commentTree -> - let + let -- run the above mutation - treeWithUpdatedState = treeStateUpdate commentTree + treeWithUpdatedState = + treeStateUpdate commentTree in - { treeWithUpdatedState | comments = - -- add new replies / comments to flattened comment map - Dict.union subCommentTree.comments treeWithUpdatedState.comments + { treeWithUpdatedState + | comments = + -- add new replies / comments to flattened comment map + Dict.union subCommentTree.comments treeWithUpdatedState.comments } ) model.commentTree in Utils.simpleUpdate - { model | commentTree = newCommentTree + { model + | commentTree = newCommentTree } LoadRepliesForCommentRequested commentCuid -> let updateCommentsInCommentTree = - updateComment + updateComment (\comment -> { comment | remoteReplyBuffer = RemoteData.Loading @@ -468,43 +481,43 @@ updateReadyModel msg model = ) commentCuid - newCommentTree = + newCommentTree = mapSimpleWebData updateCommentsInCommentTree model.commentTree - tagger = RepliesForCommentFetched commentCuid + tagger = + RepliesForCommentFetched commentCuid - apiRequest = Task.attempt tagger (model.apiClient.getRepliesForComment commentCuid) + apiRequest = + Task.attempt tagger (model.apiClient.getRepliesForComment commentCuid) in -- 1. set this specific comment's replies as RemoteData.Loading - -- 2. issue Cmd to fetch data + -- 2. issue Cmd to fetch data ( { model | commentTree = newCommentTree } , apiRequest ) - SubmitComment user postId maybeParentCommentId commentBody -> let - -- Task Stuff - addCommentTask = + -- Task Stuff + addCommentTask = model.apiClient.addComment commentBody postId maybeParentCommentId - user + user wrapCommentInTimestamp comment = Time.now - |> Task.map (\timestamp -> (timestamp, comment)) + |> Task.map (\timestamp -> ( timestamp, comment )) tasks = addCommentTask - |> Task.andThen wrapCommentInTimestamp - |> Task.attempt (CommentSubmitted user) + |> Task.andThen wrapCommentInTimestamp + |> Task.attempt (CommentSubmitted user) in -- update the time, then send the request ( model, tasks ) - CommentSubmitted user result -> case result of Err e -> @@ -517,6 +530,7 @@ updateReadyModel msg model = newTextAreaValue = if Comment.isReply newComment then model.textAreaValue + else "" @@ -530,12 +544,10 @@ updateReadyModel msg model = , textAreaValue = newTextAreaValue } - reporterMsg = model.reporter.reportInvalidTimeStamps currentTime newComment.createdAt - in case user of Anonymous maybeAnonymousUsername -> @@ -544,7 +556,7 @@ updateReadyModel msg model = -- we already have the username in both memory and cached in -- localstorage ( newModel, reporterMsg ) - + -- In the event that the user is not logged in -- AND we don't yet have a anonymous username for them -- grab the server-generated username from the new comment and @@ -553,12 +565,11 @@ updateReadyModel msg model = -- - localstorage (for future sessions) Nothing -> ( { newModel - | user = + | user = Just newComment.fallbackAnonUsername - |> Anonymous - |> RemoteUser.UserLoaded + |> Anonymous + |> RemoteUser.UserLoaded } - , Cmd.batch [ Utils.writeToLocalStorage ( "anonymousUsername" @@ -567,19 +578,18 @@ updateReadyModel msg model = , reporterMsg ] ) - + Authenticated _ _ _ -> ( newModel, reporterMsg ) CommentChanged comment -> Utils.simpleUpdate - { model | commentTree = - mapSimpleWebData - (Comment.setComment comment) - model.commentTree + { model + | commentTree = + mapSimpleWebData + (Comment.setComment comment) + model.commentTree } - ErrorReportSubmitted _ -> Utils.simpleUpdate model - diff --git a/src/elm/UI/AppShell.elm b/src/elm/UI/AppShell.elm index 6612b19..af91dd5 100644 --- a/src/elm/UI/AppShell.elm +++ b/src/elm/UI/AppShell.elm @@ -5,8 +5,8 @@ import Css exposing (..) import Css.Media as Media exposing (withMedia) import Css.ModernNormalize as NormalizeCss import Html exposing (Html) +import Html.Styled as Styled exposing (fromUnstyled, toUnstyled) import Html.Styled.Attributes as Attr exposing (css) -import Html.Styled as Styled exposing (toUnstyled, fromUnstyled) type alias MediaQueries = @@ -38,7 +38,6 @@ mediumMediaQueries = ] - largeMediaQueries : Style largeMediaQueries = withMedia [ Media.only Media.screen [ Media.minWidth (px 1008) ] ] @@ -46,7 +45,6 @@ largeMediaQueries = ] - mediaQueries : MediaQueries mediaQueries = { extraSmall = extraSmallMediaQueries @@ -56,7 +54,7 @@ mediaQueries = } -appShell : Styled.Html msg -> Html msg +appShell : Styled.Html msg -> Html msg appShell contents = toUnstyled <| Styled.div @@ -75,5 +73,3 @@ appShell contents = , fromUnstyled Ant.Css.defaultStyles , contents ] - - diff --git a/src/elm/UI/AuthenticationInfo.elm b/src/elm/UI/AuthenticationInfo.elm index 8b9c9a0..f23a053 100644 --- a/src/elm/UI/AuthenticationInfo.elm +++ b/src/elm/UI/AuthenticationInfo.elm @@ -1,25 +1,27 @@ module UI.AuthenticationInfo exposing - ( viewAuthenticationInfo - , logInForm + ( AuthenticationRequest(..) , LogInValues - , signUpForm , SignUpValues - , AuthenticationRequest(..) + , logInForm + , signUpForm + , viewAuthenticationInfo ) import Ant.Form as Form exposing (Form) import Ant.Form.PasswordField exposing (PasswordFieldValue) import Ant.Theme as AntTheme +import Color.Convert exposing (colorToHexWithAlpha) import Css exposing (..) import Data exposing (User(..)) import Data.RemoteUser as RemoteUser exposing (RemoteUser) import Html.Styled as Html exposing (..) import Html.Styled.Attributes exposing (css) import Html.Styled.Events exposing (onClick) -import Color.Convert exposing (colorToHexWithAlpha) -type AuthenticationRequest = SignUp | LogIn +type AuthenticationRequest + = SignUp + | LogIn type alias LogInValues = @@ -27,6 +29,7 @@ type alias LogInValues = , password : PasswordFieldValue } + type alias SignUpValues = { username : String , email : String @@ -38,7 +41,7 @@ type alias SignUpValues = logInForm : (String -> String -> msg) -> Form LogInValues msg logInForm tagger = let - usernameOrEmailField = + usernameOrEmailField = Form.inputField { parser = Ok , value = .usernameOrEmail @@ -50,7 +53,7 @@ logInForm tagger = } } - passwordField = + passwordField = Form.passwordField { parser = \{ value } -> Ok value , value = .password @@ -63,14 +66,14 @@ logInForm tagger = } in Form.succeed tagger - |> Form.append usernameOrEmailField - |> Form.append passwordField + |> Form.append usernameOrEmailField + |> Form.append passwordField signUpForm : (String -> String -> String -> msg) -> Form SignUpValues msg signUpForm tagger = let - usernameField = + usernameField = Form.inputField { parser = Ok , value = .username @@ -82,7 +85,7 @@ signUpForm tagger = } } - emailField = + emailField = Form.inputField { parser = Ok , value = .email @@ -94,7 +97,7 @@ signUpForm tagger = } } - passwordField = + passwordField = Form.passwordField { parser = \{ value } -> Ok value , value = .password @@ -106,8 +109,7 @@ signUpForm tagger = } } - - passwordConfirmField = + passwordConfirmField = Form.meta (\{ password } -> Form.passwordField @@ -115,6 +117,7 @@ signUpForm tagger = \{ value } -> if value == password.value then Ok value + else Err "Passwords do not match" , value = .passwordConfirm @@ -128,16 +131,13 @@ signUpForm tagger = ) in Form.succeed tagger - |> Form.append usernameField - |> Form.append emailField - |> Form.append - (Form.succeed (\password _ -> password) - |> Form.append passwordField - |> Form.append passwordConfirmField - ) - - - + |> Form.append usernameField + |> Form.append emailField + |> Form.append + (Form.succeed (\password _ -> password) + |> Form.append passwordField + |> Form.append passwordConfirmField + ) viewAuthenticationInfo : RemoteUser -> (AuthenticationRequest -> msg) -> Html msg @@ -161,10 +161,10 @@ viewAuthenticationInfo remoteUser tagger = textColor = colorToHexWithAlpha AntTheme.defaultTheme.typography.secondaryTextColor - |> hex - |> color + |> hex + |> color - authenticationPrompt = + authenticationPrompt = case remoteUser of RemoteUser.UserLoaded user -> case user of @@ -187,4 +187,3 @@ viewAuthenticationInfo remoteUser tagger = div [ css [ textColor ] ] authenticationPrompt in Html.map tagger contents - diff --git a/src/elm/UI/Comment.elm b/src/elm/UI/Comment.elm index c1f845c..a40aadb 100644 --- a/src/elm/UI/Comment.elm +++ b/src/elm/UI/Comment.elm @@ -3,12 +3,12 @@ module UI.Comment exposing (viewCommentsSection) {-| UI modules for rendering a tree of comments. -} -import Ant.Button as Btn exposing (button, Button) -import Ant.Typography.Text as Text exposing (Text, text) +import Ant.Button as Btn exposing (Button, button) import Ant.Icons as Icon exposing (Icon, downOutlined, upOutlined) +import Ant.Typography.Text as Text exposing (Text, text) import Css exposing (..) -import Data exposing (User(..), Interactions, VoteType(..)) -import Data.Comment as Comment exposing (Comment, CommentTree, CommentMap) +import Data exposing (Interactions, User(..), VoteType(..)) +import Data.Comment as Comment exposing (Comment, CommentMap, CommentTree) import Data.Cuid exposing (Cuid) import Dict import Html.Styled as S exposing (fromUnstyled) @@ -17,13 +17,17 @@ import Html.Styled.Events exposing (onClick) import RemoteData exposing (WebData) import Set exposing (Set) import Time -import UI.TextArea as TextArea +import UI.TextArea as TextArea import Utils +type alias StyledHtml a = + S.Html a + + +type alias TimeFormatter = + Time.Posix -> String -type alias StyledHtml a = S.Html a -type alias TimeFormatter = Time.Posix -> String type alias Effects msg = { loadRepliesForComment : Cuid -> msg @@ -35,78 +39,85 @@ type alias Effects msg = type CommentPointers msg = Simple (Set Cuid) - | Async + | Async -- loaded ids ( Set Cuid - -- buffer state + -- buffer state , WebData () - -- "load more" action + -- "load more" action , msg ) - renderText : Text -> StyledHtml msg -renderText = Text.toHtml >> fromUnstyled +renderText = + Text.toHtml >> fromUnstyled strongText : String -> StyledHtml msg strongText val = text val - |> Text.strong - |> renderText + |> Text.strong + |> renderText primaryText : String -> StyledHtml msg primaryText val = text val - |> Text.withType Text.Primary - |> renderText - + |> Text.withType Text.Primary + |> renderText secondaryText : String -> StyledHtml msg secondaryText val = text val - |> Text.withType Text.Secondary - |> renderText - + |> Text.withType Text.Secondary + |> renderText link : String -> Button msg link val = button val - |> Btn.withType Btn.Link + |> Btn.withType Btn.Link + -- Color of vote icons depends on the interaction that the user -- has had on a particular comment -- Either they have a (+1) upvote or a (-1) downvote + + renderVoteIcon : Effects msg -> Comment -> Maybe Interactions -> VoteType -> Icon msg -> StyledHtml msg renderVoteIcon { submitCommentVote } comment maybeInteractions vote icon = let - defaultColor = ( "color", "#737373" ) - chosenVoteColor = ( "color", "#4ba9ff" ) - + defaultColor = + ( "color", "#737373" ) + + chosenVoteColor = + ( "color", "#4ba9ff" ) + voteIconColor = case maybeInteractions of - Nothing -> defaultColor + Nothing -> + defaultColor Just interactions -> -- this represents the logged-in users -- vote for this comment interactions.commentVotes - |> Dict.get comment.id - |> Maybe.map - (\{ value } -> - if value == 1 && vote == Up then - chosenVoteColor - else if value == -1 && vote == Down then - chosenVoteColor - else - defaultColor - ) - |> Maybe.withDefault defaultColor + |> Dict.get comment.id + |> Maybe.map + (\{ value } -> + if value == 1 && vote == Up then + chosenVoteColor + + else if value == -1 && vote == Down then + chosenVoteColor + + else + defaultColor + ) + |> Maybe.withDefault defaultColor iconHtml = icon @@ -121,7 +132,6 @@ renderVoteIcon { submitCommentVote } comment maybeInteractions vote icon = [ iconHtml ] - commentActionButton : String -> msg -> StyledHtml msg commentActionButton value msg = S.button @@ -138,18 +148,18 @@ commentActionButton value msg = [ S.text value ] - - replyTextarea : Comment -> Effects msg -> StyledHtml msg replyTextarea comment effects = let - ( textAreaVisible, textAreaValue ) = comment.textAreaState + ( textAreaVisible, textAreaValue ) = + comment.textAreaState updateTextArea = \val -> effects.updateComment - { comment | textAreaState = - Tuple.mapSecond (always val) comment.textAreaState + { comment + | textAreaState = + Tuple.mapSecond (always val) comment.textAreaState } textAreaAction = @@ -157,15 +167,17 @@ replyTextarea comment effects = textArea = TextArea.replyTextArea comment updateTextArea - |> TextArea.toHtml textAreaAction + |> TextArea.toHtml textAreaAction in if textAreaVisible then textArea - else + + else S.text "" -{-| +{-| + @arg depth current depth of tree: From 0 -> n @@ -183,21 +195,23 @@ replyTextarea comment effects = @arg commentMap a flattened hashmap that represents a recursive tree of comments (i.e. just like Reddit) + -} -viewComments - : Int - -> Effects msg - -> TimeFormatter - -> CommentPointers msg - -> CommentMap - -> Maybe Interactions - -> StyledHtml msg +viewComments : + Int + -> Effects msg + -> TimeFormatter + -> CommentPointers msg + -> CommentMap + -> Maybe Interactions + -> StyledHtml msg viewComments depth effects formatter pointers commentMap maybeInteractions = let viewComments_ : Set Cuid -> StyledHtml msg viewComments_ pointerSet = if Set.isEmpty pointerSet then S.text "" + else let comments = @@ -210,6 +224,7 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = [ borderLeft3 (px 1) dotted (hex "#4ba9ff") ] ] + else [ border zero ] in @@ -218,7 +233,6 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = ] (List.map viewSingleComment comments) - -- this is embedded inside of view comments to -- take advantage of closures viewSingleComment : Comment -> StyledHtml msg @@ -228,9 +242,9 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = S.span [ css [ marginRight (px 10) ] ] [ strongText <| Utils.getAuthorName comment ] - + replyInfo = - Async + Async ( comment.replyIds , comment.remoteReplyBuffer , effects.loadRepliesForComment comment.id @@ -240,13 +254,13 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = let update = effects.updateComment - { comment | textAreaState = - Tuple.mapFirst not comment.textAreaState + { comment + | textAreaState = + Tuple.mapFirst not comment.textAreaState } in commentActionButton "reply" update - -- Includes the folding icon -- and upvote / downvote icons viewCommentSidebar = @@ -261,6 +275,7 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = commentFoldingText = if comment.isFolded then "[+]" + else "[-]" @@ -274,7 +289,8 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = ] updatedComment = - { comment | isFolded = not comment.isFolded + { comment + | isFolded = not comment.isFolded } commentFoldingIcon = @@ -287,10 +303,10 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = ] [ S.text commentFoldingText ] - votingIcons = if comment.isFolded then S.text "" + else let renderVoteIcon_ = @@ -306,7 +322,6 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = [ renderVoteIcon_ Up upOutlined , renderVoteIcon_ Down downOutlined ] - in S.div [ css sidebarStyles ] [ commentFoldingIcon @@ -317,6 +332,7 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = if comment.isFolded then [ S.div [ css [ marginBottom (px 10) ] ] [] ] + else [ S.div [ css [ marginBottom (px 15) ] ] [ S.div [ css [ overflowWrap breakWord ] ] [ primaryText comment.body ] @@ -330,8 +346,10 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = [ viewCommentSidebar , S.div [ css [ width (pct 100) ] ] ([ authorName - , secondaryText <| formatter comment.createdAt - ] ++ maybeViewComment) + , secondaryText <| formatter comment.createdAt + ] + ++ maybeViewComment + ) ] in case pointers of @@ -344,9 +362,9 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = let loadMoreBtn = link "load more comments" - |> Btn.onClick fetchReplies - |> Btn.toHtml - |> fromUnstyled + |> Btn.onClick fetchReplies + |> Btn.toHtml + |> fromUnstyled in S.div [] [ viewComments_ pointerSet @@ -360,31 +378,34 @@ viewComments depth effects formatter pointers commentMap maybeInteractions = [ secondaryText "loading ..." ] ] - RemoteData.Failure error -> + RemoteData.Failure error -> S.div [] [ viewComments_ pointerSet , S.text "error loading more comments" ] RemoteData.Success _ -> - viewComments_ pointerSet - + viewComments_ pointerSet -viewCommentsSection - : Effects msg +viewCommentsSection : + Effects msg -> TimeFormatter -> CommentTree -> User -> StyledHtml msg viewCommentsSection effects formatter { topLevelComments, comments } user = let - rootDepth = 0 + rootDepth = + 0 maybeInteractions = case user of - Authenticated _ interactions _ -> Just interactions - Anonymous _ -> Nothing + Authenticated _ interactions _ -> + Just interactions + + Anonymous _ -> + Nothing in S.div [ css [ marginLeft (px -10) ] ] @@ -396,4 +417,3 @@ viewCommentsSection effects formatter { topLevelComments, comments } user = comments maybeInteractions ] - diff --git a/src/elm/UI/TextArea.elm b/src/elm/UI/TextArea.elm index 80d4532..0d37366 100644 --- a/src/elm/UI/TextArea.elm +++ b/src/elm/UI/TextArea.elm @@ -1,8 +1,7 @@ -module UI.TextArea exposing (replyTextArea, topLevelTextArea, toHtml) - +module UI.TextArea exposing (replyTextArea, toHtml, topLevelTextArea) import Ant.Button as Btn exposing (button) -import Ant.Input as Input exposing (input, Input) +import Ant.Input as Input exposing (Input, input) import Css exposing (..) import Data.Comment exposing (Comment) import Html.Styled as S exposing (Html, fromUnstyled) @@ -10,21 +9,22 @@ import Html.Styled.Attributes exposing (css) import Utils -type TextArea msg = TextArea (Input msg, String, Maybe (Html msg)) +type TextArea msg + = TextArea ( Input msg, String, Maybe (Html msg) ) -replyTextArea : Comment -> ( String -> msg ) -> TextArea msg +replyTextArea : Comment -> (String -> msg) -> TextArea msg replyTextArea comment updateTextArea = let - textAreaInput = + textAreaInput = input updateTextArea - |> Input.withTextAreaType { rows = 4 } - |> Input.withPlaceholder ("respond to " ++ (Utils.getAuthorName comment)) + |> Input.withTextAreaType { rows = 4 } + |> Input.withPlaceholder ("respond to " ++ Utils.getAuthorName comment) in - TextArea (textAreaInput, Tuple.second comment.textAreaState, Nothing) + TextArea ( textAreaInput, Tuple.second comment.textAreaState, Nothing ) -topLevelTextArea : ( String -> msg ) -> String -> Html msg -> TextArea msg +topLevelTextArea : (String -> msg) -> String -> Html msg -> TextArea msg topLevelTextArea updateTextArea textAreaValue extraInfo = let textAreaInput = @@ -32,21 +32,23 @@ topLevelTextArea updateTextArea textAreaValue extraInfo = |> Input.withTextAreaType { rows = 5 } |> Input.withPlaceholder "What are your thoughts?" in - TextArea (textAreaInput, textAreaValue, Just extraInfo) - + TextArea ( textAreaInput, textAreaValue, Just extraInfo ) toHtml : Maybe msg -> TextArea msg -> Html msg -toHtml maybeSubmit (TextArea (input, value, maybeExtraInfo)) = +toHtml maybeSubmit (TextArea ( input, value, maybeExtraInfo )) = let htmlTextArea = Input.toHtml value input - |> fromUnstyled + |> fromUnstyled noActionAvailable = case maybeSubmit of - Just _ -> False - Nothing -> True + Just _ -> + False + + Nothing -> + True maybeBindOnClick btn = case maybeSubmit of @@ -58,20 +60,22 @@ toHtml maybeSubmit (TextArea (input, value, maybeExtraInfo)) = submitCommentButton = button "Add Comment" - |> Btn.disabled (String.length value == 0 || noActionAvailable) - |> maybeBindOnClick - |> Btn.toHtml - |> fromUnstyled + |> Btn.disabled (String.length value == 0 || noActionAvailable) + |> maybeBindOnClick + |> Btn.toHtml + |> fromUnstyled extraInfo = case maybeExtraInfo of - Just info -> info - Nothing -> S.text "" + Just info -> + info + Nothing -> + S.text "" in S.div [] [ htmlTextArea - , S.div + , S.div [ css [ displayFlex , justifyContent spaceBetween @@ -80,5 +84,3 @@ toHtml maybeSubmit (TextArea (input, value, maybeExtraInfo)) = ] [ extraInfo, submitCommentButton ] ] - - diff --git a/src/elm/Utils.elm b/src/elm/Utils.elm index 1c2de0f..a60f0d4 100644 --- a/src/elm/Utils.elm +++ b/src/elm/Utils.elm @@ -1,12 +1,12 @@ port module Utils exposing ( getAuthorName - , humanReadableTimestamp , getPathFromUrl - , removeSessionToken + , humanReadableTimestamp , openInNewTab - , writeToLocalStorage + , removeSessionToken , simpleUpdate , timeDiff + , writeToLocalStorage ) import Data exposing (Author(..)) @@ -15,11 +15,9 @@ import Time exposing (Posix, posixToMillis) import Url exposing (Url) - - - simpleUpdate : m -> ( m, Cmd msg ) -simpleUpdate m = ( m, Cmd.none ) +simpleUpdate m = + ( m, Cmd.none ) getAuthorName : Comment -> String @@ -32,31 +30,35 @@ getAuthorName { author } = userInfo.username + --- Ports +-- ( String = key, String = value ) --- ( String = key, String = value ) port writeToLocalStorage : ( String, String ) -> Cmd msg + + port removeSessionToken : () -> Cmd msg -port openInNewTab : String -> Cmd msg +port openInNewTab : String -> Cmd msg --- URL Utils + getPathFromUrl : Url -> String getPathFromUrl url = if url.path == "/" then "root" + else url.path - ---- Time Stuff +--- Time Stuff timeDiff : Posix -> Posix -> Int @@ -65,83 +67,103 @@ timeDiff first second = --- Constants +-- Constants + one_minute : Int -one_minute = 1000 * 60 +one_minute = + 1000 * 60 + one_hour : Int -one_hour = one_minute * 60 +one_hour = + one_minute * 60 + one_day : Int -one_day = one_hour * 24 +one_day = + one_hour * 24 + one_week : Int -one_week = one_day * 7 +one_week = + one_day * 7 + one_month : Int -one_month = one_day * 30 +one_month = + one_day * 30 + one_year : Int -one_year = one_month * 12 +one_year = + one_month * 12 --- Conversions + msToSec : Int -> Int -msToSec val = +msToSec val = val // 1000 + msToMin : Int -> Int msToMin val = msToSec val // 60 + msToHours : Int -> Int msToHours val = msToMin val // 60 + msToDays : Int -> Int msToDays val = msToHours val // 24 + msToWeeks : Int -> Int msToWeeks val = msToDays val // 7 + msToMonths : Int -> Int msToMonths val = msToDays val // 30 + msToYears : Int -> Int msToYears val = msToMonths val // 12 - - - - maybePluralizeLabel : Int -> String -> String maybePluralizeLabel value label = let - valStr = String.fromInt value + valStr = + String.fromInt value in if value == 1 then valStr ++ " " ++ label ++ " ago" + else valStr ++ " " ++ label ++ "s ago" - + humanReadableTimestamp : Posix -> Posix -> String humanReadableTimestamp reference date = let - differenceMs = timeDiff reference date + differenceMs = + timeDiff reference date in if differenceMs < 0 then "" + else if differenceMs < 1000 then "just now" + else if differenceMs < one_minute then maybePluralizeLabel (msToSec differenceMs) "second" @@ -158,8 +180,7 @@ humanReadableTimestamp reference date = maybePluralizeLabel (msToWeeks differenceMs) "week" else if differenceMs < one_year then - maybePluralizeLabel (msToMonths differenceMs) "month" + maybePluralizeLabel (msToMonths differenceMs) "month" else maybePluralizeLabel (msToYears differenceMs) "year" -