diff --git a/package-lock.json b/package-lock.json index 6463ada39fd86f4a088ae8726c205a054b7c90cf..a5d09a513ce24f8fbb0dba8bc7923b341a5fc223 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1624,12 +1624,6 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -1746,16 +1740,6 @@ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2772,17 +2756,6 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000971.tgz", "integrity": "sha512-TQFYFhRS0O5rdsmSbF1Wn+16latXYsQJat66f7S7lizXW1PVpWJeZw9wqqVLIjuxDRz7s7xRUj13QCfd8hKn6g==" }, - "canvas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.0.tgz", - "integrity": "sha512-bEO9f1ThmbknLPxCa8Es7obPlN9W3stB1bo7njlhOFKIdUTldeTqXCh9YclCPAi2pSQs84XA0jq/QEZXSzgyMw==", - "optional": true, - "requires": { - "nan": "^2.14.0", - "node-pre-gyp": "^0.11.0", - "simple-get": "^3.0.3" - } - }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -3600,12 +3573,6 @@ "date-now": "^0.1.4" } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true - }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -4081,26 +4048,11 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "optional": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4204,12 +4156,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -4229,12 +4175,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true - }, "detect-newline": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", @@ -5352,82 +5292,6 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, - "fabric": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-3.3.2.tgz", - "integrity": "sha512-xUbSmv3KzmmXgSTqHZbg33YMiIm3ZGlWvDgqEqdTBMSrrQd8V9t596ra7lIgr8rNh3HhxBDu7rZJ+R0t6FNFAg==", - "requires": { - "canvas": "^2.6.0", - "jsdom": "^15.1.0" - }, - "dependencies": { - "jsdom": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.1.1.tgz", - "integrity": "sha512-cQZRBB33arrDAeCrAEWn1U3SvrvC8XysBua9Oqg1yWrsY/gYcusloJC3RZJXuY5eehSCmws8f2YeliCqGSkrtQ==", - "optional": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^6.1.1", - "acorn-globals": "^4.3.2", - "array-equal": "^1.0.0", - "cssom": "^0.3.6", - "cssstyle": "^1.2.2", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.1", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.1.4", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "saxes": "^3.1.9", - "symbol-tree": "^3.2.2", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.1.2", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^7.0.0", - "xml-name-validator": "^3.0.0" - } - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "optional": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "optional": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "ws": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.0.tgz", - "integrity": "sha512-Swie2C4fs7CkwlHu1glMePLYJJsWjzhl1vm3ZaLplD0h7OMkZyZ6kLTB/OagiU923bZrPFXuDTeEqaEN4NWG4g==", - "optional": true, - "requires": { - "async-limiter": "^1.0.0" - } - } - } - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -5709,15 +5573,6 @@ "universalify": "^0.1.0" } }, - "fs-minipass": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", - "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -5750,59 +5605,6 @@ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -6046,12 +5848,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -6378,15 +6174,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, "immer": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", @@ -8488,12 +8275,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "optional": true - }, "mini-create-react-context": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz", @@ -8537,25 +8318,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, - "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -8696,28 +8458,6 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" }, - "needle": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", - "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "optional": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -8812,32 +8552,6 @@ } } }, - "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "optional": true - } - } - }, "node-releases": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.21.tgz", @@ -8853,16 +8567,6 @@ } } }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -8899,22 +8603,6 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "optional": true - }, - "npm-packlist": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", - "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -8923,18 +8611,6 @@ "path-key": "^2.0.0" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -9159,12 +8835,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "optional": true - }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -9180,16 +8850,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -10597,18 +10257,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "react": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", @@ -11651,23 +11299,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, - "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "optional": true - }, - "simple-get": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.0.3.tgz", - "integrity": "sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw==", - "optional": true, - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -12342,21 +11973,6 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, - "tar": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, "terser": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", @@ -13279,15 +12895,6 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/src/App.css b/src/App.css index bc9556dc36b07c79980b0602f863690d34a151e9..fe44e37477b045f8d82ed74f1ef0658da3cbacd1 100644 --- a/src/App.css +++ b/src/App.css @@ -13,7 +13,7 @@ body { /* UserMap */ .map { position: absolute; - margin-top: 50px; + /* margin-top: 50px; */ height: 95vh; width: 100vw; } @@ -214,6 +214,8 @@ div.login button:hover { .gamelist { border: 2px solid white; max-width: 800px; + max-height: 500px; + overflow: scroll; } .gamelist-item { @@ -238,6 +240,20 @@ div.login button:hover { flex-direction: column; } +.notification-popup { + position: absolute; + z-index: 1010; +} + +.notification-popup.warning { + background-color: yellow; + color: black; +} + +.notification-popup.alert { + background-color: red; +} + .leaflet-control-playback { position: relative; background-color: #7cbdf5; diff --git a/src/App.js b/src/App.js index b1752f70d4e09e5afc57d112005bc247f8d70d8e..c0fb6be1e2302e9f6f186b126553e0afe4508a78 100644 --- a/src/App.js +++ b/src/App.js @@ -1,8 +1,6 @@ import React, { Component } from "react"; import "../node_modules/leaflet-draw/dist/leaflet.draw.css"; import "./App.css"; - -import ClientSocket from "./components/Socket"; import { BrowserRouter as Router, Route, diff --git a/src/components/DrawTools.js b/src/components/DrawTools.js index be1b7b35dedab72665c5c3221072c5781656b027..8040ce5920a3f990eebb6ce94081538103d2a6b7 100644 --- a/src/components/DrawTools.js +++ b/src/components/DrawTools.js @@ -246,51 +246,54 @@ class DrawTools extends Component { // The elements you draw will be added to this FeatureGroup layer, // when you hit edit button only items in this layer will be edited." <FeatureGroup> - <EditControl - position="topright" - onCreated={this._onCreated} - onEdited={this._onEdited} - onEditStart={this._onEditDeleteStart} - onEditStop={this._onEditDeleteStop} - onDeleted={this._onDeleted} - onDeleteStart={this._onEditDeleteStart} - onDeleteStop={this._onEditDeleteStop} - draw={{ - circle: { - repeatMode: false, // allows using the tool again after finishing the previous shape - shapeOptions: { - color: "#f9f10c", - opacity: 1 // affects the outline only. for some reason it wasn't at full opacity, so this is needed for more clarity - } - }, - rectangle: { - repeatMode: false - }, - polygon: { - repeatMode: true, - allowIntersection: false, // Restricts shapes to simple polygons - drawError: { - color: "#e1e100", // Color the shape will turn when intersects - message: "<strong>Oh snap!<strong> you can't draw that!" // Message that will show when intersect + {(this.props.role === "admin" || + this.props.role === "factionleader") && ( + <EditControl + position="topright" + onCreated={this._onCreated} + onEdited={this._onEdited} + onEditStart={this._onEditDeleteStart} + onEditStop={this._onEditDeleteStop} + onDeleted={this._onDeleted} + onDeleteStart={this._onEditDeleteStart} + onDeleteStop={this._onEditDeleteStop} + draw={{ + circle: { + repeatMode: false, // allows using the tool again after finishing the previous shape + shapeOptions: { + color: "#f9f10c", + opacity: 1 // affects the outline only. for some reason it wasn't at full opacity, so this is needed for more clarity + } }, - shapeOptions: { - color: "#ed2572", - opacity: 1 - } - }, - polyline: { - repeatMode: true, - shapeOptions: { - color: "#ed2572", - opacity: 1 - } - }, - marker: { - repeatMode: false - }, - circlemarker: false - }} - /> + rectangle: { + repeatMode: false + }, + polygon: { + repeatMode: true, + allowIntersection: false, // Restricts shapes to simple polygons + drawError: { + color: "#e1e100", // Color the shape will turn when intersects + message: "<strong>Oh snap!<strong> you can't draw that!" // Message that will show when intersect + }, + shapeOptions: { + color: "#ed2572", + opacity: 1 + } + }, + polyline: { + repeatMode: true, + shapeOptions: { + color: "#ed2572", + opacity: 1 + } + }, + marker: { + repeatMode: false + }, + circlemarker: false + }} + /> + )} {/* iterate through every element fetched from back-end */} {this.props.geoJSONLayer.features.map(feature => { diff --git a/src/components/EditGameForm.js b/src/components/EditGameForm.js index c0fc662e0442d42c9a4ac6df49ef3b9fc943bc6b..65b5d7f38bb7f1664401f316f2808cf909eff421 100644 --- a/src/components/EditGameForm.js +++ b/src/components/EditGameForm.js @@ -30,19 +30,14 @@ export default class EditGameForm extends React.Component { objectivePoints: [], capture_time: 300, confirmation_time: 60, - displayColorPicker: false + displayColorPicker: false, + saved: true }; - - this.handleMapDrag = this.handleMapDrag.bind(this); } - handleError = error => { - this.setState({ errorMsg: error }); - }; - handleChange = e => { const { name, value } = e.target; - this.setState({ [name]: value }); + this.setState({ [name]: value, saved: false }); }; handleFactionAdd = e => { @@ -185,7 +180,12 @@ export default class EditGameForm extends React.Component { // show/hide this form handleView = e => { - this.props.toggleView(this.props.view); + if ( + this.state.saved || + window.confirm("Are you sure you want to leave without saving?") + ) { + this.props.toggleView(this.props.view); + } }; // remove view with ESC @@ -195,17 +195,7 @@ export default class EditGameForm extends React.Component { } }; - handleMapDrag = e => { - this.setState({ - mapCenter: e.target.getCenter() - }); - }; - - handleMapScroll = e => { - this.setState({ - zoom: e.target.getZoom() - }); - }; + handleMapScroll = e => {}; handleGameSave = e => { e.preventDefault(); @@ -248,6 +238,7 @@ export default class EditGameForm extends React.Component { } let token = sessionStorage.getItem("token"); + let error = false; // Send Game info to the server fetch(`${process.env.REACT_APP_API_URL}/game/edit/${this.props.gameId}`, { @@ -261,15 +252,23 @@ export default class EditGameForm extends React.Component { }) .then(res => { if (!res.ok) { - throw Error(res.statusMessage); - } else { - return res.json(); + error = true; } + return res.json(); }) .then(result => { alert(result.message); - this.props.onEditSave(); - this.handleView(); + if (!error) { + this.setState( + { + saved: true + }, + () => { + this.handleView(); + this.props.onEditSave(); + } + ); + } }) .catch(error => console.log("Error: ", error)); }; @@ -446,7 +445,7 @@ export default class EditGameForm extends React.Component { onSubmit={this.handleObjectivePointAdd} /> - <h1>Demo Game Editor</h1> + <h1>Game Editor</h1> <br /> <input placeholder="Game name" @@ -515,10 +514,10 @@ export default class EditGameForm extends React.Component { /> <br /> <br /> - <label>Factions</label> <br /> <input + id="editGameFactionNameInput" name="factionNameInput" value={this.state.factionNameInput} minLength="2" @@ -527,6 +526,7 @@ export default class EditGameForm extends React.Component { form="factionAddFrom" /> <input + id="editGameFactionPasswordInput" name="factionPasswordInput" value={this.state.factionPasswordInput} minLength="3" @@ -536,6 +536,7 @@ export default class EditGameForm extends React.Component { form="factionAddFrom" /> <div + id="editGameColorPickerButton" style={styles.swatch} onClick={() => this.setState({ @@ -547,6 +548,7 @@ export default class EditGameForm extends React.Component { </div> {this.state.displayColorPicker && ( <div + id="editGameColorPicker" style={styles.cover} onClick={() => this.setState({ displayColorPicker: false })} > @@ -558,7 +560,11 @@ export default class EditGameForm extends React.Component { /> </div> )} - <button type="submit" form="factionAddFrom"> + <button + id="editGameFactionSubmitButton" + type="submit" + form="factionAddFrom" + > Add </button> <ul>{factions}</ul> @@ -567,6 +573,7 @@ export default class EditGameForm extends React.Component { <label>Objective points</label> <br /> <input + id="editGameObjectivePointDescriptionInput" name="objectivePointDescriptionInput" type="number" value={this.state.objectivePointDescriptionInput} @@ -576,6 +583,7 @@ export default class EditGameForm extends React.Component { form="objectivePointAddFrom" /> <input + id="editGameObjectivePointMultiplierInput" name="objectivePointMultiplierInput" type="number" value={this.state.objectivePointMultiplierInput} @@ -583,7 +591,11 @@ export default class EditGameForm extends React.Component { placeholder="Objective point multiplier" form="objectivePointAddFrom" /> - <button type="submit" form="objectivePointAddFrom"> + <button + id="editGameObjectivePointSubmitButton" + type="submit" + form="objectivePointAddFrom" + > Add </button> <ul>{objectivePoints}</ul> @@ -596,6 +608,7 @@ export default class EditGameForm extends React.Component { Capture time: </label> <input + id="editGameCaptureTimeInput" name="capture_time" type="number" value={this.state.capture_time} @@ -604,6 +617,7 @@ export default class EditGameForm extends React.Component { /> <label className="">Confimation time:</label> <input + id="editGameConfirmationTimeInput" name="confirmation_time" type="number" value={this.state.confirmation_time} @@ -621,8 +635,8 @@ export default class EditGameForm extends React.Component { zoom={this.state.zoom} maxZoom="13" style={{ height: "400px", width: "400px" }} - onmoveend={this.handleMapDrag} - onzoomend={this.handleMapScroll} + onmoveend={e => this.setState({ mapCenter: e.target.getCenter() })} + onzoomend={e => this.setState({ zoom: e.target.getZoom() })} > <TileLayer attribution="Maanmittauslaitoksen kartta" @@ -631,6 +645,7 @@ export default class EditGameForm extends React.Component { </Map> <br /> <button + id="editGameDeleteGameButton" style={{ backgroundColor: "red" }} type="submit" form="gameDeletionForm" diff --git a/src/components/GameInfoView.js b/src/components/GameInfoView.js new file mode 100644 index 0000000000000000000000000000000000000000..dc961d61a855e0bccf28c2a5a82c335817de66a4 --- /dev/null +++ b/src/components/GameInfoView.js @@ -0,0 +1,70 @@ +import React from "react"; +import { Map, TileLayer } from "react-leaflet"; + +export default class GameInfoView extends React.Component { + componentDidMount() { + document.addEventListener("keyup", this.handleEsc); + } + + componentWillUnmount() { + document.removeEventListener("keyup", this.handleEsc); + } + + handleEsc = e => { + if (e.keyCode === 27) { + this.props.toggleView(); + } + }; + + render() { + if (this.props.gameInfo === undefined) { + return false; + } + return ( + <div className="fade-main"> + <div className="sticky"> + <span + id="closeGameInfoX" + className="close" + onClick={this.props.toggleView} + > + × + </span> + </div> + <div className=""> + <h1>Game Info</h1> + <p>Game name: {this.props.gameInfo.name}</p> + <p>Description: {this.props.gameInfo.desc}</p> + <p>Start date: {this.props.gameInfo.startdate}</p> + <p>End date: {this.props.gameInfo.enddate}</p> + <h2>Factions</h2> + <div> + {this.props.gameInfo.factions.map(faction => ( + <p key={faction.factionId} style={{ color: faction.colour }}> + {faction.factionName} + </p> + ))} + </div> + <div> + <Map + id="gameInfoCenterMap" + className="" + center={[ + this.props.gameInfo.center.lat, + this.props.gameInfo.center.lng + ]} + zoom="13" + maxZoom="13" + style={{ height: "400px", width: "400px" }} + > + <TileLayer + attribution="Maanmittauslaitoksen kartta" + url=" https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg" + /> + </Map> + </div> + </div> + </div> + ); + } +} diff --git a/src/components/GameList.js b/src/components/GameList.js index 4796e8a1c3dfe575f9efded6b02c2017ece1e8eb..c7df36e8ae8147d2e051ac1976101689f208abee 100644 --- a/src/components/GameList.js +++ b/src/components/GameList.js @@ -35,8 +35,6 @@ class GameList extends React.Component { ? games[0].id : undefined }); - // taking the initialized gameID to App.js (GameList.js -> GameSidebar.js -> Header.js -> App.js) - this.props.handleGameChange(games[0].id); }) .catch(error => { console.log(error); @@ -89,10 +87,7 @@ class GameList extends React.Component { )); return ( - <div - className="gamelist" - style={{ maxHeight: "500px", overflow: "scroll" }} - > + <div className="gamelist"> <div className="gamelist-item">{gamelistItems}</div> </div> ); diff --git a/src/components/GameStateButtons.js b/src/components/GameStateButtons.js index c770ed7896facdddaf56adecde4c689d10dbeb3f..946f9e5b14003d7ad793f08bc9a8f67da8196dea 100644 --- a/src/components/GameStateButtons.js +++ b/src/components/GameStateButtons.js @@ -6,53 +6,71 @@ export default class GameStateButtons extends React.Component { }; setGameState(state) { - console.log(state); - let token = sessionStorage.getItem("token"); - let error = false; - fetch( - `${process.env.REACT_APP_API_URL}/game/edit-state/${this.props.gameId}`, - { - method: "PUT", - headers: { - Authorization: "Bearer " + token, - Accept: "application/json", - "Content-Type": "application/json" - }, - body: JSON.stringify({ - id: this.props.gameId, - state: state - }) - } - ) - .then(res => { - if (!res.ok) { - error = true; - } - return res.json(); - }) - .then(res => { - if (error) { - console.log(res); - } else { - alert(`Game state changed to ${state}`); - this.setState({ gameState: state }); + if ( + window.confirm(`Are you sure you want to change game state to ${state}?`) + ) { + let token = sessionStorage.getItem("token"); + let error = false; + fetch( + `${process.env.REACT_APP_API_URL}/game/edit-state/${this.props.gameId}`, + { + method: "PUT", + headers: { + Authorization: "Bearer " + token, + Accept: "application/json", + "Content-Type": "application/json" + }, + body: JSON.stringify({ + id: this.props.gameId, + state: state + }) } - }) - .catch(error => console.log(error)); + ) + .then(res => { + if (!res.ok) { + error = true; + } + return res.json(); + }) + .then(res => { + if (error) { + console.log(res); + } else { + alert(`Game state changed to ${state}`); + this.setState({ gameState: state }); + } + }) + .catch(error => console.log(error)); + } } render() { if (this.state.gameState === "CREATED") { return ( - <button onClick={() => this.setGameState("STARTED")}>Start</button> + <button + id="gameStateStartButton" + onClick={() => this.setGameState("STARTED")} + > + Start + </button> ); } if (this.state.gameState === "STARTED") { return ( <Fragment> - <button onClick={() => this.setGameState("PAUSED")}>Pause</button> - <button onClick={() => this.setGameState("ENDED")}>Stop</button> + <button + id="gameStatePauseButton" + onClick={() => this.setGameState("PAUSED")} + > + Pause + </button> + <button + id="gameStateStopButton" + onClick={() => this.setGameState("ENDED")} + > + Stop + </button> </Fragment> ); } @@ -60,8 +78,18 @@ export default class GameStateButtons extends React.Component { if (this.state.gameState === "PAUSED") { return ( <Fragment> - <button onClick={() => this.setGameState("STARTED")}>Continue</button> - <button onClick={() => this.setGameState("ENDED")}>Stop</button> + <button + id="gameStateContinueButton" + onClick={() => this.setGameState("STARTED")} + > + Continue + </button> + <button + id="gameStateStopButton" + onClick={() => this.setGameState("ENDED")} + > + Stop + </button> </Fragment> ); } diff --git a/src/components/GameView.js b/src/components/GameView.js index a6f19ecd92759fa930d68769bd7a14535d32aba1..73f61c60cfb6160af96c32850113f711bb2f2eb7 100644 --- a/src/components/GameView.js +++ b/src/components/GameView.js @@ -8,6 +8,8 @@ import PlayerlistView from "./PlayerlistView"; import NotificationView from "./NotificationView"; import GameStateButtons from "./GameStateButtons"; import ClientSocket from "./Socket"; +import NotificationPopup from "./NotificationPopup"; +import GameInfoView from "./GameInfoView"; export default class GameView extends React.Component { state = { @@ -18,7 +20,8 @@ export default class GameView extends React.Component { lng: 25.7597186, zoom: 13, mapUrl: "https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg", - socketSignal: null + socketSignal: null, + socket: null }; componentDidMount() { @@ -79,24 +82,54 @@ export default class GameView extends React.Component { ) .then(res => { if (!res.ok) { - error = true; + throw Error(); + } else { + return res.json(); } - return res.json(); }) .then(res => { alert(res.message); this.getPlayerRole(this.state.gameInfo.id); }) - .catch(error => console.log(error)); + .catch(error => { + alert("Game not found"); + window.document.location.href = "/"; + }); + }; + + handleLeaveFaction = e => { + if (window.confirm("Are you sure you want to leave your faction?")) { + let token = sessionStorage.getItem("token"); + fetch( + `${process.env.REACT_APP_API_URL}/faction/leave/${ + this.state.gameInfo.id + }`, + { + method: "DELETE", + headers: { + Authorization: "Bearer " + token + } + } + ) + .then(res => { + if (!res.ok) { + } + return res.json(); + }) + .then(res => { + alert(res.message); + this.getPlayerRole(this.state.gameInfo.id); + }) + .catch(error => console.log(error)); + } }; // setting the socket signal automatically fires shouldComponentUpdate function where socketSignal prop is present // setting socketSignal to null immediately after to avoid multiple database fetches - getSocketSignal = type => { - console.log(type); + getSocketSignal = data => { this.setState( { - socketSignal: type + socketSignal: data }, () => { this.setState({ @@ -106,13 +139,18 @@ export default class GameView extends React.Component { ); }; + onSocketChange = newSocket => { + this.setState({ + socket: newSocket + }); + }; + render() { const initialPosition = [this.state.lat, this.state.lng]; - return ( <div> <Link to="/"> - <button>Game selection</button> + <button id="gameViewGameSelectionButton">Game selection</button> </Link> {this.state.gameInfo !== null && ( <div> @@ -120,6 +158,7 @@ export default class GameView extends React.Component { <ClientSocket gameId={this.state.gameInfo.id} getSocketSignal={this.getSocketSignal} + onSocketChange={this.onSocketChange} /> )} <div>Game Name: {this.state.gameInfo.name}</div> @@ -129,14 +168,21 @@ export default class GameView extends React.Component { {this.state.role !== "" && ( <div>Your role in this game: {this.state.role}</div> )} - {this.state.role === "admin" && ( - <button - id="editGameButton" - onClick={() => this.setState({ form: "edit" })} - > - Edit - </button> - )} + {this.state.role === "admin" && + this.state.gameInfo.state === "CREATED" && ( + <button + id="editGameButton" + onClick={() => this.setState({ form: "edit" })} + > + Edit + </button> + )} + <button + id="gameInfoButton" + onClick={() => this.setState({ form: "info" })} + > + Game Info + </button> {this.state.role === "" && ( <button id="joinGameButton" @@ -163,6 +209,7 @@ export default class GameView extends React.Component { <TaskListButton gameId={this.state.gameInfo.id} role={this.state.role} + factions={this.state.gameInfo.factions} /> )} {this.state.role !== "admin" && this.state.role !== "" && ( @@ -181,16 +228,20 @@ export default class GameView extends React.Component { zoom={this.state.zoom} mapUrl={this.state.mapUrl} currentGameId={this.state.gameInfo.id} - socketSignal={this.state.socketSignal} + socketSignal={ + this.state.socketSignal === null + ? null + : this.state.socketSignal.type + } role={this.state.role} - /> + > + <NotificationPopup socketSignal={this.state.socketSignal} /> + </UserMap> {this.state.form === "edit" && ( <EditGameForm gameId={this.state.gameInfo.id} toggleView={() => this.setState({ form: "" })} - onEditSave={() => { - this.getGameInfo(this.state.gameInfo.id); - }} + onEditSave={() => this.getGameInfo(this.state.gameInfo.id)} /> )} {this.state.form === "join" && ( @@ -211,6 +262,19 @@ export default class GameView extends React.Component { <NotificationView gameId={this.state.gameInfo.id} toggleView={() => this.setState({ form: "" })} + socket={this.state.socket} + role={this.state.role} + gameState={ + this.state.gameInfo !== undefined + ? this.state.gameInfo.state + : "" + } + /> + )} + {this.state.form === "info" && ( + <GameInfoView + gameInfo={this.state.gameInfo} + toggleView={() => this.setState({ form: "" })} /> )} </div> diff --git a/src/components/JoinGameForm.js b/src/components/JoinGameForm.js index b638b61f2aeb6ac745f9afde6aa833103f69d318..35841c043a890f8ed4450590afe7740fb00c7028 100644 --- a/src/components/JoinGameForm.js +++ b/src/components/JoinGameForm.js @@ -105,6 +105,7 @@ export default class JoinGameForm extends React.Component { <h1>Join game: {this.state.gameJSON.name}</h1> <h2>Description: {this.state.gameJSON.desc}</h2> <select + id="selectFactionList" onChange={e => this.setState({ selectedFactionId: e.target.value }) } diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index b71777f007071ead97db3da75187caa80728a10d..d1e90cb994a8abfd30e15ad81aaaac2ac6c64ad9 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -87,7 +87,7 @@ export class LoginForm extends React.Component { <h2>{this.state.errorMsg}</h2> </form> <Link to="/register"> - <button>Create account</button> + <button id="loginRegisterButton">Create account</button> </Link> </div> </div> diff --git a/src/components/NotificationCard.js b/src/components/NotificationCard.js new file mode 100644 index 0000000000000000000000000000000000000000..cf49d28f285a8f6f662007b947b1d7a42cc682fc --- /dev/null +++ b/src/components/NotificationCard.js @@ -0,0 +1,11 @@ +import React from "react"; + +export default class NotificationCard extends React.Component { + render() { + return ( + <div> + {this.props.notification.type} : {this.props.notification.message} + </div> + ); + } +} diff --git a/src/components/NotificationPopup.js b/src/components/NotificationPopup.js new file mode 100644 index 0000000000000000000000000000000000000000..a3e9a723a6b62f03d7ace1024c1912e8f32ee38a --- /dev/null +++ b/src/components/NotificationPopup.js @@ -0,0 +1,58 @@ +import React from "react"; + +export default class NotificationPopup extends React.Component { + state = { + lastNotification: null, + visible: true + }; + + componentDidUpdate(prevProps, prevState) { + if ( + prevProps.socketSignal !== null && + prevProps.socketSignal !== this.state.lastNotification + ) { + if (prevProps.socketSignal.type === "alert") { + this.setState({ + lastNotification: prevProps.socketSignal, + visible: true + }); + } + if (prevProps.socketSignal.type === "note") { + this.setState({ + lastNotification: prevProps.socketSignal, + visible: true + }); + } + } + } + + render() { + if (this.state.lastNotification !== null && this.state.visible) { + return ( + <div + className={ + this.state.lastNotification.type === "alert" + ? "notification-popup alert" + : "notification-popup warning" + } + > + <button + id="NotificationPopupCloseButton" + onClick={() => { + this.setState({ visible: false }); + }} + > + Close + </button> + <br /> + <label> + {this.state.lastNotification.type === "alert" ? "ALERT" : "Note"} + </label> + <p>{this.state.lastNotification.message}</p> + </div> + ); + } + + return false; + } +} diff --git a/src/components/NotificationView.js b/src/components/NotificationView.js index 5fcf80141e569562492417d719854bd7b654ced1..d63d0253b0f585b9ad7d904fb9eaced0fa28b3d9 100644 --- a/src/components/NotificationView.js +++ b/src/components/NotificationView.js @@ -1,7 +1,99 @@ import React from "react"; +import NotificationCard from "./NotificationCard"; export default class NotificationView extends React.Component { + state = { + notifications: [], + notificationInput: "", + notificationTypeInput: "note" + }; + + componentDidMount() { + this.getNotifications(this.props.gameId); + } + + getNotifications(gameId) { + let token = sessionStorage.getItem("token"); + fetch(`${process.env.REACT_APP_API_URL}/notifications/${gameId}`, { + headers: { + Authorization: "Bearer " + token + } + }) + .then(res => res.json()) + .then(res => { + this.setState({ notifications: res.reverse() }); + }); + } + + handleSend = e => { + e.preventDefault(); + + if (this.state.notificationInput === "") { + alert("notification message can't be empty"); + } else if ( + window.confirm("Are you sure you want to send the notification") + ) { + this.props.socket.emit("game-info", { + type: this.state.notificationTypeInput, + message: this.state.notificationInput, + game: this.props.gameId + }); + alert("Notification sent"); + this.getNotifications(this.props.gameId); + this.setState({ notificationInput: "" }); + } + }; + render() { - return false; + let notifications = this.state.notifications.map(notification => ( + <NotificationCard key={notification.id} notification={notification} /> + )); + + return ( + <div className="fade-main"> + <button + id="notificationViewCloseButton" + onClick={() => this.props.toggleView()} + > + Close + </button> + <div> + {this.props.role === "admin" && + this.props.gameState !== "ENDED" && + this.props.gameState !== "CREATED" && ( + <form onSubmit={this.handleSend}> + <select + id="notificationViewTypeSelect" + value={this.state.notificationTypeInput} + onChange={e => + this.setState({ notificationTypeInput: e.target.value }) + } + > + <option value="note">Note</option> + <option value="alert">Alert</option> + </select> + <input + id="notificationViewMessageInput" + type="text" + value={this.state.notificationInput} + onChange={e => + this.setState({ notificationInput: e.target.value }) + } + placeholder="Notification message..." + /> + <button id="notificationSubmitButton" type="submit"> + Send Notification + </button> + </form> + )} + {this.props.role === "admin" && + (this.props.gameState === "ENDED" || + this.props.gameState === "CREATED") && ( + <p>Notifications can only be sent if the game is ongoing</p> + )} + </div> + {notifications} + </div> + ); } } diff --git a/src/components/PlayerlistPlayerCard.js b/src/components/PlayerlistPlayerCard.js index ebc2ddeb5c6eb75958cf6390404206f488f73878..7260d5abaa4b4f617cb32ea7b428756f5ab606ba 100644 --- a/src/components/PlayerlistPlayerCard.js +++ b/src/components/PlayerlistPlayerCard.js @@ -69,13 +69,20 @@ export default class PlayerlistPlayerCard extends React.Component { <div> {this.props.player.person.name} :{" "} <select + id={"playerCardRoleSelect" + this.props.player.person.name} value={this.state.roleInput} onChange={e => this.setState({ roleInput: e.target.value })} > {roleOptions()} </select> - <button onClick={this.handleSave}>Save</button> <button + id={"playerCardSaveButton" + this.props.player.person.name} + onClick={this.handleSave} + > + Save + </button> + <button + id={"playerCardCancelButton" + this.props.player.person.name} onClick={() => { this.setState({ edit: false, roleInput: this.props.player.role }); }} @@ -92,16 +99,13 @@ export default class PlayerlistPlayerCard extends React.Component { {this.props.player.person.name} : {this.state.edit && roleOptions()} {!this.state.edit && this.props.player.role} {this.props.role === "admin" && !this.state.edit && ( - <button onClick={() => this.setState({ edit: !this.state.edit })}> + <button + id={"playerCardEditButton" + this.props.player.person.name} + onClick={() => this.setState({ edit: !this.state.edit })} + > Edit </button> )} - {this.state.edit && ( - <Fragment> - <button>Save</button> - <button>Cancel</button> - </Fragment> - )} </div> ); } diff --git a/src/components/PlayerlistView.js b/src/components/PlayerlistView.js index 6967b0e2e0dc25017ff4940710e3d1e4488a6150..60c294fc257c3f12cd9fded17659c8e0323be4c5 100644 --- a/src/components/PlayerlistView.js +++ b/src/components/PlayerlistView.js @@ -71,7 +71,7 @@ export default class PlayerlistView extends React.Component { <div className="fade-main"> <div className="sticky"> <span - id="closeEditGameFormX" + id="closePlayerlistX" className="close" onClick={() => this.props.toggleView()} > diff --git a/src/components/RegisterForm.js b/src/components/RegisterForm.js index 889f3351917fd7d4366fa0afde5ed3b3f348ae68..b0e2ad24bae0139169c94b6d5447c508e4fecb1d 100644 --- a/src/components/RegisterForm.js +++ b/src/components/RegisterForm.js @@ -72,6 +72,7 @@ export class RegisterForm extends React.Component { <h1>Register</h1> <br /> <input + id="registerUsernameInput" placeholder="Enter Username" name="username" value={this.state.username} @@ -81,6 +82,7 @@ export class RegisterForm extends React.Component { /> <br /> <input + id="registerPasswordInput" placeholder="Enter password" type="password" name="password" @@ -90,6 +92,7 @@ export class RegisterForm extends React.Component { /> <br /> <input + id="registerPasswordVerifyInput" placeholder="Verify password" type="password" name="password2" @@ -98,11 +101,13 @@ export class RegisterForm extends React.Component { //required /> <br /> - <button type="submit">Submit</button> + <button id="submitRegisterButton" type="submit"> + Submit + </button> <h2>{this.state.errorMsg}</h2> </form> <Link to="/"> - <button>Login</button> + <button id="openLoginFormButton">Login</button> </Link> </div> </div> diff --git a/src/components/Socket.js b/src/components/Socket.js index 8256585f5df46e07970c64e774e79c586c3522cb..07142e51ea875131ffa3605ce2f83fcee746fd01 100644 --- a/src/components/Socket.js +++ b/src/components/Socket.js @@ -20,8 +20,8 @@ export default class ClientSocket extends React.Component { console.log("hi socket"); // need to explicitly update drawings and trackings when gameID first becomes available if (this.props.gameId !== null) { - await this.props.getSocketSignal("drawing-update"); - await this.props.getSocketSignal("tracking-update"); + await this.props.getSocketSignal({ type: "drawing-update" }); + await this.props.getSocketSignal({ type: "tracking-update" }); } this.initSocket(); } @@ -38,7 +38,7 @@ export default class ClientSocket extends React.Component { // disconnect the socket on component dismount componentWillUnmount() { - console.log("bye socket"); + // console.log("bye socket"); this.state.sock.disconnect(); } @@ -47,11 +47,12 @@ export default class ClientSocket extends React.Component { // set the socket to listen gameId-thread socket.on(this.props.gameId, data => { - this.props.getSocketSignal(data.type); + this.props.getSocketSignal(data); // check socket update type - this.setState({ update: data.type }); + this.setState({ update: data }); }); + this.props.onSocketChange(socket); this.setState({ sock: socket }); }; diff --git a/src/components/TaskItem.js b/src/components/TaskItem.js index 0265ca779fe1efdea077bca5c26557726f4400c1..21644f10a0bde41ed9d09b7c0a02406969ede078 100644 --- a/src/components/TaskItem.js +++ b/src/components/TaskItem.js @@ -96,11 +96,17 @@ class TaskItem extends React.Component { )} </div> {this.props.task.taskIsActive && this.props.role === "admin" && ( - <button onClick={this.onEditClick}>Edit</button> + <button + id={"taskEditButton" + this.props.task.taskName} + onClick={this.onEditClick} + > + Edit + </button> )} {this.state.edit && ( <form onSubmit={this.onSaveSubmit}> <select + id={"taskWinnerSelect" + this.props.task.taskName} value={this.state.selectedFactionId} onChange={e => this.setState({ selectedFactionId: e.target.value }) @@ -113,6 +119,7 @@ class TaskItem extends React.Component { )} {this.props.role === "admin" && ( <button + id={"taskDeleteButton" + this.props.task.taskName} onClick={this.onTaskDelete} style={{ backgroundColor: "red" }} > diff --git a/src/components/TaskList.js b/src/components/TaskList.js index 241caa494f16220184c55288e637d54b4585dd52..1b8e1374f85cb8c81b6212368e370e9c1495c3cf 100644 --- a/src/components/TaskList.js +++ b/src/components/TaskList.js @@ -9,14 +9,12 @@ class TaskList extends React.Component { taskNameInput: "", // 3-31 chars taskDescriptionInput: "", // 0-255 tasks: [], - factionlist: [], selectedFactionId: "" }; } componentDidMount() { this.getTasks(this.props.gameId); - this.getFactionlist(this.props.gameId); // TODO: remove if the user is not admin? } getTasks(gameId) { @@ -42,25 +40,6 @@ class TaskList extends React.Component { .catch(error => console.log(error)); } - getFactionlist(gameId) { - fetch(`${process.env.REACT_APP_API_URL}/game/${gameId}`, { - method: "GET" - }) - .then(result => { - if (!result.ok) { - throw Error(result.responseText); - } else { - return result.json(); - } - }) - .then(result => { - this.setState({ - factionlist: result.factions - }); - }) - .catch(error => console.log(error)); - } - handleTaskCreation = e => { e.preventDefault(); if (this.state.taskNameInput === "") { @@ -206,10 +185,10 @@ class TaskList extends React.Component { } } - let factionlistItems = this.state.factionlist.map(item => { + let factionlistItems = this.props.factions.map(faction => { return ( - <option key={item.factionId} value={item.factionId}> - {item.factionName} + <option key={faction.factionId} value={faction.factionId}> + {faction.factionName} </option> ); }); diff --git a/src/components/TaskListButton.js b/src/components/TaskListButton.js index c0e5f8c1257edaaae2e062fbc8dce562538aa5c1..bb56314129f92204ab45d226b3d0f69593b18d05 100644 --- a/src/components/TaskListButton.js +++ b/src/components/TaskListButton.js @@ -45,7 +45,11 @@ export default class TaskListButton extends React.Component { Tasks </button> {this.state.open && ( - <TaskList gameId={this.props.gameId} role={this.props.role} /> + <TaskList + gameId={this.props.gameId} + role={this.props.role} + factions={this.props.factions} + /> )} </Fragment> ); diff --git a/src/components/UserMap.js b/src/components/UserMap.js index e9b2b26252fc313e0d5b171d7c36bf71e684ebf8..83f4de8d09da8efa58426855273b27fbf4145b0b 100644 --- a/src/components/UserMap.js +++ b/src/components/UserMap.js @@ -39,6 +39,7 @@ class UserMap extends Component { } componentDidUpdate(prevProps, prevState) { + console.log(prevProps.socketSignal); if (prevProps.socketSignal === "drawing-update") { this.fetchGeoJSON(); } @@ -162,15 +163,13 @@ class UserMap extends Component { url={this.props.mapUrl} /> <ZoomControl position="topright" /> - {(this.props.role === "admin" || - this.props.role === "factionleader") && ( - <DrawTools - position={this.props.position} - sendGeoJSON={this.sendGeoJSON} - geoJSONLayer={this.state.geoJSONLayer} - currentGameId={this.props.currentGameId} - /> - )} + <DrawTools + position={this.props.position} + sendGeoJSON={this.sendGeoJSON} + geoJSONLayer={this.state.geoJSONLayer} + currentGameId={this.props.currentGameId} + role={this.props.role} + /> {this.state.ownLat !== null && ( <Marker position={[this.state.ownLat, this.state.ownLng]}> <Popup> @@ -183,6 +182,7 @@ class UserMap extends Component { currentGameId={this.props.currentGameId} socketSignal={this.props.socketSignal} /> + {this.props.children} </Map> ); }