Implemented AI (“Rando”), refactored card rendering.
This commit is contained in:
Generated
+300
-132
@@ -45,9 +45,9 @@
|
||||
}
|
||||
},
|
||||
"@types/chromecast-caf-receiver": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/chromecast-caf-receiver/-/chromecast-caf-receiver-3.0.8.tgz",
|
||||
"integrity": "sha512-Zdl05s8nutvcXFri2ie5U16Sn8hT4I5SbkPW0Dsc84n3VJTQkEra3zroD/rDF0xAPL2fY1BU4w3nJA3X91KAVw==",
|
||||
"version": "3.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/chromecast-caf-receiver/-/chromecast-caf-receiver-3.0.12.tgz",
|
||||
"integrity": "sha512-GdR9nGOENDWYhF40FasB0Xnsy3c+e68K90sGVBZx1W1N3LP1NGOmCtaxgUpxk4IuHYmzGrW7I57zWZIbT3D5BQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/chromecast-caf-sender": {
|
||||
@@ -360,15 +360,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
|
||||
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-dynamic-import": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
|
||||
"integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz",
|
||||
"integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
@@ -772,18 +766,18 @@
|
||||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.0.tgz",
|
||||
"integrity": "sha512-kuip9YilBqhirhHEGHaBTZKXL//xxGnzvsD0FtBQa6z+A69qZD6s/BAX9VzDF1i9VKDquTJDQaPLSEhOnL6FvQ==",
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz",
|
||||
"integrity": "sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.6.1",
|
||||
"caniuse-lite": "^1.0.30000971",
|
||||
"browserslist": "^4.6.3",
|
||||
"caniuse-lite": "^1.0.30000980",
|
||||
"chalk": "^2.4.2",
|
||||
"normalize-range": "^0.1.2",
|
||||
"num2fraction": "^1.2.2",
|
||||
"postcss": "^7.0.16",
|
||||
"postcss-value-parser": "^3.3.1"
|
||||
"postcss": "^7.0.17",
|
||||
"postcss-value-parser": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
@@ -795,6 +789,12 @@
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000984",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz",
|
||||
"integrity": "sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
@@ -806,12 +806,6 @@
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
|
||||
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
@@ -2127,9 +2121,9 @@
|
||||
}
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.0.0.tgz",
|
||||
"integrity": "sha512-WR6KZuCkFbnMhRrGPlkwAA7SSCtwqPwpyXJAPhotYkYsc0mKU9n/fu5wufy4jl2WhBw9Ia8gUQMIp/1w98DuPw==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.1.0.tgz",
|
||||
"integrity": "sha512-MuL8WsF/KSrHCBCYaozBKlx+r7vIfUaDTEreo7wR7Vv3J6N0z6fqWjRk3e/6wjneitXN1r/Y9FTK1psYNOBdJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.3.1",
|
||||
@@ -2143,7 +2137,31 @@
|
||||
"postcss-modules-scope": "^2.1.0",
|
||||
"postcss-modules-values": "^3.0.0",
|
||||
"postcss-value-parser": "^4.0.0",
|
||||
"schema-utils": "^1.0.0"
|
||||
"schema-utils": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.1.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-prefers-color-scheme": {
|
||||
@@ -2642,9 +2660,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
|
||||
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz",
|
||||
"integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
@@ -3190,13 +3208,37 @@
|
||||
"dev": true
|
||||
},
|
||||
"file-loader": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.0.0.tgz",
|
||||
"integrity": "sha512-roAbL6IdSGczwfXxhMi6Zq+jD4IfUpL0jWHD7fvmjdOVb7xBfdRUHe4LpBgO23VtVK5AW1OlWZo0p34Jvx3iWg==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.1.0.tgz",
|
||||
"integrity": "sha512-ajDk1nlByoalZAGR4b0H6oD+EGlWnyW1qbSxzaUc7RFiqmn+RbXQQRbTc72jsiUIlVusJ4Et58ltds8ZwTfnAw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "^1.2.2",
|
||||
"schema-utils": "^1.0.0"
|
||||
"loader-utils": "^1.2.3",
|
||||
"schema-utils": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.0.1.tgz",
|
||||
"integrity": "sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.1.0",
|
||||
"ajv-keywords": "^3.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fill-range": {
|
||||
@@ -3254,6 +3296,14 @@
|
||||
"requires": {
|
||||
"firstline": "1.2.0",
|
||||
"lodash": "4.17.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
@@ -5515,9 +5565,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._reinterpolate": {
|
||||
@@ -5539,22 +5589,22 @@
|
||||
"dev": true
|
||||
},
|
||||
"lodash.template": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
|
||||
"integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
|
||||
"integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._reinterpolate": "~3.0.0",
|
||||
"lodash._reinterpolate": "^3.0.0",
|
||||
"lodash.templatesettings": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.templatesettings": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz",
|
||||
"integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
|
||||
"integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._reinterpolate": "~3.0.0"
|
||||
"lodash._reinterpolate": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.uniq": {
|
||||
@@ -5596,18 +5646,18 @@
|
||||
}
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
|
||||
"integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz",
|
||||
"integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pify": "^3.0.0"
|
||||
"semver": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -5786,6 +5836,12 @@
|
||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
|
||||
"dev": true
|
||||
},
|
||||
"merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"dev": true
|
||||
},
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@@ -5916,9 +5972,9 @@
|
||||
}
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
|
||||
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
|
||||
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"for-in": "^1.0.2",
|
||||
@@ -6077,6 +6133,14 @@
|
||||
"find-elm-dependencies": "2.0.1",
|
||||
"lodash": "4.17.11",
|
||||
"temp": "^0.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
@@ -6440,6 +6504,15 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"onetime": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
|
||||
"integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"opn": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
|
||||
@@ -7227,12 +7300,12 @@
|
||||
}
|
||||
},
|
||||
"postcss-initial": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.0.tgz",
|
||||
"integrity": "sha512-WzrqZ5nG9R9fUtrA+we92R4jhVvEB32IIRTzfIG/PLL8UV4CvbF1ugTEHEFX6vWxl41Xt5RTCJPEZkuWzrOM+Q==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.1.tgz",
|
||||
"integrity": "sha512-I2Sz83ZSHybMNh02xQDK609lZ1/QOyYeuizCjzEhlMgeV/HcDJapQiH4yTqLjZss0X6/6VvKFXUeObaHpJoINw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.template": "^4.2.4",
|
||||
"lodash.template": "^4.5.0",
|
||||
"postcss": "^7.0.2"
|
||||
}
|
||||
},
|
||||
@@ -7686,27 +7759,27 @@
|
||||
}
|
||||
},
|
||||
"postcss-preset-env": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.6.0.tgz",
|
||||
"integrity": "sha512-I3zAiycfqXpPIFD6HXhLfWXIewAWO8emOKz+QSsxaUZb9Dp8HbF5kUf+4Wy/AxR33o+LRoO8blEWCHth0ZsCLA==",
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz",
|
||||
"integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"autoprefixer": "^9.4.9",
|
||||
"browserslist": "^4.4.2",
|
||||
"caniuse-lite": "^1.0.30000939",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"browserslist": "^4.6.4",
|
||||
"caniuse-lite": "^1.0.30000981",
|
||||
"css-blank-pseudo": "^0.1.4",
|
||||
"css-has-pseudo": "^0.10.0",
|
||||
"css-prefers-color-scheme": "^3.1.1",
|
||||
"cssdb": "^4.3.0",
|
||||
"postcss": "^7.0.14",
|
||||
"cssdb": "^4.4.0",
|
||||
"postcss": "^7.0.17",
|
||||
"postcss-attribute-case-insensitive": "^4.0.1",
|
||||
"postcss-color-functional-notation": "^2.0.1",
|
||||
"postcss-color-gray": "^5.0.0",
|
||||
"postcss-color-hex-alpha": "^5.0.2",
|
||||
"postcss-color-hex-alpha": "^5.0.3",
|
||||
"postcss-color-mod-function": "^3.0.3",
|
||||
"postcss-color-rebeccapurple": "^4.0.1",
|
||||
"postcss-custom-media": "^7.0.7",
|
||||
"postcss-custom-properties": "^8.0.9",
|
||||
"postcss-custom-media": "^7.0.8",
|
||||
"postcss-custom-properties": "^8.0.11",
|
||||
"postcss-custom-selectors": "^5.1.2",
|
||||
"postcss-dir-pseudo-class": "^5.0.0",
|
||||
"postcss-double-position-gradients": "^1.0.0",
|
||||
@@ -7728,6 +7801,40 @@
|
||||
"postcss-replace-overflow-wrap": "^3.0.0",
|
||||
"postcss-selector-matches": "^4.0.0",
|
||||
"postcss-selector-not": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"browserslist": {
|
||||
"version": "4.6.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz",
|
||||
"integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30000984",
|
||||
"electron-to-chromium": "^1.3.191",
|
||||
"node-releases": "^1.1.25"
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000984",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz",
|
||||
"integrity": "sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.191",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.191.tgz",
|
||||
"integrity": "sha512-jasjtY5RUy/TOyiUYM2fb4BDaPZfm6CXRFeJDMfFsXYADGxUN49RBqtgB7EL2RmJXeIRUk9lM1U6A5yk2YJMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.25",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.25.tgz",
|
||||
"integrity": "sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss-pseudo-class-any-link": {
|
||||
@@ -8013,17 +8120,87 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier-plugin-elm": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-elm/-/prettier-plugin-elm-0.4.2.tgz",
|
||||
"integrity": "sha512-ux3JcE+YGoLRpceRCmAt44j1qknhRg8/UieGK8LA4j6rohaJeKh4RT3ubaIijI1gspxExP4duiVAevqrZ1AsZA==",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-elm/-/prettier-plugin-elm-0.5.0.tgz",
|
||||
"integrity": "sha512-NZ52TgVENft2jOiOC1xOKnIFJyIyyW74JTRuNqoh/8WvNBAdMSR8aWMbkuDBHVWS6J7YR/JrayNTmlzZ/yGKXg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"elm-format": "^0.8.1",
|
||||
"execa": "^1.0.0",
|
||||
"make-dir": "^1.3.0",
|
||||
"execa": "^2.0.3",
|
||||
"make-dir": "^3.0.0",
|
||||
"object-hash": "^1.3.1",
|
||||
"serialize-error": "^3.0.0",
|
||||
"temp-dir": "^1.0.0"
|
||||
"serialize-error": "^4.1.0",
|
||||
"temp-dir": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-spawn": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nice-try": "^1.0.4",
|
||||
"path-key": "^2.0.1",
|
||||
"semver": "^5.5.0",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
}
|
||||
},
|
||||
"execa": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-2.0.3.tgz",
|
||||
"integrity": "sha512-iM124nlyGSrXmuyZF1EMe83ESY2chIYVyDRZKgmcDynid2Q2v/+GuE7gNMl6Sy9Niwf4MC0DDxagOxeMPjuLsw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^6.0.5",
|
||||
"get-stream": "^5.0.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^3.0.0",
|
||||
"onetime": "^5.1.0",
|
||||
"p-finally": "^2.0.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
|
||||
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz",
|
||||
"integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"path-key": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz",
|
||||
"integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"p-finally": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
|
||||
"integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"pretty-error": {
|
||||
@@ -8923,10 +9100,13 @@
|
||||
}
|
||||
},
|
||||
"serialize-error": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-3.0.0.tgz",
|
||||
"integrity": "sha512-+y3nkkG/go1Vdw+2f/+XUXM1DXX1XcxTl99FfiD/OEPUNw4uo0i6FKABfTAN5ZcgGtjTRZcEbxcE/jtXbEY19A==",
|
||||
"dev": true
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-4.1.0.tgz",
|
||||
"integrity": "sha512-5j9GgyGsP9vV9Uj1S0lDCvlsd+gc2LEPVK7HHHte7IyPwOD4lVQFeaX143gx3U5AnoCi+wbcb3mvaxVysjpxEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "1.7.0",
|
||||
@@ -8994,9 +9174,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"set-value": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
|
||||
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
||||
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
@@ -9702,6 +9882,12 @@
|
||||
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-final-newline": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-indent": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
|
||||
@@ -9800,9 +9986,9 @@
|
||||
}
|
||||
},
|
||||
"temp-dir": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
|
||||
"integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||
"integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
@@ -10094,6 +10280,12 @@
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
|
||||
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
@@ -10111,9 +10303,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
|
||||
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
@@ -10176,38 +10368,15 @@
|
||||
}
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
|
||||
"integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||
"integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-union": "^3.1.0",
|
||||
"get-value": "^2.0.6",
|
||||
"is-extendable": "^0.1.1",
|
||||
"set-value": "^0.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"set-value": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
|
||||
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"is-extendable": "^0.1.1",
|
||||
"is-plain-object": "^2.0.1",
|
||||
"to-object-path": "^0.3.0"
|
||||
}
|
||||
}
|
||||
"set-value": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"uniq": {
|
||||
@@ -10540,17 +10709,16 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.35.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.35.0.tgz",
|
||||
"integrity": "sha512-M5hL3qpVvtr8d4YaJANbAQBc4uT01G33eDpl/psRTBCfjxFTihdhin1NtAKB1ruDwzeVdcsHHV3NX+QsAgOosw==",
|
||||
"version": "4.38.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.38.0.tgz",
|
||||
"integrity": "sha512-lbuFsVOq8PZY+1Ytz/mYOvYOo+d4IJ31hHk/7iyoeWtwN33V+5HYotSH+UIb9tq914ey0Hot7z6HugD+je3sWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.8.5",
|
||||
"@webassemblyjs/helper-module-context": "1.8.5",
|
||||
"@webassemblyjs/wasm-edit": "1.8.5",
|
||||
"@webassemblyjs/wasm-parser": "1.8.5",
|
||||
"acorn": "^6.0.5",
|
||||
"acorn-dynamic-import": "^4.0.0",
|
||||
"acorn": "^6.2.0",
|
||||
"ajv": "^6.1.0",
|
||||
"ajv-keywords": "^3.1.0",
|
||||
"chrome-trace-event": "^1.0.0",
|
||||
@@ -10572,9 +10740,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
@@ -10689,9 +10857,9 @@
|
||||
}
|
||||
},
|
||||
"webpack-cli": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.5.tgz",
|
||||
"integrity": "sha512-w0j/s42c5UhchwTmV/45MLQnTVwRoaUTu9fM5LuyOd/8lFoCNCELDogFoecx5NzRUndO0yD/gF2b02XKMnmAWQ==",
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.6.tgz",
|
||||
"integrity": "sha512-0vEa83M7kJtxK/jUhlpZ27WHIOndz5mghWL2O53kiDoA9DIxSKnfqB92LoqEn77cT4f3H2cZm1BMEat/6AZz3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "2.4.2",
|
||||
|
||||
+8
-8
@@ -28,36 +28,36 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.0.86",
|
||||
"@types/chromecast-caf-receiver": "^3.0.8",
|
||||
"@types/chromecast-caf-receiver": "^3.0.12",
|
||||
"@types/chromecast-caf-sender": "^1.0.1",
|
||||
"app-manifest-loader": "^2.3.1",
|
||||
"chromecast-device-emulator": "^1.2.7",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"compression-webpack-plugin": "^3.0.0",
|
||||
"css-hot-loader": "^1.4.4",
|
||||
"css-loader": "^3.0.0",
|
||||
"css-loader": "^3.1.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"elm": "^0.19.0-no-deps",
|
||||
"elm-hot-webpack-loader": "^1.0.2",
|
||||
"elm-webpack-loader": "^5.0.0",
|
||||
"extract-loader": "^3.1.0",
|
||||
"file-loader": "^4.0.0",
|
||||
"file-loader": "^4.1.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.6.0",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prettier": "1.18.2",
|
||||
"prettier-plugin-elm": "^0.4.2",
|
||||
"prettier-plugin-elm": "^0.5.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"terser-webpack-plugin": "^1.3.0",
|
||||
"ts-loader": "^6.0.4",
|
||||
"typescript": "^3.5.2",
|
||||
"typescript": "^3.5.3",
|
||||
"uglifyjs-webpack-plugin": "^2.1.3",
|
||||
"webpack": "^4.35.0",
|
||||
"webpack-cli": "^3.3.5",
|
||||
"webpack": "^4.38.0",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-dev-server": "^3.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ init flags url key =
|
||||
Route.fromUrl url
|
||||
|
||||
{ settings, browserLanguages } =
|
||||
Json.decodeValue Decoders.flags flags |> Result.toMaybe |> Maybe.withDefault (Flags Settings.defaults [])
|
||||
Json.decodeValue Decoders.flags flags
|
||||
|> Result.toMaybe
|
||||
|> Maybe.withDefault (Flags Settings.defaults [])
|
||||
|
||||
( initialisedSettings, settingsCmd ) =
|
||||
Settings.init settings
|
||||
@@ -109,7 +111,8 @@ update msg model =
|
||||
case msg of
|
||||
ChangePage route ->
|
||||
if oldRoute /= route then
|
||||
changePageFromRoute model.shared route model.page |> Util.modelLift (\p -> { model | page = p })
|
||||
changePageFromRoute model.shared route model.page
|
||||
|> Util.modelLift (\p -> { model | page = p })
|
||||
|
||||
else
|
||||
( model, Cmd.none )
|
||||
@@ -154,7 +157,9 @@ update msg model =
|
||||
)
|
||||
|
||||
Route.Redirect redirect ->
|
||||
( changePageFromRoute model.shared redirect model.page, ( shared.settings, Cmd.none ) )
|
||||
( changePageFromRoute model.shared redirect model.page
|
||||
, ( shared.settings, Cmd.none )
|
||||
)
|
||||
in
|
||||
( { model
|
||||
| shared = { shared | settings = settings }
|
||||
|
||||
@@ -3,6 +3,7 @@ module MassiveDecks.Animated exposing
|
||||
, Msg(..)
|
||||
, State
|
||||
, animate
|
||||
, defaultDuration
|
||||
, enterAfter
|
||||
, exitAfter
|
||||
, subscriptions
|
||||
@@ -92,6 +93,13 @@ exitAfter milliseconds item =
|
||||
Exit item |> Cmd.after milliseconds
|
||||
|
||||
|
||||
{-| If changed, sync to CSS.
|
||||
-}
|
||||
defaultDuration : Int
|
||||
defaultDuration =
|
||||
200
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
@@ -1,151 +1,45 @@
|
||||
module MassiveDecks.Card exposing
|
||||
( id
|
||||
, slotCount
|
||||
, source
|
||||
, view
|
||||
, viewFilled
|
||||
, viewUnknownCall
|
||||
, viewUnknownResponse
|
||||
( view
|
||||
, viewUnknown
|
||||
)
|
||||
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card.Model exposing (..)
|
||||
import MassiveDecks.Card.Parts as Parts exposing (Parts)
|
||||
import MassiveDecks.Card.Source as Source
|
||||
import MassiveDecks.Card.Source.Model as Source exposing (Source)
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Strings exposing (MdString(..))
|
||||
import MassiveDecks.Strings.Languages as Lang
|
||||
import MassiveDecks.Util.Html as Html
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import MassiveDecks.Util.String as String
|
||||
|
||||
|
||||
{-| Render a card to HTML, with the given side face-up.
|
||||
-}
|
||||
view : Shared -> List Configure.Deck -> Side -> List (Html.Attribute msg) -> Card -> Html msg
|
||||
view shared decks visibleSide extraAttributes card =
|
||||
viewInternal shared decks visibleSide extraAttributes [] card
|
||||
view : String -> List Configure.Deck -> Side -> List (Html.Attribute msg) -> ViewBody msg -> ViewInstructions msg -> Source -> Html msg
|
||||
view cardTypeClass decks visibleSide attributes viewBody viewInstructions source =
|
||||
Html.div
|
||||
(HtmlA.classList
|
||||
[ ( "game-card", True )
|
||||
, ( cardTypeClass, True )
|
||||
, ( "face-down", visibleSide == Back )
|
||||
]
|
||||
:: attributes
|
||||
)
|
||||
[ Html.div [ HtmlA.class "aspect" ] [ front decks viewBody viewInstructions source, back ] ]
|
||||
|
||||
|
||||
{-| View a call rendered with the given play filling the slots.
|
||||
{-| Render an unknown card to HTML, face-down.
|
||||
-}
|
||||
viewFilled : Shared -> List Configure.Deck -> Side -> List (Html.Attribute msg) -> List String -> Call -> Html msg
|
||||
viewFilled shared decks visibleSide extraAttributes play card =
|
||||
viewInternal shared decks visibleSide extraAttributes play (C card)
|
||||
|
||||
|
||||
{-| Render an unknown response to HTML, face-down.
|
||||
-}
|
||||
viewUnknownResponse : List (Html.Attribute msg) -> Html msg
|
||||
viewUnknownResponse extraAttributes =
|
||||
viewUnknown (HtmlA.class "response" :: extraAttributes)
|
||||
|
||||
|
||||
{-| Render an unknown response to HTML, face-down.
|
||||
-}
|
||||
viewUnknownCall : List (Html.Attribute msg) -> Html msg
|
||||
viewUnknownCall extraAttributes =
|
||||
viewUnknown (HtmlA.class "call" :: extraAttributes)
|
||||
|
||||
|
||||
{-| Get the source of a card.
|
||||
-}
|
||||
source : Card -> Source
|
||||
source card =
|
||||
card |> details |> .source
|
||||
|
||||
|
||||
{-| Get the id of a card.
|
||||
-}
|
||||
id : Card -> Id
|
||||
id card =
|
||||
card |> details |> .id
|
||||
|
||||
|
||||
{-| How many slots there are on a call.
|
||||
-}
|
||||
slotCount : Call -> Int
|
||||
slotCount call =
|
||||
call.body |> Parts.slotCount
|
||||
viewUnknown : String -> List (Html.Attribute msg) -> Html msg
|
||||
viewUnknown cardTypeClass attributes =
|
||||
Html.div (HtmlA.class "game-card face-down" :: (HtmlA.class cardTypeClass :: attributes))
|
||||
[ Html.div [ HtmlA.class "aspect" ] [ back ] ]
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewInternal : Shared -> List Configure.Deck -> Side -> List (Html.Attribute msg) -> List String -> Card -> Html msg
|
||||
viewInternal shared decks visibleSide extraAttributes play card =
|
||||
Html.div
|
||||
([ HtmlA.classList
|
||||
[ ( "game-card", True )
|
||||
, ( cardTypeClass card, True )
|
||||
, ( "face-down", visibleSide == Back )
|
||||
]
|
||||
]
|
||||
++ extraAttributes
|
||||
)
|
||||
[ Html.div [ HtmlA.class "aspect" ]
|
||||
[ front shared decks play card
|
||||
, back
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewUnknown : List (Html.Attribute msg) -> Html msg
|
||||
viewUnknown extraAttributes =
|
||||
Html.div
|
||||
([ HtmlA.class "game-card face-down" ] ++ extraAttributes)
|
||||
[ Html.div [ HtmlA.class "aspect" ]
|
||||
[ back ]
|
||||
]
|
||||
|
||||
|
||||
instructions : Shared -> Parts -> List (Html msg)
|
||||
instructions shared parts =
|
||||
let
|
||||
slots =
|
||||
Parts.slotCount parts
|
||||
|
||||
instructionViews =
|
||||
List.concat [ extraCardsInstruction shared slots, pickInstruction shared slots ]
|
||||
in
|
||||
if List.length instructionViews > 0 then
|
||||
[ Html.ol
|
||||
[ HtmlA.class "instructions" ]
|
||||
instructionViews
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
extraCardsInstruction : Shared -> Int -> List (Html msg)
|
||||
extraCardsInstruction shared slots =
|
||||
let
|
||||
extraCards =
|
||||
slots - 1
|
||||
in
|
||||
if extraCards > 0 then
|
||||
[ Html.li [] [ Draw { numberOfCards = extraCards } |> Lang.html shared ]
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
pickInstruction : Shared -> Int -> List (Html msg)
|
||||
pickInstruction shared slots =
|
||||
if slots > 1 then
|
||||
[ Html.li [] [ Pick { numberOfCards = slots } |> Lang.html shared ]
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
cardSide : Side -> List (Html msg) -> Html msg
|
||||
cardSide side content =
|
||||
Html.div [ HtmlA.classList [ ( "side", True ), ( "front", side == Front ), ( "back", side == Back ) ] ] content
|
||||
@@ -159,37 +53,17 @@ back =
|
||||
]
|
||||
|
||||
|
||||
front : Shared -> List Configure.Deck -> List String -> Card -> Html msg
|
||||
front shared decks play card =
|
||||
front : List Configure.Deck -> ViewBody msg -> ViewInstructions msg -> Source -> Html msg
|
||||
front decks (ViewBody viewBody) viewInstructions source =
|
||||
cardSide Front
|
||||
[ Html.div [ HtmlA.class "content" ]
|
||||
(case card of
|
||||
C call ->
|
||||
Parts.viewFilled play call.body
|
||||
|
||||
R response ->
|
||||
[ Html.p [] [ Html.span [] [ response.body |> String.capitalise |> Html.text ] ] ]
|
||||
)
|
||||
, info shared decks card
|
||||
[ Html.div [ HtmlA.class "content" ] (viewBody ())
|
||||
, info decks viewInstructions source
|
||||
]
|
||||
|
||||
|
||||
info : Shared -> List Configure.Deck -> Card -> Html msg
|
||||
info shared decks card =
|
||||
let
|
||||
instructionsInfo =
|
||||
case card of
|
||||
C call ->
|
||||
instructions shared call.body
|
||||
|
||||
R _ ->
|
||||
[]
|
||||
in
|
||||
Html.div [ HtmlA.class "info" ]
|
||||
([ card |> source |> viewSource decks
|
||||
]
|
||||
++ instructionsInfo
|
||||
)
|
||||
info : List Configure.Deck -> ViewInstructions msg -> Source -> Html msg
|
||||
info decks (ViewInstructions viewInstructions) source =
|
||||
Html.div [ HtmlA.class "info" ] ((source |> viewSource decks) :: viewInstructions ())
|
||||
|
||||
|
||||
viewSource : List Configure.Deck -> Source -> Html msg
|
||||
@@ -211,23 +85,3 @@ viewSource decks s =
|
||||
makeLink : Html msg -> String -> Html msg
|
||||
makeLink text url =
|
||||
Html.blankA [ HtmlA.href url ] [ text ]
|
||||
|
||||
|
||||
cardTypeClass : Card -> String
|
||||
cardTypeClass card =
|
||||
case card of
|
||||
C _ ->
|
||||
"call"
|
||||
|
||||
R _ ->
|
||||
"response"
|
||||
|
||||
|
||||
details : Card -> Details
|
||||
details card =
|
||||
case card of
|
||||
C call ->
|
||||
call.details
|
||||
|
||||
R response ->
|
||||
response.details
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
module MassiveDecks.Card.Call exposing (slotCount, view, viewFilled, viewUnknown)
|
||||
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model exposing (..)
|
||||
import MassiveDecks.Card.Parts as Parts exposing (Parts)
|
||||
import MassiveDecks.Game.Rules exposing (Rules)
|
||||
import MassiveDecks.Model exposing (Shared)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Strings exposing (MdString(..))
|
||||
import MassiveDecks.Strings.Languages as Lang
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
|
||||
|
||||
{-| How many slots there are on a call.
|
||||
-}
|
||||
slotCount : Call -> Int
|
||||
slotCount call =
|
||||
call.body |> Parts.slotCount
|
||||
|
||||
|
||||
{-| Render the call to HTML.
|
||||
-}
|
||||
view : Shared -> Config -> Side -> List (Html.Attribute msg) -> Call -> Html msg
|
||||
view shared config side attributes call =
|
||||
viewInternal shared config side attributes Parts.view call
|
||||
|
||||
|
||||
{-| Render the call to HTML, with the slots filled with the given values.
|
||||
-}
|
||||
viewFilled : Shared -> Config -> Side -> List (Html.Attribute msg) -> List String -> Call -> Html msg
|
||||
viewFilled shared config side attributes fillWith call =
|
||||
viewInternal shared config side attributes (Parts.viewFilled fillWith) call
|
||||
|
||||
|
||||
{-| Render an unknown response to HTML, face-down.
|
||||
-}
|
||||
viewUnknown : List (Html.Attribute msg) -> Html msg
|
||||
viewUnknown attributes =
|
||||
Card.viewUnknown "call" attributes
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewInternal : Shared -> Config -> Side -> List (Html.Attribute msg) -> (Parts -> List (Html msg)) -> Call -> Html msg
|
||||
viewInternal shared config side attributes viewParts call =
|
||||
Card.view
|
||||
"call"
|
||||
config.decks
|
||||
side
|
||||
attributes
|
||||
(viewBody viewParts call)
|
||||
(viewInstructions shared config.rules call)
|
||||
call.details.source
|
||||
|
||||
|
||||
viewBody : (Parts -> List (Html msg)) -> Call -> ViewBody msg
|
||||
viewBody viewParts call =
|
||||
ViewBody (\() -> viewParts call.body)
|
||||
|
||||
|
||||
viewInstructions : Shared -> Rules -> Call -> ViewInstructions msg
|
||||
viewInstructions shared rules call =
|
||||
ViewInstructions (\() -> instructions shared rules call.body)
|
||||
|
||||
|
||||
instructions : Shared -> Rules -> Parts -> List (Html msg)
|
||||
instructions shared rules parts =
|
||||
let
|
||||
slots =
|
||||
Parts.slotCount parts
|
||||
|
||||
instructionViews =
|
||||
List.concat [ extraCardsInstruction shared rules slots, pickInstruction shared slots ]
|
||||
in
|
||||
[ Html.ol [ HtmlA.class "instructions" ] instructionViews ]
|
||||
|> Maybe.justIf (List.length instructionViews > 0)
|
||||
|> Maybe.withDefault []
|
||||
|
||||
|
||||
extraCardsInstruction : Shared -> Rules -> Int -> List (Html msg)
|
||||
extraCardsInstruction shared rules slots =
|
||||
if Maybe.isJust rules.houseRules.packingHeat || slots > 2 then
|
||||
let
|
||||
extraCards =
|
||||
slots - 1
|
||||
in
|
||||
[ Html.li [] [ Draw { numberOfCards = extraCards } |> Lang.html shared ] ]
|
||||
|> Maybe.justIf (extraCards > 0)
|
||||
|> Maybe.withDefault []
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
pickInstruction : Shared -> Int -> List (Html msg)
|
||||
pickInstruction shared slots =
|
||||
[ Html.li [] [ Pick { numberOfCards = slots } |> Lang.html shared ] ]
|
||||
|> Maybe.justIf (slots > 1)
|
||||
|> Maybe.withDefault []
|
||||
@@ -1,17 +1,18 @@
|
||||
module MassiveDecks.Card.Model exposing
|
||||
( Call
|
||||
, Card(..)
|
||||
, Details
|
||||
, Id
|
||||
, Response
|
||||
, Side(..)
|
||||
, Type
|
||||
, UnknownResponse
|
||||
, ViewBody(..)
|
||||
, ViewInstructions(..)
|
||||
, call
|
||||
, frontSide
|
||||
, response
|
||||
)
|
||||
|
||||
import Html exposing (Html)
|
||||
import MassiveDecks.Card.Parts exposing (..)
|
||||
import MassiveDecks.Card.Source.Model exposing (..)
|
||||
|
||||
@@ -40,17 +41,10 @@ frontSide isFront =
|
||||
Back
|
||||
|
||||
|
||||
{-| A general card.
|
||||
-}
|
||||
type Card
|
||||
= C Call
|
||||
| R Response
|
||||
|
||||
|
||||
{-| A call card.
|
||||
-}
|
||||
type alias Call =
|
||||
Type Parts
|
||||
Card Parts
|
||||
|
||||
|
||||
{-| A simple constructor for a call card.
|
||||
@@ -65,7 +59,7 @@ call parts id source =
|
||||
{-| A response card.
|
||||
-}
|
||||
type alias Response =
|
||||
Type String
|
||||
Card String
|
||||
|
||||
|
||||
{-| A simple constructor for a response card.
|
||||
@@ -79,13 +73,13 @@ response text id source =
|
||||
|
||||
{-| A response that isn't known to the user. Only the back can be displayed.
|
||||
-}
|
||||
type alias UnknownResponse =
|
||||
Id
|
||||
type UnknownResponse
|
||||
= UnknownResponse Id
|
||||
|
||||
|
||||
{-| The data for a type of card.
|
||||
-}
|
||||
type alias Type body =
|
||||
type alias Card body =
|
||||
{ details : Details
|
||||
, body : body
|
||||
}
|
||||
@@ -97,3 +91,15 @@ type alias Details =
|
||||
{ source : Source
|
||||
, id : Id
|
||||
}
|
||||
|
||||
|
||||
{-| A function to render the body of a card.
|
||||
-}
|
||||
type ViewBody msg
|
||||
= ViewBody (() -> List (Html msg))
|
||||
|
||||
|
||||
{-| A function to render the instructions for a card.
|
||||
-}
|
||||
type ViewInstructions msg
|
||||
= ViewInstructions (() -> List (Html msg))
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
module MassiveDecks.Card.Response exposing (view, viewUnknown)
|
||||
|
||||
import Html exposing (Html)
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Util.String as String
|
||||
|
||||
|
||||
{-| Render the response to HTML.
|
||||
-}
|
||||
view : Config -> Side -> List (Html.Attribute msg) -> Response -> Html msg
|
||||
view config side attributes response =
|
||||
Card.view
|
||||
"response"
|
||||
config.decks
|
||||
side
|
||||
attributes
|
||||
(viewBody response)
|
||||
viewInstructions
|
||||
response.details.source
|
||||
|
||||
|
||||
{-| Render an unknown response to HTML, face-down.
|
||||
-}
|
||||
viewUnknown : List (Html.Attribute msg) -> Html msg
|
||||
viewUnknown attributes =
|
||||
Card.viewUnknown "response" attributes
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewBody : Response -> ViewBody msg
|
||||
viewBody response =
|
||||
ViewBody (\() -> [ Html.p [] [ Html.span [] [ response.body |> String.capitalise |> Html.text ] ] ])
|
||||
|
||||
|
||||
viewInstructions : ViewInstructions msg
|
||||
viewInstructions =
|
||||
ViewInstructions (\() -> [])
|
||||
@@ -11,7 +11,7 @@ module MassiveDecks.Components.Form.Message exposing
|
||||
)
|
||||
|
||||
import FontAwesome.Attributes as Icon
|
||||
import FontAwesome.Icon as Icon
|
||||
import FontAwesome.Icon as Icon exposing (Icon)
|
||||
import FontAwesome.Solid as Icon
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
@@ -20,11 +20,13 @@ import MassiveDecks.Components as Components
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Strings exposing (MdString)
|
||||
import MassiveDecks.Strings.Languages as Lang
|
||||
import Svg.Attributes as Svg
|
||||
|
||||
|
||||
type alias Fix msg =
|
||||
{ text : MdString
|
||||
, msg : msg
|
||||
{ description : MdString
|
||||
, icon : Icon
|
||||
, action : msg
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +50,7 @@ info mdString =
|
||||
Just
|
||||
{ severity = Info
|
||||
, description = mdString
|
||||
, fix = Nothing
|
||||
, fixes = []
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +59,7 @@ warning mdString =
|
||||
Just
|
||||
{ severity = Warning
|
||||
, description = mdString
|
||||
, fix = Nothing
|
||||
, fixes = []
|
||||
}
|
||||
|
||||
|
||||
@@ -66,16 +68,16 @@ error mdString =
|
||||
Just
|
||||
{ severity = Error
|
||||
, description = mdString
|
||||
, fix = Nothing
|
||||
, fixes = []
|
||||
}
|
||||
|
||||
|
||||
errorWithFix : MdString -> MdString -> msg -> Message msg
|
||||
errorWithFix errorString fixString fix =
|
||||
errorWithFix : MdString -> List (Fix msg) -> Message msg
|
||||
errorWithFix errorString fixes =
|
||||
Just
|
||||
{ severity = Error
|
||||
, description = errorString
|
||||
, fix = Just { text = fixString, msg = fix }
|
||||
, fixes = fixes
|
||||
}
|
||||
|
||||
|
||||
@@ -91,12 +93,12 @@ none =
|
||||
type alias InternalMessage msg =
|
||||
{ severity : Severity
|
||||
, description : MdString
|
||||
, fix : Maybe (Fix msg)
|
||||
, fixes : List (Fix msg)
|
||||
}
|
||||
|
||||
|
||||
internalMessage : Shared -> InternalMessage msg -> Html msg
|
||||
internalMessage shared { severity, description, fix } =
|
||||
internalMessage shared { severity, description, fixes } =
|
||||
let
|
||||
( class, icon ) =
|
||||
case severity of
|
||||
@@ -108,16 +110,20 @@ internalMessage shared { severity, description, fix } =
|
||||
|
||||
Error ->
|
||||
( "inline-error", Icon.exclamationCircle )
|
||||
|
||||
fixLink =
|
||||
case fix of
|
||||
Just { text, msg } ->
|
||||
[ Html.text " ", Components.linkButton [ msg |> HtmlE.onClick ] [ text |> Lang.html shared ] ]
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
in
|
||||
Html.span [ HtmlA.class class ]
|
||||
[ Icon.viewStyled [ Icon.fw ] icon
|
||||
, Html.span [] ((description |> Lang.html shared) :: fixLink)
|
||||
[ Icon.viewStyled [ Icon.fw, Svg.class "message-type-icon" ] icon
|
||||
, Html.span [] [ description |> Lang.html shared ]
|
||||
, Html.ul [ HtmlA.class "fixes" ] (fixes |> List.map (viewFix shared))
|
||||
]
|
||||
|
||||
|
||||
viewFix : Shared -> Fix msg -> Html msg
|
||||
viewFix shared { icon, description, action } =
|
||||
Html.li []
|
||||
[ Components.iconButton
|
||||
[ action |> HtmlE.onClick
|
||||
, description |> Lang.title shared
|
||||
]
|
||||
icon
|
||||
]
|
||||
|
||||
@@ -14,7 +14,7 @@ import FontAwesome.Solid as Icon
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import Html.Events as HtmlE
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Play as Play exposing (Play)
|
||||
import MassiveDecks.Components as Components
|
||||
@@ -32,7 +32,7 @@ import MassiveDecks.Game.Rules as Rules
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Actions as Actions
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Events as Events
|
||||
import MassiveDecks.Pages.Lobby.Messages as Lobby
|
||||
import MassiveDecks.Pages.Lobby.Model as Lobby exposing (Lobby)
|
||||
@@ -40,8 +40,6 @@ import MassiveDecks.Strings as Strings exposing (MdString)
|
||||
import MassiveDecks.Strings.Languages as Lang
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util as Util
|
||||
import MassiveDecks.Util.Html as Html
|
||||
import MassiveDecks.Util.Html.Attributes as HtmlA
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set
|
||||
import Task
|
||||
@@ -88,7 +86,7 @@ update msg model =
|
||||
picks.cards ++ [ id ]
|
||||
|
||||
extra =
|
||||
max 0 (List.length picked - (playingRound.call |> Card.slotCount))
|
||||
max 0 (List.length picked - (playingRound.call |> Call.slotCount))
|
||||
|
||||
newRound =
|
||||
Round.P { playingRound | pick = { picks | cards = picked |> List.drop extra } }
|
||||
@@ -181,9 +179,9 @@ update msg model =
|
||||
( { model | playStyles = playStyles }, Cmd.none )
|
||||
|
||||
AdvanceRound ->
|
||||
case model.nextRound of
|
||||
Just nextRound ->
|
||||
( { model | game = { game | round = Round.P nextRound }, nextRound = Nothing }
|
||||
case model.completeRound of
|
||||
Just _ ->
|
||||
( { model | completeRound = Nothing }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
@@ -199,22 +197,27 @@ subscriptions model =
|
||||
Sub.none
|
||||
|
||||
|
||||
view : Shared -> Lobby.Auth -> List Configure.Deck -> Dict User.Id User -> Model -> Html Global.Msg
|
||||
view shared auth decks users model =
|
||||
view : Shared -> Lobby.Auth -> Config -> Dict User.Id User -> Model -> Html Global.Msg
|
||||
view shared auth config users model =
|
||||
let
|
||||
{ instruction, action, content, fillCallWith } =
|
||||
case game.round of
|
||||
Round.P round ->
|
||||
Playing.view shared auth decks model round
|
||||
case model.completeRound of
|
||||
Just completeRound ->
|
||||
Complete.view shared (Round.C completeRound /= game.round) config users completeRound
|
||||
|
||||
Round.R round ->
|
||||
Revealing.view shared auth decks round
|
||||
Nothing ->
|
||||
case game.round of
|
||||
Round.P round ->
|
||||
Playing.view auth config model round
|
||||
|
||||
Round.J round ->
|
||||
Judging.view shared auth decks round
|
||||
Round.R round ->
|
||||
Revealing.view auth config round
|
||||
|
||||
Round.C round ->
|
||||
Complete.view shared (Maybe.isJust model.nextRound) decks users round
|
||||
Round.J round ->
|
||||
Judging.view auth config round
|
||||
|
||||
Round.C round ->
|
||||
Complete.view shared False config users round
|
||||
|
||||
game =
|
||||
model.game
|
||||
@@ -226,7 +229,7 @@ view shared auth decks users model =
|
||||
fillCallWith |> List.map .body
|
||||
|
||||
renderedCall =
|
||||
call |> Card.viewFilled shared decks Card.Front [] parts
|
||||
call |> Call.viewFilled shared config Card.Front [] parts
|
||||
|
||||
help =
|
||||
let
|
||||
@@ -415,13 +418,29 @@ applyGameEvent auth gameEvent model =
|
||||
|
||||
Events.RoundStarted { id, czar, players, call, drawn } ->
|
||||
let
|
||||
drawnAsList =
|
||||
drawn |> Maybe.withDefault []
|
||||
|
||||
( newRound, cmd ) =
|
||||
Playing.init (Round.playing id czar players call Set.empty) Round.noPick
|
||||
game =
|
||||
model.game
|
||||
in
|
||||
( { model | nextRound = Just newRound, hand = model.hand ++ drawnAsList }, cmd )
|
||||
case model.game.round of
|
||||
Round.C c ->
|
||||
let
|
||||
drawnAsList =
|
||||
drawn |> Maybe.withDefault []
|
||||
|
||||
( newRound, cmd ) =
|
||||
Playing.init (Round.playing id czar players call Set.empty) Round.noPick
|
||||
in
|
||||
( { model
|
||||
| game = { game | round = Round.P newRound }
|
||||
, completeRound = Just c
|
||||
, hand = model.hand ++ drawnAsList
|
||||
}
|
||||
, cmd
|
||||
)
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
( model, Cmd.none )
|
||||
|
||||
Events.StartRevealing { plays, drawn } ->
|
||||
case model.game.round of
|
||||
@@ -458,7 +477,7 @@ applyGameStarted lobby round hand =
|
||||
lobby.users |> Dict.toList |> List.map (\( id, _ ) -> id)
|
||||
|
||||
defaultPlayer =
|
||||
{ control = Player.Human, score = 0 }
|
||||
{ score = 0 }
|
||||
|
||||
game =
|
||||
{ round = Round.P round
|
||||
|
||||
@@ -25,7 +25,7 @@ type alias Model =
|
||||
{ game : Game
|
||||
, hand : List Card.Response
|
||||
, playStyles : PlayStyles
|
||||
, nextRound : Maybe Round.Playing
|
||||
, completeRound : Maybe Round.Complete
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ emptyModel game =
|
||||
{ game = game
|
||||
, hand = []
|
||||
, playStyles = Dict.empty
|
||||
, nextRound = Nothing
|
||||
, completeRound = Nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module MassiveDecks.Game.Player exposing
|
||||
( Control(..)
|
||||
, PlayState(..)
|
||||
( PlayState(..)
|
||||
, Player
|
||||
, Role(..)
|
||||
, Score
|
||||
@@ -19,8 +18,7 @@ import Set
|
||||
{-| A player in the game.
|
||||
-}
|
||||
type alias Player =
|
||||
{ control : Control
|
||||
, score : Score
|
||||
{ score : Score
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +28,6 @@ type alias Score =
|
||||
Int
|
||||
|
||||
|
||||
{-| How the player is being controlled.
|
||||
-}
|
||||
type Control
|
||||
= Human
|
||||
| Computer
|
||||
|
||||
|
||||
{-| The role the player currently has in the game.
|
||||
-}
|
||||
type Role
|
||||
|
||||
@@ -6,14 +6,14 @@ import FontAwesome.Solid as Icon
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import Html.Keyed as HtmlK
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.Strings.Languages as Lang
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
@@ -21,8 +21,8 @@ import MassiveDecks.Util.Html as Html
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
|
||||
|
||||
view : Shared -> Bool -> List Configure.Deck -> Dict User.Id User -> Round.Complete -> RoundView Global.Msg
|
||||
view shared nextRoundReady decks users round =
|
||||
view : Shared -> Bool -> Config -> Dict User.Id User -> Round.Complete -> RoundView Global.Msg
|
||||
view shared nextRoundReady config users round =
|
||||
let
|
||||
winning =
|
||||
round.plays |> Dict.get round.winner
|
||||
@@ -31,22 +31,16 @@ view shared nextRoundReady decks users round =
|
||||
, action = Action.Advance |> Maybe.justIf nextRoundReady
|
||||
, content =
|
||||
HtmlK.ul [ HtmlA.class "complete plays cards" ]
|
||||
(round.plays |> Dict.toList |> List.map (viewPlay shared decks users round.winner))
|
||||
(round.plays |> Dict.toList |> List.map (viewPlay shared config users round.winner))
|
||||
, fillCallWith = winning |> Maybe.withDefault []
|
||||
}
|
||||
|
||||
|
||||
viewPlay :
|
||||
Shared
|
||||
-> List Configure.Deck
|
||||
-> Dict User.Id User
|
||||
-> User.Id
|
||||
-> ( User.Id, List Card.Response )
|
||||
-> ( String, Html Global.Msg )
|
||||
viewPlay shared decks users winner ( id, responses ) =
|
||||
viewPlay : Shared -> Config -> Dict User.Id User -> User.Id -> ( User.Id, List Card.Response ) -> ( String, Html Global.Msg )
|
||||
viewPlay shared config users winner ( id, responses ) =
|
||||
let
|
||||
cards =
|
||||
responses |> List.map (\r -> ( r.details.id, Card.view shared decks Card.Front [] (Card.R r) ))
|
||||
responses |> List.map (\r -> ( r.details.id, Response.view config Card.Front [] r ))
|
||||
|
||||
playedBy =
|
||||
users |> Dict.get id |> Maybe.map .name |> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module MassiveDecks.Game.Round.Judging exposing (view)
|
||||
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Play as Play
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages exposing (..)
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
@@ -11,8 +11,7 @@ import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Game.Round.Plays as Plays
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Messages as Lobby
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
@@ -20,8 +19,8 @@ import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
view : Shared -> Auth -> List Configure.Deck -> Round.Judging -> RoundView Global.Msg
|
||||
view shared auth decks round =
|
||||
view : Auth -> Config -> Round.Judging -> RoundView Global.Msg
|
||||
view auth config round =
|
||||
let
|
||||
role =
|
||||
Player.role (Round.J round) auth.claims.uid
|
||||
@@ -42,7 +41,7 @@ view shared auth decks round =
|
||||
|> Maybe.withDefault []
|
||||
|
||||
details =
|
||||
round.plays |> List.map (playDetails shared decks round.liked)
|
||||
round.plays |> List.map (playDetails config round.liked)
|
||||
in
|
||||
{ instruction = Just instruction
|
||||
, action = action
|
||||
@@ -55,12 +54,12 @@ view shared auth decks round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : Shared -> List Configure.Deck -> Set Play.Id -> Play.Known -> Plays.Details Global.Msg
|
||||
playDetails shared decks liked { id, responses } =
|
||||
playDetails : Config -> Set Play.Id -> Play.Known -> Plays.Details Global.Msg
|
||||
playDetails config liked { id, responses } =
|
||||
let
|
||||
cards =
|
||||
responses
|
||||
|> List.map (\r -> Card.view shared decks Card.Front [] (Card.R r))
|
||||
|> List.map (\r -> Response.view config Card.Front [] r)
|
||||
|
||||
attrs =
|
||||
[ HtmlA.class "liked" ] |> Maybe.justIf (Set.member id liked) |> Maybe.withDefault []
|
||||
|
||||
@@ -8,16 +8,16 @@ import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import Html.Events as HtmlE
|
||||
import Html.Keyed as HtmlK
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages as Game
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Messages as Lobby
|
||||
import MassiveDecks.Pages.Lobby.Model as Lobby
|
||||
import MassiveDecks.Strings as Strings
|
||||
@@ -33,7 +33,7 @@ init round pick =
|
||||
let
|
||||
cmd =
|
||||
round.players
|
||||
|> playStylesGenerator (Card.slotCount round.call)
|
||||
|> playStylesGenerator (Call.slotCount round.call)
|
||||
|> Random.generate (Game.SetPlayStyles >> lift)
|
||||
in
|
||||
( { round | pick = pick }
|
||||
@@ -41,11 +41,11 @@ init round pick =
|
||||
)
|
||||
|
||||
|
||||
view : Shared -> Lobby.Auth -> List Configure.Deck -> Model -> Round.Playing -> RoundView Global.Msg
|
||||
view shared auth decks model round =
|
||||
view : Lobby.Auth -> Config -> Model -> Round.Playing -> RoundView Global.Msg
|
||||
view auth config model round =
|
||||
let
|
||||
slots =
|
||||
Card.slotCount round.call
|
||||
Call.slotCount round.call
|
||||
|
||||
missingFromPick =
|
||||
slots - (round.pick.cards |> List.length)
|
||||
@@ -68,7 +68,7 @@ view shared auth decks model round =
|
||||
|
||||
hand =
|
||||
HtmlK.ul [ HtmlA.classList [ ( "hand", True ), ( "cards", True ), ( "not-playing", notPlaying ) ] ]
|
||||
(model.hand |> List.map (round.pick.cards |> viewHandCard shared decks))
|
||||
(model.hand |> List.map (round.pick.cards |> viewHandCard config))
|
||||
|
||||
backgroundPlays =
|
||||
Html.div [ HtmlA.class "background-plays" ]
|
||||
@@ -94,17 +94,16 @@ view shared auth decks model round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewHandCard : Shared -> List Configure.Deck -> List Card.Id -> Card.Response -> ( String, Html Global.Msg )
|
||||
viewHandCard shared decks picked response =
|
||||
viewHandCard : Config -> List Card.Id -> Card.Response -> ( String, Html Global.Msg )
|
||||
viewHandCard config picked response =
|
||||
( response.details.id
|
||||
, Card.view
|
||||
shared
|
||||
decks
|
||||
, Response.view
|
||||
config
|
||||
Card.Front
|
||||
[ HtmlA.classList [ ( "picked", List.member response.details.id picked ) ]
|
||||
, response.details.id |> Game.Pick |> lift |> HtmlE.onClick
|
||||
]
|
||||
(Card.R response)
|
||||
response
|
||||
)
|
||||
|
||||
|
||||
@@ -123,7 +122,8 @@ viewBackgroundPlay playStyles slots played for =
|
||||
|
||||
viewBackgroundPlayCard : CardStyle -> Html msg
|
||||
viewBackgroundPlayCard playStyle =
|
||||
Card.viewUnknownResponse [ "rotate(" ++ String.fromFloat playStyle.rotation ++ "turn)" |> HtmlA.style "transform" ]
|
||||
Response.viewUnknown
|
||||
[ "rotate(" ++ String.fromFloat playStyle.rotation ++ "turn)" |> HtmlA.style "transform" ]
|
||||
|
||||
|
||||
lift : Game.Msg -> Global.Msg
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
module MassiveDecks.Game.Round.Revealing exposing (view)
|
||||
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Play as Play exposing (Play)
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Messages exposing (..)
|
||||
import MassiveDecks.Game.Model exposing (RoundView)
|
||||
import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Game.Round.Plays as Plays
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Messages as Lobby
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
|
||||
|
||||
view : Shared -> Auth -> List Configure.Deck -> Round.Revealing -> RoundView Global.Msg
|
||||
view shared auth decks round =
|
||||
view : Auth -> Config -> Round.Revealing -> RoundView Global.Msg
|
||||
view auth config round =
|
||||
let
|
||||
role =
|
||||
Player.role (Round.R round) auth.claims.uid
|
||||
@@ -32,10 +32,10 @@ view shared auth decks round =
|
||||
Strings.WaitingForCzarInstruction
|
||||
|
||||
slots =
|
||||
Card.slotCount round.call
|
||||
Call.slotCount round.call
|
||||
|
||||
plays =
|
||||
round.plays |> List.map (playDetails shared decks slots (role == Player.RCzar))
|
||||
round.plays |> List.map (playDetails config slots (role == Player.RCzar))
|
||||
|
||||
-- TODO: Last revealed.
|
||||
lastRevealed =
|
||||
@@ -52,12 +52,12 @@ view shared auth decks round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : Shared -> List Configure.Deck -> Int -> Bool -> Play -> Plays.Details Global.Msg
|
||||
playDetails shared decks slots isCzar { id, responses } =
|
||||
playDetails : Config -> Int -> Bool -> Play -> Plays.Details Global.Msg
|
||||
playDetails config slots isCzar { id, responses } =
|
||||
let
|
||||
cards =
|
||||
responses
|
||||
|> Maybe.map (List.map (\r -> Card.view shared decks Card.Front [] (Card.R r)))
|
||||
|> Maybe.withDefault (List.repeat slots (Card.viewUnknownResponse []))
|
||||
|> Maybe.map (List.map (\r -> Response.view config Card.Front [] r))
|
||||
|> Maybe.withDefault (List.repeat slots (Response.viewUnknown []))
|
||||
in
|
||||
Plays.Details id cards (Reveal id |> Lobby.GameMsg |> Global.LobbyMsg |> Maybe.justIf isCzar) []
|
||||
|
||||
@@ -21,7 +21,7 @@ module MassiveDecks.Models.Decoders exposing
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import Json.Decode as Json
|
||||
import MassiveDecks.Card.Model as Card exposing (Card)
|
||||
import MassiveDecks.Card.Model as Card exposing (Call, Response)
|
||||
import MassiveDecks.Card.Parts as Parts exposing (Part, Parts)
|
||||
import MassiveDecks.Card.Play as Play exposing (Play)
|
||||
import MassiveDecks.Card.Source.Cardcast.Model as Cardcast
|
||||
@@ -242,24 +242,23 @@ rando =
|
||||
|
||||
player : Json.Decoder Player
|
||||
player =
|
||||
Json.map2 Player
|
||||
(Json.field "control" control)
|
||||
Json.map Player
|
||||
(Json.field "score" score)
|
||||
|
||||
|
||||
control : Json.Decoder Player.Control
|
||||
control : Json.Decoder User.Control
|
||||
control =
|
||||
Json.string |> Json.andThen controlByName
|
||||
|
||||
|
||||
controlByName : String -> Json.Decoder Player.Control
|
||||
controlByName : String -> Json.Decoder User.Control
|
||||
controlByName name =
|
||||
case name of
|
||||
"Human" ->
|
||||
Json.succeed Player.Human
|
||||
Json.succeed User.Human
|
||||
|
||||
"Computer" ->
|
||||
Json.succeed Player.Computer
|
||||
Json.succeed User.Computer
|
||||
|
||||
_ ->
|
||||
unknownValue "user controller" name
|
||||
@@ -277,12 +276,13 @@ users =
|
||||
|
||||
user : Json.Decoder User
|
||||
user =
|
||||
Json.map5 User
|
||||
Json.map6 User
|
||||
(Json.field "name" Json.string)
|
||||
(Json.field "presence" userPresence)
|
||||
(Json.field "connection" userConnection)
|
||||
(Json.field "privilege" privilege)
|
||||
(Json.field "role" userRole)
|
||||
(Json.field "control" control)
|
||||
|
||||
|
||||
userConnection : Json.Decoder User.Connection
|
||||
@@ -423,8 +423,7 @@ eventByName name =
|
||||
connection User.Disconnected
|
||||
|
||||
"Joined" ->
|
||||
Json.field "name" Json.string
|
||||
|> Json.andThen (\n -> Events.UserJoined { name = n } |> presence)
|
||||
userJoined |> Json.andThen presence
|
||||
|
||||
"Left" ->
|
||||
presence Events.UserLeft
|
||||
@@ -606,6 +605,14 @@ presence state =
|
||||
(Json.field "user" userId)
|
||||
|
||||
|
||||
userJoined : Json.Decoder Events.PresenceState
|
||||
userJoined =
|
||||
Json.map3 (\n -> \p -> \c -> Events.UserJoined { name = n, privilege = p, control = c })
|
||||
(Json.field "name" Json.string)
|
||||
(Json.maybe (Json.field "privilege" privilege) |> Json.map (Maybe.withDefault User.Unprivileged))
|
||||
(Json.maybe (Json.field "control" control) |> Json.map (Maybe.withDefault User.Human))
|
||||
|
||||
|
||||
publicSet : Json.Decoder Events.ConfigChanged
|
||||
publicSet =
|
||||
Json.map (\public -> Events.PublicSet { public = public })
|
||||
@@ -713,11 +720,6 @@ cardId =
|
||||
Json.string
|
||||
|
||||
|
||||
card : Json.Decoder Card
|
||||
card =
|
||||
Json.oneOf [ call |> Json.map Card.C, response |> Json.map Card.R ]
|
||||
|
||||
|
||||
response : Json.Decoder Card.Response
|
||||
response =
|
||||
Json.map3 Card.response
|
||||
|
||||
@@ -61,7 +61,7 @@ import Weightless.Attributes as WlA
|
||||
|
||||
changeRoute : Route -> Model -> ( Model, Cmd Global.Msg )
|
||||
changeRoute r model =
|
||||
( { model | route = r, lobby = Nothing, selectedUser = Nothing }, Cmd.none )
|
||||
( { model | route = r, lobby = Nothing }, Cmd.none )
|
||||
|
||||
|
||||
init : Shared -> Route -> Maybe Auth -> Route.Fork ( Model, Cmd Global.Msg )
|
||||
@@ -80,7 +80,6 @@ init shared r auth =
|
||||
( { route = r
|
||||
, auth = a
|
||||
, lobby = Nothing
|
||||
, selectedUser = Nothing
|
||||
, configure = Configure.init
|
||||
, notificationId = 0
|
||||
, notifications = []
|
||||
@@ -112,9 +111,6 @@ subscriptions model =
|
||||
update : Msg -> Model -> ( Model, Cmd Global.Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
SelectUser user ->
|
||||
( { model | selectedUser = user }, Cmd.none )
|
||||
|
||||
GameMsg gameMsg ->
|
||||
case model.lobby of
|
||||
Just l ->
|
||||
@@ -173,19 +169,21 @@ update msg model =
|
||||
let
|
||||
newUsers =
|
||||
case state of
|
||||
Events.UserJoined { name } ->
|
||||
Events.UserJoined { name, privilege, control } ->
|
||||
Dict.insert
|
||||
user
|
||||
{ name = name
|
||||
, presence = User.Joined
|
||||
, connection = User.Connected
|
||||
, privilege = User.Unprivileged
|
||||
, privilege = privilege
|
||||
, role = User.Player
|
||||
, control = control
|
||||
}
|
||||
lobby.users
|
||||
|
||||
Events.UserLeft ->
|
||||
Dict.remove user lobby.users
|
||||
lobby.users
|
||||
|> Dict.update user (Maybe.map (\u -> { u | presence = User.Left }))
|
||||
|
||||
message =
|
||||
case state of
|
||||
@@ -199,11 +197,8 @@ update msg model =
|
||||
|
||||
Events.PrivilegeSet { user, privilege } ->
|
||||
let
|
||||
updateUser =
|
||||
\id -> \u -> { u | privilege = privilege } |> Maybe.justIf (user == id) |> Maybe.withDefault u
|
||||
|
||||
users =
|
||||
lobby.users |> Dict.map updateUser
|
||||
lobby.users |> Dict.update user (Maybe.map (\u -> { u | privilege = privilege }))
|
||||
in
|
||||
( { model | lobby = Just { lobby | users = users } }, Cmd.none )
|
||||
|
||||
@@ -236,7 +231,7 @@ update msg model =
|
||||
Util.lift
|
||||
(\notifications -> { model | notifications = notifications })
|
||||
(NotificationMsg >> Global.LobbyMsg)
|
||||
(Animated.update { removeDone = Just notificationAnimationDuration } notificationMsg model.notifications)
|
||||
(Animated.update { removeDone = Just Animated.defaultDuration } notificationMsg model.notifications)
|
||||
|
||||
ToggleInviteDialog ->
|
||||
( { model | inviteDialogOpen = not model.inviteDialogOpen }, Cmd.none )
|
||||
@@ -299,7 +294,7 @@ view shared model =
|
||||
]
|
||||
:: HtmlK.ol [ HtmlA.class "notifications" ] notifications
|
||||
:: (model.lobby
|
||||
|> Maybe.map (viewLobby shared model.configure model.auth model.selectedUser)
|
||||
|> Maybe.map (viewLobby shared model.configure model.auth)
|
||||
|> Maybe.withDefault
|
||||
[ Html.div [ HtmlA.class "loading" ]
|
||||
[ Icon.viewStyled [ Icon.spin, Icon.fa3x ] Icon.circleNotch ]
|
||||
@@ -402,11 +397,6 @@ notificationDuration =
|
||||
3500
|
||||
|
||||
|
||||
notificationAnimationDuration : Int
|
||||
notificationAnimationDuration =
|
||||
200
|
||||
|
||||
|
||||
applySync : Model -> Lobby -> Maybe (List Card.Response) -> Maybe (List Card.Id) -> ( Model, Cmd Global.Msg )
|
||||
applySync model state hand pick =
|
||||
let
|
||||
@@ -500,8 +490,8 @@ username shared users id =
|
||||
|> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
|
||||
|
||||
|
||||
viewLobby : Shared -> Configure.Model -> Auth -> Maybe User.Id -> Lobby -> List (Html Global.Msg)
|
||||
viewLobby shared configure auth selectedUser lobby =
|
||||
viewLobby : Shared -> Configure.Model -> Auth -> Lobby -> List (Html Global.Msg)
|
||||
viewLobby shared configure auth lobby =
|
||||
let
|
||||
game =
|
||||
lobby.game |> Maybe.map .game
|
||||
@@ -510,35 +500,59 @@ viewLobby shared configure auth selectedUser lobby =
|
||||
auth.claims.pvg == User.Privileged
|
||||
in
|
||||
[ Html.div [ HtmlA.id "lobby-content" ]
|
||||
[ viewUsers shared auth.claims.uid selectedUser lobby.users game
|
||||
[ viewUsers shared auth.claims.uid lobby.users game
|
||||
, Html.div [ HtmlA.id "scroll-frame" ]
|
||||
[ lobby.game
|
||||
|> Maybe.map (Game.view shared auth lobby.config.decks lobby.users)
|
||||
|> Maybe.map (Game.view shared auth lobby.config lobby.users)
|
||||
|> Maybe.withDefault (Configure.view shared privileged configure auth.claims.gc lobby lobby.config)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewUsers : Shared -> User.Id -> Maybe User.Id -> Dict User.Id User -> Maybe Game -> Html Global.Msg
|
||||
viewUsers shared player selectedUser users game =
|
||||
viewUsers : Shared -> User.Id -> Dict User.Id User -> Maybe Game -> Html Global.Msg
|
||||
viewUsers shared player users game =
|
||||
let
|
||||
( active, inactive ) =
|
||||
users |> Dict.toList |> List.partition (\( _, user ) -> user.presence == User.Joined)
|
||||
|
||||
activeGroups =
|
||||
active |> byRole |> List.map (viewRoleGroup shared player game)
|
||||
|
||||
inactiveGroup =
|
||||
if List.isEmpty inactive then
|
||||
[]
|
||||
|
||||
else
|
||||
[ viewUserListGroup shared player game ( ( "left", Strings.Left ), inactive ) ]
|
||||
|
||||
groups =
|
||||
List.concat [ activeGroups, inactiveGroup ]
|
||||
in
|
||||
Wl.card [ HtmlA.id "users" ]
|
||||
[ Html.div [ HtmlA.class "collapsible" ]
|
||||
[ users
|
||||
|> Dict.toList
|
||||
|> byRole
|
||||
|> List.map (viewRoleGroup shared player selectedUser game)
|
||||
|> HtmlK.ol []
|
||||
]
|
||||
]
|
||||
[ Html.div [ HtmlA.class "collapsible" ] [ HtmlK.ol [] groups ] ]
|
||||
|
||||
|
||||
viewRoleGroup : Shared -> User.Id -> Maybe User.Id -> Maybe Game -> ( User.Role, List ( User.Id, User ) ) -> ( String, Html Global.Msg )
|
||||
viewRoleGroup shared player selectedUser game ( role, users ) =
|
||||
( role |> roleId
|
||||
, Html.li []
|
||||
[ Html.span [] [ role |> roleDescription |> Lang.html shared ]
|
||||
, HtmlK.ol [] (users |> List.map (viewUser shared player selectedUser game))
|
||||
viewRoleGroup : Shared -> User.Id -> Maybe Game -> ( User.Role, List ( User.Id, User ) ) -> ( String, Html Global.Msg )
|
||||
viewRoleGroup shared player game ( role, users ) =
|
||||
let
|
||||
idAndDescription =
|
||||
case role of
|
||||
User.Player ->
|
||||
( "players", Strings.Players )
|
||||
|
||||
User.Spectator ->
|
||||
( "spectators", Strings.Spectators )
|
||||
in
|
||||
viewUserListGroup shared player game ( idAndDescription, users )
|
||||
|
||||
|
||||
viewUserListGroup : Shared -> User.Id -> Maybe Game -> ( ( String, MdString ), List ( User.Id, User ) ) -> ( String, Html Global.Msg )
|
||||
viewUserListGroup shared player game ( ( id, description ), users ) =
|
||||
( id
|
||||
, Html.li [ HtmlA.class id ]
|
||||
[ Html.span [] [ description |> Lang.html shared ]
|
||||
, HtmlK.ol [] (users |> List.map (viewUser shared player game))
|
||||
]
|
||||
)
|
||||
|
||||
@@ -555,8 +569,8 @@ byRole users =
|
||||
|> List.filter (\( _, us ) -> not (List.isEmpty us))
|
||||
|
||||
|
||||
viewUser : Shared -> User.Id -> Maybe User.Id -> Maybe Game -> ( User.Id, User ) -> ( String, Html Global.Msg )
|
||||
viewUser shared player selectedUser game ( userId, user ) =
|
||||
viewUser : Shared -> User.Id -> Maybe Game -> ( User.Id, User ) -> ( String, Html Global.Msg )
|
||||
viewUser shared player game ( userId, user ) =
|
||||
let
|
||||
( secondary, score ) =
|
||||
userDetails shared game userId user
|
||||
@@ -565,18 +579,49 @@ viewUser shared player selectedUser game ( userId, user ) =
|
||||
"user-" ++ userId
|
||||
|
||||
menuItems =
|
||||
[ Menu.button Icon.userCog Strings.Promote Strings.Promote Nothing
|
||||
, Menu.Separator
|
||||
, Menu.button Icon.userClock Strings.SetAway Strings.SetAway Nothing
|
||||
, Menu.button Icon.ban Strings.KickUser Strings.KickUser Nothing
|
||||
]
|
||||
if user.control == User.Human then
|
||||
let
|
||||
privilegeMenuItems =
|
||||
case user.privilege of
|
||||
User.Unprivileged ->
|
||||
Just [ Menu.button Icon.userPlus Strings.Promote Strings.Promote Nothing ]
|
||||
|
||||
User.Privileged ->
|
||||
Just [ Menu.button Icon.userMinus Strings.Demote Strings.Demote Nothing ]
|
||||
|
||||
presenceMenuItems =
|
||||
case user.presence of
|
||||
User.Joined ->
|
||||
Just
|
||||
[ Menu.button Icon.userClock Strings.SetAway Strings.SetAway Nothing
|
||||
, Menu.button Icon.ban Strings.KickUser Strings.KickUser Nothing
|
||||
]
|
||||
|
||||
User.Left ->
|
||||
Nothing
|
||||
in
|
||||
[ privilegeMenuItems, presenceMenuItems ]
|
||||
|> List.filterMap identity
|
||||
|> List.intersperse [ Menu.Separator ]
|
||||
|> List.concat
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
( menu, clickable ) =
|
||||
if List.isEmpty menuItems then
|
||||
( Html.nothing, HtmlA.nothing )
|
||||
|
||||
else
|
||||
( menuItems |> Menu.view shared id ( WlA.XRight, WlA.YCenter ) ( WlA.XLeft, WlA.YCenter )
|
||||
, WlA.clickable
|
||||
)
|
||||
in
|
||||
( userId
|
||||
, Html.li []
|
||||
[ Wl.listItem
|
||||
[ Just userId |> SelectUser |> Global.LobbyMsg |> HtmlE.onClick
|
||||
, HtmlA.classList [ ( "mdc-menu-surface--anchor", True ), ( "you", player == userId ) ]
|
||||
, WlA.clickable
|
||||
[ HtmlA.classList [ ( "you", player == userId ) ]
|
||||
, clickable
|
||||
, HtmlA.id id
|
||||
]
|
||||
[ Html.div [ HtmlA.class "user compressed-terms" ]
|
||||
@@ -585,7 +630,7 @@ viewUser shared player selectedUser game ( userId, user ) =
|
||||
)
|
||||
, Html.span [ WlA.listItemSlot WlA.AfterItem ] score
|
||||
]
|
||||
, Menu.view shared id ( WlA.XRight, WlA.YCenter ) ( WlA.XLeft, WlA.YCenter ) menuItems
|
||||
, menu
|
||||
]
|
||||
)
|
||||
|
||||
@@ -601,7 +646,7 @@ userDetails shared game userId user =
|
||||
|
||||
details =
|
||||
[ Strings.Privileged |> Maybe.justIf (user.privilege == User.Privileged)
|
||||
, player |> Maybe.andThen (\p -> Strings.Ai |> Maybe.justIf (p.control == Player.Computer))
|
||||
, Strings.Ai |> Maybe.justIf (user.control == User.Computer)
|
||||
, round |> Maybe.andThen (\r -> Strings.Czar |> Maybe.justIf (Player.isCzar r userId))
|
||||
, playStateDetail round userId
|
||||
]
|
||||
@@ -635,26 +680,6 @@ viewDetails shared details =
|
||||
details |> List.filterMap identity |> List.map (Lang.html shared) |> List.intersperse (Html.text " ")
|
||||
|
||||
|
||||
roleDescription : User.Role -> Strings.MdString
|
||||
roleDescription role =
|
||||
case role of
|
||||
User.Player ->
|
||||
Strings.Players
|
||||
|
||||
User.Spectator ->
|
||||
Strings.Spectators
|
||||
|
||||
|
||||
roleId : User.Role -> String
|
||||
roleId role =
|
||||
case role of
|
||||
User.Player ->
|
||||
"players"
|
||||
|
||||
User.Spectator ->
|
||||
"spectators"
|
||||
|
||||
|
||||
viewCastButton : Auth -> List (Html.Attribute Global.Msg) -> List (Html Global.Msg)
|
||||
viewCastButton auth attrs =
|
||||
[ Components.iconButtonStyled
|
||||
|
||||
@@ -142,7 +142,7 @@ view shared canEdit model gameCode lobby config =
|
||||
]
|
||||
]
|
||||
, Wl.tabGroup [ WlA.align WlA.Center ] (tabs |> List.map (tab shared model.tab))
|
||||
, tabContent shared canEdit model lobby config
|
||||
, tabContent shared canEdit model config
|
||||
]
|
||||
, Wl.card []
|
||||
[ startGameSegment shared canEdit lobby config
|
||||
@@ -265,8 +265,11 @@ startGameProblems users config =
|
||||
if noDecks then
|
||||
[ Message.errorWithFix
|
||||
Strings.NeedAtLeastOneDeck
|
||||
Strings.NoDecksHint
|
||||
("CAHBS" |> Cardcast.playCode |> Source.Cardcast |> AddDeck |> lift)
|
||||
[ { description = Strings.NoDecksHint
|
||||
, icon = Icon.plus
|
||||
, action = "CAHBS" |> Cardcast.playCode |> Source.Cardcast |> AddDeck |> lift
|
||||
}
|
||||
]
|
||||
|> Just
|
||||
]
|
||||
|
||||
@@ -283,15 +286,33 @@ startGameProblems users config =
|
||||
]
|
||||
|
||||
playerCount =
|
||||
users |> Dict.values |> List.filter (\user -> user.role == User.Player) |> List.length
|
||||
users
|
||||
|> Dict.values
|
||||
|> List.filter (\user -> user.role == User.Player && user.presence == User.Joined)
|
||||
|> List.length
|
||||
|
||||
aiPlayers =
|
||||
config.rules.houseRules.rando |> Maybe.map .number |> Maybe.withDefault 0
|
||||
|
||||
playerIssues =
|
||||
[ Message.errorWithFix
|
||||
Strings.NeedAtLeastThreePlayers
|
||||
Strings.Invite
|
||||
(Lobby.ToggleInviteDialog |> Global.LobbyMsg)
|
||||
-- TODO: 3! 2 for testing.
|
||||
|> Maybe.justIf (playerCount < 2)
|
||||
[ { description = Strings.Invite
|
||||
, icon = Icon.bullhorn
|
||||
, action = Lobby.ToggleInviteDialog |> Global.LobbyMsg
|
||||
}
|
||||
, { description = Strings.AddAnAiPlayer
|
||||
, icon = Icon.robot
|
||||
, action =
|
||||
{ number = 3 - playerCount + aiPlayers }
|
||||
|> Just
|
||||
|> Rules.RandoChange
|
||||
|> HouseRuleChange Remote
|
||||
|> Lobby.ConfigureMsg
|
||||
|> Global.LobbyMsg
|
||||
}
|
||||
]
|
||||
|> Maybe.justIf (playerCount < 3)
|
||||
]
|
||||
in
|
||||
[ deckIssues, playerIssues ] |> List.concat |> List.filterMap identity
|
||||
@@ -334,28 +355,32 @@ tabName target =
|
||||
Strings.ConfigurePrivacy
|
||||
|
||||
|
||||
tabContent : Shared -> Bool -> Model -> Lobby -> Config -> Html Global.Msg
|
||||
tabContent shared canEdit model lobby config =
|
||||
case model.tab of
|
||||
Decks ->
|
||||
configureDecks shared canEdit model lobby config
|
||||
tabContent : Shared -> Bool -> Model -> Config -> Html Global.Msg
|
||||
tabContent shared canEdit model config =
|
||||
let
|
||||
viewTab =
|
||||
case model.tab of
|
||||
Decks ->
|
||||
configureDecks
|
||||
|
||||
Rules ->
|
||||
configureRules shared canEdit model lobby config
|
||||
Rules ->
|
||||
configureRules
|
||||
|
||||
Privacy ->
|
||||
configureGameSettings shared canEdit model lobby config
|
||||
Privacy ->
|
||||
configureGameSettings
|
||||
in
|
||||
viewTab shared canEdit model config
|
||||
|
||||
|
||||
configureRules : Shared -> Bool -> Model -> Lobby -> Config -> Html Global.Msg
|
||||
configureRules shared canEdit model lobby config =
|
||||
configureRules : Shared -> Bool -> Model -> Config -> Html Global.Msg
|
||||
configureRules shared canEdit model config =
|
||||
Html.div [ HtmlA.class "rules" ]
|
||||
[ Html.div [ HtmlA.class "core-rules" ]
|
||||
[ Html.h3 [] [ Strings.GameRulesTitle |> Lang.html shared ]
|
||||
, handSize shared canEdit model config
|
||||
, scoreLimit shared canEdit model config
|
||||
]
|
||||
, houseRules shared canEdit model lobby config
|
||||
, houseRules shared canEdit model config
|
||||
]
|
||||
|
||||
|
||||
@@ -438,8 +463,8 @@ scoreLimit shared canEdit model config =
|
||||
[ Message.info Strings.ScoreLimitDescription ]
|
||||
|
||||
|
||||
houseRules : Shared -> Bool -> Model -> Lobby -> Config -> Html Global.Msg
|
||||
houseRules shared canEdit model lobby config =
|
||||
houseRules : Shared -> Bool -> Model -> Config -> Html Global.Msg
|
||||
houseRules shared canEdit model config =
|
||||
Html.div [ HtmlA.class "house-rules" ]
|
||||
[ Html.h3 [] [ Strings.HouseRulesTitle |> Lang.html shared ]
|
||||
, rando shared canEdit model config
|
||||
@@ -567,8 +592,8 @@ rebootSettings shared canEdit value localChange =
|
||||
]
|
||||
|
||||
|
||||
configureDecks : Shared -> Bool -> Model -> Lobby -> Config -> Html Global.Msg
|
||||
configureDecks shared canEdit model lobby config =
|
||||
configureDecks : Shared -> Bool -> Model -> Config -> Html Global.Msg
|
||||
configureDecks shared canEdit model config =
|
||||
let
|
||||
hint =
|
||||
if canEdit then
|
||||
@@ -628,8 +653,8 @@ configureDecks shared canEdit model lobby config =
|
||||
)
|
||||
|
||||
|
||||
configureGameSettings : Shared -> Bool -> Model -> Lobby -> Config -> Html Global.Msg
|
||||
configureGameSettings shared canEdit model lobby config =
|
||||
configureGameSettings : Shared -> Bool -> Model -> Config -> Html Global.Msg
|
||||
configureGameSettings shared canEdit model config =
|
||||
let
|
||||
passwordAttrs =
|
||||
List.concat
|
||||
|
||||
@@ -4,6 +4,7 @@ module MassiveDecks.Pages.Lobby.Configure.Model exposing
|
||||
, DeckError
|
||||
, Model
|
||||
, Tab(..)
|
||||
, fake
|
||||
)
|
||||
|
||||
import MassiveDecks.Card.Source.Model as Source
|
||||
@@ -50,3 +51,23 @@ type alias Config =
|
||||
, version : String
|
||||
, public : Bool
|
||||
}
|
||||
|
||||
|
||||
{-| A fake configuration.
|
||||
-}
|
||||
fake : Config
|
||||
fake =
|
||||
{ rules =
|
||||
{ handSize = 0
|
||||
, scoreLimit = Nothing
|
||||
, houseRules =
|
||||
{ rando = Nothing
|
||||
, packingHeat = Nothing
|
||||
, reboot = Nothing
|
||||
}
|
||||
}
|
||||
, decks = []
|
||||
, password = Nothing
|
||||
, version = ""
|
||||
, public = False
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type Event
|
||||
{-| The user's intentional presence in the lobby.
|
||||
-}
|
||||
type PresenceState
|
||||
= UserJoined { name : String }
|
||||
= UserJoined { name : String, privilege : User.Privilege, control : User.Control }
|
||||
| UserLeft
|
||||
|
||||
|
||||
|
||||
@@ -6,12 +6,10 @@ import MassiveDecks.Models.MdError exposing (MdError)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Messages as Configure
|
||||
import MassiveDecks.Pages.Lobby.Events exposing (Event)
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (..)
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
|
||||
|
||||
type Msg
|
||||
= SelectUser (Maybe User.Id)
|
||||
| GameMsg Game.Msg
|
||||
= GameMsg Game.Msg
|
||||
| EventReceived Event
|
||||
| ErrorReceived MdError
|
||||
| ConfigureMsg Configure.Msg
|
||||
|
||||
@@ -28,7 +28,6 @@ type alias Model =
|
||||
, auth : Auth
|
||||
, lobby : Maybe Lobby
|
||||
, configure : Configure.Model
|
||||
, selectedUser : Maybe User.Id
|
||||
, notificationId : NotificationId
|
||||
, notifications : List (Animated Notification)
|
||||
, inviteDialogOpen : Bool
|
||||
|
||||
@@ -11,9 +11,11 @@ import FontAwesome.Solid as Icon
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model as Card exposing (Card)
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Parts as Parts
|
||||
import MassiveDecks.Card.Play as Play exposing (Play)
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Card.Source.Cardcast.Model as Cardcast
|
||||
import MassiveDecks.Card.Source.Model as Source
|
||||
import MassiveDecks.Messages exposing (..)
|
||||
@@ -101,7 +103,7 @@ view shared model =
|
||||
turns 1 / toFloat playCount
|
||||
|
||||
slots =
|
||||
Card.slotCount model.call
|
||||
Call.slotCount model.call
|
||||
|
||||
qr =
|
||||
Route.externalUrl shared.origin (Route.Start { section = Start.Join (Just model.route.lobby.gameCode) })
|
||||
@@ -112,10 +114,9 @@ view shared model =
|
||||
[ Html.div
|
||||
[ HtmlA.class "spectate" ]
|
||||
[ Html.div [ HtmlA.class "middle" ]
|
||||
[ Card.view shared [] Card.Front [] (Card.C model.call)
|
||||
|
||||
--, HtmlA.style "--play-count" (model.plays |> List.length |> String.fromInt)
|
||||
, Html.ul [ HtmlA.class "plays" ] (viewPlays shared slots angle model.plays)
|
||||
[ -- Call.view shared config Card.Front [] model.call
|
||||
--, HtmlA.style "--play-count" (model.plays |> List.length |> String.fromInt)
|
||||
Html.ul [ HtmlA.class "plays" ] (viewPlays shared slots angle model.plays)
|
||||
]
|
||||
]
|
||||
, Html.div [ HtmlA.class "join-info" ]
|
||||
@@ -282,7 +283,7 @@ viewPlayingPlay slots play =
|
||||
Just p ->
|
||||
List.map
|
||||
(\a ->
|
||||
Card.viewUnknownResponse [ rotated a ]
|
||||
Response.viewUnknown [ rotated a ]
|
||||
)
|
||||
(p.animation |> Maybe.withDefault (defaultedRotations slots p.animation))
|
||||
|
||||
@@ -292,7 +293,8 @@ viewPlayingPlay slots play =
|
||||
|
||||
viewKnownPlay : Shared -> Play.Known -> Rotations -> List (Html msg)
|
||||
viewKnownPlay shared play rotations =
|
||||
List.map2 (\c -> \a -> Card.view shared [] Card.Front [ rotated a ] (Card.R c)) play.responses rotations
|
||||
--List.map2 (\c -> \a -> Response.view config Card.Front [ rotated a ] c) play.responses rotations
|
||||
[]
|
||||
|
||||
|
||||
defaultedRotations : Int -> Maybe Rotations -> Rotations
|
||||
|
||||
@@ -9,7 +9,7 @@ module MassiveDecks.Pages.Spectate.Model exposing
|
||||
, playingPlay
|
||||
)
|
||||
|
||||
import MassiveDecks.Card.Model as Card exposing (Card)
|
||||
import MassiveDecks.Card.Model as Card exposing (Call, Response)
|
||||
import MassiveDecks.Card.Play as Play
|
||||
import MassiveDecks.Pages.Spectate.Route exposing (..)
|
||||
import MassiveDecks.User as User
|
||||
|
||||
@@ -14,9 +14,10 @@ import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import Html.Events as HtmlE
|
||||
import Http
|
||||
import MassiveDecks.Card as Card
|
||||
import MassiveDecks.Card.Model as Card exposing (Card)
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Parts as Parts
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Card.Source.Model as Source
|
||||
import MassiveDecks.Components.Form as Form
|
||||
import MassiveDecks.Components.Form.Message as Message
|
||||
@@ -25,6 +26,7 @@ import MassiveDecks.Icon as Icon
|
||||
import MassiveDecks.Messages as Global
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Models.MdError as MdError exposing (MdError)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model as Configure
|
||||
import MassiveDecks.Pages.Lobby.GameCode as GameCode exposing (GameCode)
|
||||
import MassiveDecks.Pages.Lobby.Model as Lobby
|
||||
import MassiveDecks.Pages.Lobby.Token as Token
|
||||
@@ -146,10 +148,10 @@ view : Shared -> Model -> List (Html Global.Msg)
|
||||
view shared model =
|
||||
[ Html.div [ HtmlA.class "page start" ]
|
||||
[ Html.header [ HtmlA.class "title-card" ]
|
||||
[ Html.h1 [] [ Html.div [ HtmlA.class "card-slicer" ] [ Card.viewUnknownCall [] ] ]
|
||||
[ Html.h1 [] [ Html.div [ HtmlA.class "card-slicer" ] [ Call.viewUnknown [] ] ]
|
||||
, Html.div [ HtmlA.class "subtitle" ]
|
||||
[ Html.div [ HtmlA.class "card-slicer" ]
|
||||
[ Card.view shared [] Card.Front [] (subtitleCard shared)
|
||||
[ Response.view Configure.fake Card.Front [] (subtitleCard shared)
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -233,9 +235,9 @@ loadingOrLoaded model =
|
||||
[ model.newLobbyRequest ] |> List.any HttpData.loadingOrLoaded
|
||||
|
||||
|
||||
subtitleCard : Shared -> Card
|
||||
subtitleCard : Shared -> Card.Response
|
||||
subtitleCard shared =
|
||||
Card.response (Strings.ShortGameDescription |> Lang.string shared) "" Source.Fake |> Card.R
|
||||
Card.response (Strings.ShortGameDescription |> Lang.string shared) "" Source.Fake
|
||||
|
||||
|
||||
gameCodeForRoute : Route -> Maybe GameCode
|
||||
@@ -487,7 +489,7 @@ aboutContent shared =
|
||||
, Html.p [] [ Strings.RulesPlaying |> html ]
|
||||
, Html.p [] [ Strings.RulesJudging |> html ]
|
||||
, Html.figure [ HtmlA.class "example-card" ]
|
||||
[ Card.view shared [] Card.Front [] examplePick2
|
||||
[ Call.view shared Configure.fake Card.Front [] examplePick2
|
||||
, Html.figcaption []
|
||||
[ Strings.ExamplePickDescription |> html ]
|
||||
]
|
||||
@@ -516,7 +518,7 @@ houseRule shared ( name, description ) =
|
||||
]
|
||||
|
||||
|
||||
examplePick2 : Card
|
||||
examplePick2 : Card.Call
|
||||
examplePick2 =
|
||||
Card.call
|
||||
(Parts.unsafeFromList
|
||||
@@ -526,4 +528,3 @@ examplePick2 =
|
||||
)
|
||||
""
|
||||
Source.Fake
|
||||
|> Card.C
|
||||
|
||||
@@ -122,6 +122,8 @@ type MdString
|
||||
| PlayersDescription -- A description of a group of users who are playing in the game.
|
||||
| Spectators -- A short term for a group of users who are only spectating the game.
|
||||
| SpectatorsDescription -- A description of a group of users who are only spectating the game.
|
||||
| Left -- A short term for a group of users who have left the game.
|
||||
| LeftDescription -- A description of a group of users who have left the game.
|
||||
| Privileged -- The short term for a player who has privileges over the game.
|
||||
| PrivilegedDescription -- A description of a player who has privileges over the game (e.g: can change settings)
|
||||
| Ai -- The short term for a player who is controlled by the computer.
|
||||
@@ -138,6 +140,7 @@ type MdString
|
||||
| EndGameDescription -- A description of the action of ending the game early.
|
||||
| KickUser -- A short term for the action of forcing a user to leave the game permanently.
|
||||
| Promote -- A short term for the action of allowing a user to edit the game configuration.
|
||||
| Demote -- A short term for the action of no longer allowing a user to edit the game configuration.
|
||||
-- Notifications
|
||||
| UserJoined { username : String } -- A notification that a user has joined the game.
|
||||
| UserLeft { username : String } -- A notification that a user has left the game.
|
||||
@@ -164,6 +167,7 @@ type MdString
|
||||
| ScoreLimitDescription -- The description of the above rule.
|
||||
| NeedAtLeastOneDeck -- A description of the problem that the game needs at least one deck to start.
|
||||
| NeedAtLeastThreePlayers -- A description of the problem that the game needs at least three players to start.
|
||||
| AddAnAiPlayer -- A description of adding an AI player to the game.
|
||||
| PasswordShared -- A warning that game passwords are visible to anyone else in the game.
|
||||
| PasswordNotSecured -- A warning that game passwords are not stored securely and should not be used elsewhere.
|
||||
| LobbyPassword -- A short label for the lobby password.
|
||||
|
||||
@@ -238,7 +238,7 @@ translate mdString =
|
||||
]
|
||||
|
||||
HouseRuleRandoCardrissianNumber ->
|
||||
[ Text "Number of Randos" ]
|
||||
[ Text "AI Players" ]
|
||||
|
||||
HouseRuleRandoCardrissianNumberDescription ->
|
||||
[ Text "The number of AI players that will be in the game." ]
|
||||
@@ -444,6 +444,12 @@ translate mdString =
|
||||
SpectatorsDescription ->
|
||||
[ Text "Users watching the game without playing." ]
|
||||
|
||||
Left ->
|
||||
[ Text "Left" ]
|
||||
|
||||
LeftDescription ->
|
||||
[ Text "Users who have left the game." ]
|
||||
|
||||
Privileged ->
|
||||
[ Text "Owner" ]
|
||||
|
||||
@@ -495,6 +501,9 @@ translate mdString =
|
||||
Promote ->
|
||||
[ Text "Promote" ]
|
||||
|
||||
Demote ->
|
||||
[ Text "Demote" ]
|
||||
|
||||
-- Notifications
|
||||
UserConnected { username } ->
|
||||
[ Text username, Text " has reconnected to the game." ]
|
||||
@@ -582,6 +591,9 @@ translate mdString =
|
||||
NeedAtLeastThreePlayers ->
|
||||
[ Text "You need at least three players to start the game." ]
|
||||
|
||||
AddAnAiPlayer ->
|
||||
[ Text "Add an AI player to the game." ]
|
||||
|
||||
PasswordShared ->
|
||||
[ Text "Anyone in the game can see the password! "
|
||||
, Text "Hiding it above only affects you (useful if streaming, etc…)."
|
||||
|
||||
@@ -117,7 +117,7 @@ enhanceHtml context mdString unenhanced =
|
||||
GameCodeTerm ->
|
||||
term context GameCodeDescription Icon.qrcode unenhanced
|
||||
|
||||
GameCode { code } ->
|
||||
GameCode _ ->
|
||||
[ Html.span
|
||||
[ HtmlA.class "game-code", GameCodeSpecificDescription |> asString context |> HtmlA.title ]
|
||||
unenhanced
|
||||
@@ -138,6 +138,9 @@ enhanceHtml context mdString unenhanced =
|
||||
Spectators ->
|
||||
term context SpectatorsDescription Icon.eye unenhanced
|
||||
|
||||
Left ->
|
||||
term context LeftDescription Icon.signOutAlt unenhanced
|
||||
|
||||
Privileged ->
|
||||
term context PrivilegedDescription Icon.userCog unenhanced
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module MassiveDecks.User exposing
|
||||
( Connection(..)
|
||||
, Control(..)
|
||||
, Id
|
||||
, Presence(..)
|
||||
, Privilege(..)
|
||||
@@ -59,6 +60,13 @@ type Connection
|
||||
| Disconnected
|
||||
|
||||
|
||||
{-| How the user is being controlled.
|
||||
-}
|
||||
type Control
|
||||
= Human
|
||||
| Computer
|
||||
|
||||
|
||||
{-| A request to register a new user in a lobby.
|
||||
-}
|
||||
type alias Registration =
|
||||
@@ -75,4 +83,5 @@ type alias User =
|
||||
, connection : Connection
|
||||
, privilege : Privilege
|
||||
, role : Role
|
||||
, control : Control
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// If the duration is changed, sync to Animated.elm
|
||||
|
||||
.animation {
|
||||
transition: transform 200ms ease-in, opacity 200ms ease-in;
|
||||
opacity: 0;
|
||||
|
||||
@@ -23,6 +23,10 @@ body {
|
||||
min-width: $min-page-width;
|
||||
}
|
||||
|
||||
#game {
|
||||
background: $background url(../../assets/images/background.svg) repeat;
|
||||
}
|
||||
|
||||
.game-code-input {
|
||||
--input-font-family: "Source Code Pro", monospace;
|
||||
}
|
||||
@@ -193,8 +197,8 @@ a,
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.svg-inline--fa {
|
||||
margin-right: 0.3em;
|
||||
.message-type-icon {
|
||||
margin: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +209,8 @@ a,
|
||||
.inline-error {
|
||||
@include message;
|
||||
color: $error;
|
||||
background-color: lighten($error, 55%);
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
.warning {
|
||||
@@ -212,6 +218,20 @@ a,
|
||||
color: #6f6401;
|
||||
}
|
||||
|
||||
.fixes {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0 0 0 auto;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
|
||||
wl-button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remove-from-flow {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@@ -121,6 +121,10 @@
|
||||
li {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.left {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $users-width * 2) {
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"allowTypedFunctionExpressions": true,
|
||||
"allowHigherOrderFunctions": true
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
"@typescript-eslint/no-empty-interface": "off"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+203
-155
@@ -5,18 +5,18 @@
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
|
||||
"integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
||||
"integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
|
||||
"integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
|
||||
"integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.0",
|
||||
@@ -154,6 +154,12 @@
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/json-schema": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
|
||||
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-stable-stringify": {
|
||||
"version": "1.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz",
|
||||
@@ -237,9 +243,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
|
||||
"integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==",
|
||||
"version": "3.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz",
|
||||
"integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
@@ -262,12 +268,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.11.0.tgz",
|
||||
"integrity": "sha512-mXv9ccCou89C8/4avKHuPB2WkSZyY/XcTQUXd5LFZAcLw1I3mWYVjUu6eS9Ja0QkP/ClolbcW9tb3Ov/pMdcqw==",
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
|
||||
"integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/experimental-utils": "1.11.0",
|
||||
"@typescript-eslint/experimental-utils": "1.13.0",
|
||||
"eslint-utils": "^1.3.1",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"regexpp": "^2.0.1",
|
||||
@@ -275,31 +281,32 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.11.0.tgz",
|
||||
"integrity": "sha512-7LbfaqF6B8oa8cp/315zxKk8FFzosRzzhF8Kn/ZRsRsnpm7Qcu25cR/9RnAQo5utZ2KIWVgaALr+ZmcbG47ruw==",
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz",
|
||||
"integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "1.11.0",
|
||||
"@types/json-schema": "^7.0.3",
|
||||
"@typescript-eslint/typescript-estree": "1.13.0",
|
||||
"eslint-scope": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.11.0.tgz",
|
||||
"integrity": "sha512-5xBExyXaxVyczrZvbRKEXvaTUFFq7gIM9BynXukXZE0zF3IQP/FxF4mPmmh3gJ9egafZFqByCpPTFm3dk4SY7Q==",
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz",
|
||||
"integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-visitor-keys": "^1.0.0",
|
||||
"@typescript-eslint/experimental-utils": "1.11.0",
|
||||
"@typescript-eslint/typescript-estree": "1.11.0",
|
||||
"@typescript-eslint/experimental-utils": "1.13.0",
|
||||
"@typescript-eslint/typescript-estree": "1.13.0",
|
||||
"eslint-visitor-keys": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.11.0.tgz",
|
||||
"integrity": "sha512-fquUHF5tAx1sM2OeRCC7wVxFd1iMELWMGCzOSmJ3pLzArj9+kRixdlC4d5MncuzXpjEqc6045p3KwM0o/3FuUA==",
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz",
|
||||
"integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.unescape": "4.0.1",
|
||||
@@ -330,9 +337,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
|
||||
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz",
|
||||
"integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
@@ -342,9 +349,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -871,12 +878,6 @@
|
||||
"text-hex": "1.0.x"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
|
||||
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
|
||||
"dev": true
|
||||
},
|
||||
"component-emitter": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
|
||||
@@ -912,9 +913,9 @@
|
||||
}
|
||||
},
|
||||
"content-security-policy-builder": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz",
|
||||
"integrity": "sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz",
|
||||
"integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ=="
|
||||
},
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
@@ -1067,9 +1068,9 @@
|
||||
}
|
||||
},
|
||||
"dns-prefetch-control": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz",
|
||||
"integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI="
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz",
|
||||
"integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q=="
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "3.0.0",
|
||||
@@ -1081,9 +1082,9 @@
|
||||
}
|
||||
},
|
||||
"dont-sniff-mimetype": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz",
|
||||
"integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz",
|
||||
"integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug=="
|
||||
},
|
||||
"dot-prop": {
|
||||
"version": "4.2.0",
|
||||
@@ -1157,9 +1158,9 @@
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"eslint": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz",
|
||||
"integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz",
|
||||
"integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
@@ -1168,7 +1169,7 @@
|
||||
"cross-spawn": "^6.0.5",
|
||||
"debug": "^4.0.1",
|
||||
"doctrine": "^3.0.0",
|
||||
"eslint-scope": "^4.0.3",
|
||||
"eslint-scope": "^5.0.0",
|
||||
"eslint-utils": "^1.3.1",
|
||||
"eslint-visitor-keys": "^1.0.0",
|
||||
"espree": "^6.0.0",
|
||||
@@ -1176,30 +1177,37 @@
|
||||
"esutils": "^2.0.2",
|
||||
"file-entry-cache": "^5.0.1",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"glob-parent": "^3.1.0",
|
||||
"glob-parent": "^5.0.0",
|
||||
"globals": "^11.7.0",
|
||||
"ignore": "^4.0.6",
|
||||
"import-fresh": "^3.0.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"inquirer": "^6.2.2",
|
||||
"inquirer": "^6.4.1",
|
||||
"is-glob": "^4.0.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.3.0",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash": "^4.17.14",
|
||||
"minimatch": "^3.0.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.8.2",
|
||||
"progress": "^2.0.0",
|
||||
"regexpp": "^2.0.1",
|
||||
"semver": "^5.5.1",
|
||||
"strip-ansi": "^4.0.0",
|
||||
"strip-json-comments": "^2.0.1",
|
||||
"semver": "^6.1.2",
|
||||
"strip-ansi": "^5.2.0",
|
||||
"strip-json-comments": "^3.0.1",
|
||||
"table": "^5.2.3",
|
||||
"text-table": "^0.2.0"
|
||||
"text-table": "^0.2.0",
|
||||
"v8-compile-cache": "^2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||
@@ -1211,6 +1219,14 @@
|
||||
"semver": "^5.5.0",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
@@ -1222,11 +1238,57 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
|
||||
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esrecurse": "^4.1.0",
|
||||
"estraverse": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz",
|
||||
"integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"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
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
|
||||
"integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1259,10 +1321,13 @@
|
||||
}
|
||||
},
|
||||
"eslint-utils": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
|
||||
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
|
||||
"dev": true
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-visitor-keys": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
@@ -1420,9 +1485,9 @@
|
||||
"integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng=="
|
||||
},
|
||||
"express-winston": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/express-winston/-/express-winston-3.2.0.tgz",
|
||||
"integrity": "sha512-AcVSV016U7aPs1Uknoil962SU124UgNkdakP564X6w+lH0oS1FhV3uctUj2/dOog7np4uvsAHhBmSoICjZMDmA==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express-winston/-/express-winston-3.2.1.tgz",
|
||||
"integrity": "sha512-yh49mpFcoLAFs4yuplIutTAqs+jgRQYB2bLoDTpMvQDARzQKHvABfj2PYMPfmk087+OsEBJmm96i6//q9RJnnw==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.1",
|
||||
"lodash": "^4.17.10"
|
||||
@@ -1458,9 +1523,9 @@
|
||||
}
|
||||
},
|
||||
"external-editor": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
|
||||
"integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chardet": "^0.7.0",
|
||||
@@ -2406,25 +2471,25 @@
|
||||
"integrity": "sha512-dEHCG2LraR6PNvSGxosZHIRgxF5sNLOIBFEHbj8lfP9WWmu/PWPMzsip1drdVSOFi51N2pU7gZavrgn7sbGFuw=="
|
||||
},
|
||||
"helmet": {
|
||||
"version": "3.18.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.18.0.tgz",
|
||||
"integrity": "sha512-TsKlGE5UVkV0NiQ4PllV9EVfZklPjyzcMEMjWlyI/8S6epqgRT+4s4GHVgc25x0TixsKvp3L7c91HQQt5l0+QA==",
|
||||
"version": "3.20.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-3.20.0.tgz",
|
||||
"integrity": "sha512-Ob+TqmQFZ5f7WgP8kBbAzNPsbf6p1lOj5r+327/ymw/IILWih3wcx9u/u/S8Mwv5wbBkO7Li6x5s23t3COhUKw==",
|
||||
"requires": {
|
||||
"depd": "2.0.0",
|
||||
"dns-prefetch-control": "0.1.0",
|
||||
"dont-sniff-mimetype": "1.0.0",
|
||||
"dns-prefetch-control": "0.2.0",
|
||||
"dont-sniff-mimetype": "1.1.0",
|
||||
"expect-ct": "0.2.0",
|
||||
"feature-policy": "0.3.0",
|
||||
"frameguard": "3.1.0",
|
||||
"helmet-crossdomain": "0.3.0",
|
||||
"helmet-csp": "2.7.1",
|
||||
"hide-powered-by": "1.0.0",
|
||||
"helmet-crossdomain": "0.4.0",
|
||||
"helmet-csp": "2.8.0",
|
||||
"hide-powered-by": "1.1.0",
|
||||
"hpkp": "2.0.0",
|
||||
"hsts": "2.2.0",
|
||||
"ienoopen": "1.1.0",
|
||||
"nocache": "2.1.0",
|
||||
"referrer-policy": "1.2.0",
|
||||
"x-xss-protection": "1.1.0"
|
||||
"x-xss-protection": "1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"depd": {
|
||||
@@ -2435,25 +2500,25 @@
|
||||
}
|
||||
},
|
||||
"helmet-crossdomain": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.3.0.tgz",
|
||||
"integrity": "sha512-YiXhj0E35nC4Na5EPE4mTfoXMf9JTGpN4OtB4aLqShKuH9d2HNaJX5MQoglO6STVka0uMsHyG5lCut5Kzsy7Lg=="
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz",
|
||||
"integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA=="
|
||||
},
|
||||
"helmet-csp": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.1.tgz",
|
||||
"integrity": "sha512-sCHwywg4daQ2mY0YYwXSZRsgcCeerUwxMwNixGA7aMLkVmPTYBl7gJoZDHOZyXkqPrtuDT3s2B1A+RLI7WxSdQ==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.8.0.tgz",
|
||||
"integrity": "sha512-MlCPeM0Sm3pS9RACRihx70VeTHmkQwa7sum9EK1tfw1VZyvFU0dBWym9nHh3CRkTRNlyNm/WFCMvuh9zXkOjNw==",
|
||||
"requires": {
|
||||
"camelize": "1.0.0",
|
||||
"content-security-policy-builder": "2.0.0",
|
||||
"content-security-policy-builder": "2.1.0",
|
||||
"dasherize": "2.0.0",
|
||||
"platform": "1.3.5"
|
||||
}
|
||||
},
|
||||
"hide-powered-by": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz",
|
||||
"integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz",
|
||||
"integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg=="
|
||||
},
|
||||
"hpkp": {
|
||||
"version": "2.0.0",
|
||||
@@ -2518,9 +2583,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
|
||||
"integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
|
||||
"integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
@@ -2561,9 +2626,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz",
|
||||
"integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz",
|
||||
"integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^3.2.0",
|
||||
@@ -2572,7 +2637,7 @@
|
||||
"cli-width": "^2.0.0",
|
||||
"external-editor": "^3.0.3",
|
||||
"figures": "^2.0.0",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash": "^4.17.12",
|
||||
"mute-stream": "0.0.7",
|
||||
"run-async": "^2.2.0",
|
||||
"rxjs": "^6.4.0",
|
||||
@@ -2587,6 +2652,12 @@
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
@@ -2976,9 +3047,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
@@ -3175,9 +3246,9 @@
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
|
||||
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
|
||||
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"for-in": "^1.0.2",
|
||||
@@ -3954,9 +4025,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"set-value": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
|
||||
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
||||
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
@@ -4269,13 +4340,13 @@
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz",
|
||||
"integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==",
|
||||
"version": "5.4.4",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-5.4.4.tgz",
|
||||
"integrity": "sha512-IIfEAUx5QlODLblLrGTTLJA7Tk0iLSGBvgY8essPRVNGHAzThujww1YqHLs6h3HfTg55h++RzLHH5Xw/rfv+mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.9.1",
|
||||
"lodash": "^4.17.11",
|
||||
"ajv": "^6.10.2",
|
||||
"lodash": "^4.17.14",
|
||||
"slice-ansi": "^2.1.0",
|
||||
"string-width": "^3.0.0"
|
||||
},
|
||||
@@ -4286,6 +4357,12 @@
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
@@ -4410,18 +4487,6 @@
|
||||
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
|
||||
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
|
||||
},
|
||||
"ts-json-schema-generator": {
|
||||
"version": "0.50.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-0.50.2.tgz",
|
||||
"integrity": "sha512-S6n8kHpU2y7IJvr07rA2Xn9wbKRWTs31jvgpFQnkWn4n7jWXOj/yGEzp1UN/qQOBB0r3qSZjsOMr2KzuaSWTGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "~2.20.0",
|
||||
"glob": "~7.1.4",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
"typescript": "~3.5.2"
|
||||
}
|
||||
},
|
||||
"ts-postgres": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-postgres/-/ts-postgres-1.1.0.tgz",
|
||||
@@ -4454,9 +4519,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.14.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz",
|
||||
"integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==",
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.1.tgz",
|
||||
"integrity": "sha512-kiuZzD1uUA5DxGj/uxbde+ymp6VVdAxdzOIlAFbYKrPyla8/uiJ9JLBm1QsPhOm4Muj0/+cWEDP99yoCUcSl6Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
@@ -4481,9 +4546,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
|
||||
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript-json-schema": {
|
||||
@@ -4543,38 +4608,15 @@
|
||||
}
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
|
||||
"integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
|
||||
"integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arr-union": "^3.1.0",
|
||||
"get-value": "^2.0.6",
|
||||
"is-extendable": "^0.1.1",
|
||||
"set-value": "^0.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"set-value": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
|
||||
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"is-extendable": "^0.1.1",
|
||||
"is-plain-object": "^2.0.1",
|
||||
"to-object-path": "^0.3.0"
|
||||
}
|
||||
}
|
||||
"set-value": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"unique-string": {
|
||||
@@ -4705,6 +4747,12 @@
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
|
||||
"integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
|
||||
"dev": true
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@@ -4856,9 +4904,9 @@
|
||||
"integrity": "sha1-fnLj+6I+D/Zp3dU34chX09SxYU8="
|
||||
},
|
||||
"x-xss-protection": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.1.0.tgz",
|
||||
"integrity": "sha512-rx3GzJlgEeZ08MIcDsU2vY2B1QEriUKJTSiNHHUIem6eg9pzVOr2TL3Y4Pd6TMAM5D5azGjcxqI62piITBDHVg=="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.2.0.tgz",
|
||||
"integrity": "sha512-xN0kV+8XfOQM2OPPBdEbGtbvJNNP1pvZR7sE6d44cjJFQG4OiGDdienPg5iOUGswBTiGbBvtYDURd30BMJwwqg=="
|
||||
},
|
||||
"xdg-basedir": {
|
||||
"version": "3.0.0",
|
||||
|
||||
+8
-8
@@ -25,16 +25,16 @@
|
||||
"config.json5"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "^6.10.0",
|
||||
"ajv": "^6.10.2",
|
||||
"axios": "^0.19.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"express-winston": "^3.2.0",
|
||||
"express-winston": "^3.2.1",
|
||||
"express-ws": "^4.0.0",
|
||||
"generic-pool": "^3.7.1",
|
||||
"hashids": "^1.2.2",
|
||||
"helmet": "^3.18.0",
|
||||
"helmet": "^3.20.0",
|
||||
"http-status-codes": "^1.3.2",
|
||||
"json5": "^2.1.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
@@ -55,16 +55,16 @@
|
||||
"@types/json5": "0.0.30",
|
||||
"@types/jsonwebtoken": "^8.3.2",
|
||||
"@types/source-map-support": "^0.5.0",
|
||||
"@types/uuid": "^3.4.4",
|
||||
"@types/uuid": "^3.4.5",
|
||||
"@types/wu": "^2.1.39",
|
||||
"@typescript-eslint/eslint-plugin": "^1.11.0",
|
||||
"@typescript-eslint/parser": "^1.11.0",
|
||||
"eslint": "^6.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^1.13.0",
|
||||
"@typescript-eslint/parser": "^1.13.0",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"prettier": "1.18.2",
|
||||
"typescript": "^3.5.2",
|
||||
"typescript": "^3.5.3",
|
||||
"typescript-json-validator": "^2.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as rules from "../../../games/rules";
|
||||
import { Handler } from "../../handler";
|
||||
import * as configure from "../configure";
|
||||
import * as houseRuleChanged from "../../../events/lobby-event/configured/house-rule-changed";
|
||||
import * as rando from "../../../games/rules/rando";
|
||||
|
||||
/**
|
||||
* Set the hand size for the lobby.
|
||||
@@ -24,24 +25,40 @@ export const is = (action: Action): action is ChangeHouseRule =>
|
||||
action.action === name;
|
||||
|
||||
export const handle: Handler<ChangeHouseRule> = (auth, lobby, action) => {
|
||||
const houseRules = lobby.config.rules.houseRules;
|
||||
const hr = lobby.config.rules.houseRules;
|
||||
const events = [];
|
||||
let changed = false;
|
||||
switch (action.change.houseRule) {
|
||||
case "PackingHeat":
|
||||
houseRules.packingHeat = action.change.settings;
|
||||
if (hr.packingHeat !== action.change.settings) {
|
||||
hr.packingHeat = action.change.settings;
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case "Rando":
|
||||
houseRules.rando = action.change.settings;
|
||||
const userEvents = rando.change(lobby, hr.rando, action.change.settings);
|
||||
if (userEvents !== null) {
|
||||
events.push(...userEvents);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case "Reboot":
|
||||
houseRules.reboot = action.change.settings;
|
||||
if (hr.reboot !== action.change.settings) {
|
||||
hr.reboot = action.change.settings;
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
lobby.config.version += 1;
|
||||
|
||||
return {
|
||||
lobby,
|
||||
events: [
|
||||
if (changed) {
|
||||
lobby.config.version += 1;
|
||||
events.push(
|
||||
event.targetAll(houseRuleChanged.of(action.change, lobby.config.version))
|
||||
]
|
||||
};
|
||||
);
|
||||
return {
|
||||
lobby,
|
||||
events
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as card from "../../games/cards/card";
|
||||
import * as round from "../../games/game/round";
|
||||
import * as publicRound from "../../games/game/round/public";
|
||||
|
||||
/**
|
||||
@@ -11,10 +12,10 @@ export interface GameStarted {
|
||||
}
|
||||
|
||||
export const of = (
|
||||
round: publicRound.Playing,
|
||||
startedRound: round.Playing,
|
||||
hand?: card.Response[]
|
||||
): GameStarted => ({
|
||||
event: "GameStarted",
|
||||
round,
|
||||
round: round.censor(startedRound),
|
||||
...(hand !== undefined ? { hand } : {})
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as card from "../../games/cards/card";
|
||||
import { Round } from "../../games/game/round";
|
||||
import * as user from "../../user";
|
||||
import * as round from "../../games/game/round";
|
||||
|
||||
/**
|
||||
* Indicates a new round has started.
|
||||
@@ -14,17 +14,11 @@ export interface RoundStarted {
|
||||
drawn?: card.Response[];
|
||||
}
|
||||
|
||||
export const of = (
|
||||
id: round.Id,
|
||||
czar: user.Id,
|
||||
players: user.Id[],
|
||||
call: card.Call,
|
||||
drawn?: card.Response[]
|
||||
): RoundStarted => ({
|
||||
export const of = (round: Round, drawn?: card.Response[]): RoundStarted => ({
|
||||
event: "RoundStarted",
|
||||
id: id.toString(),
|
||||
czar,
|
||||
players,
|
||||
call,
|
||||
id: round.id.toString(),
|
||||
czar: round.czar,
|
||||
players: Array.from(round.players.keys()),
|
||||
call: round.call,
|
||||
...(drawn === undefined ? {} : { drawn })
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { User } from "../../user";
|
||||
import * as user from "../../user";
|
||||
|
||||
/**
|
||||
@@ -15,12 +16,16 @@ interface Base {
|
||||
export interface Joined extends Base {
|
||||
event: "Joined";
|
||||
name: user.Name;
|
||||
privilege?: user.Privilege;
|
||||
control?: user.Control;
|
||||
}
|
||||
|
||||
export const joined = (user: user.Id, name: user.Name): Joined => ({
|
||||
export const joined = (id: user.Id, user: User): Joined => ({
|
||||
event: "Joined",
|
||||
user,
|
||||
name
|
||||
user: id,
|
||||
name: user.name,
|
||||
...(user.privilege !== "Unprivileged" ? { privilege: user.privilege } : {}),
|
||||
...(user.control !== "Human" ? { control: user.control } : {})
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
+103
-5
@@ -1,7 +1,16 @@
|
||||
import * as event from "../event";
|
||||
import * as gameStarted from "../events/game-event/game-started";
|
||||
import * as playSubmitted from "../events/game-event/play-submitted";
|
||||
import * as roundStarted from "../events/game-event/round-started";
|
||||
import { ServerState } from "../server-state";
|
||||
import * as finishedPlaying from "../timeout/finished-playing";
|
||||
import { User } from "../user";
|
||||
import * as user from "../user";
|
||||
import * as util from "../util";
|
||||
import * as card from "./cards/card";
|
||||
import { Decks } from "./cards/decks";
|
||||
import * as decks from "./cards/decks";
|
||||
import * as play from "./cards/play";
|
||||
import * as round from "./game/round";
|
||||
import { Round } from "./game/round";
|
||||
import * as publicRound from "./game/round/public";
|
||||
@@ -10,6 +19,8 @@ import * as player from "./player";
|
||||
import { Player } from "./player";
|
||||
import * as rules from "./rules";
|
||||
import { Rules } from "./rules";
|
||||
import * as lobby from "../lobby";
|
||||
import * as timeout from "../timeout";
|
||||
import wu from "wu";
|
||||
|
||||
/**
|
||||
@@ -45,7 +56,6 @@ export const censor: (game: Game) => Public = game => ({
|
||||
|
||||
const newPlayerForUser = (decks: Decks, rules: Rules): Player => ({
|
||||
hand: decks.responses.draw(rules.handSize),
|
||||
control: "Human",
|
||||
score: 0
|
||||
});
|
||||
|
||||
@@ -79,8 +89,96 @@ export const start = (
|
||||
};
|
||||
};
|
||||
|
||||
export const nextCzar = (game: Game): user.Id => {
|
||||
const current = game.round.czar;
|
||||
const index = game.playerOrder.findIndex(id => id === current) + 1;
|
||||
return game.playerOrder[index >= game.playerOrder.length ? 0 : index];
|
||||
export const atStartOfRound = (
|
||||
server: ServerState,
|
||||
first: boolean,
|
||||
game: Game & { round: round.Playing }
|
||||
): {
|
||||
game: Game & { round: round.Playing };
|
||||
events?: Iterable<event.Distributor>;
|
||||
timeouts?: Iterable<timeout.TimeoutAfter>;
|
||||
} => {
|
||||
const slotCount = card.slotCount(game.round.call);
|
||||
|
||||
const events = [];
|
||||
if (
|
||||
slotCount > 2 ||
|
||||
(slotCount === 2 && game.rules.houseRules.packingHeat !== undefined)
|
||||
) {
|
||||
const responseDeck = game.decks.responses;
|
||||
const drawnByPlayer = new Map();
|
||||
for (const [id, playerState] of game.players) {
|
||||
if (player.role(game, id) === "Player") {
|
||||
const drawn = responseDeck.draw(slotCount - 1);
|
||||
drawnByPlayer.set(id, { drawn });
|
||||
playerState.hand.push(...drawn);
|
||||
}
|
||||
}
|
||||
if (!first) {
|
||||
events.push(
|
||||
event.additionally(roundStarted.of(game.round), drawnByPlayer)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!first) {
|
||||
events.push(event.targetAll(roundStarted.of(game.round)));
|
||||
}
|
||||
}
|
||||
|
||||
if (first) {
|
||||
events.push(
|
||||
event.playerSpecificAddition(
|
||||
gameStarted.of(game.round),
|
||||
(id, user, player) => ({
|
||||
hand: player.hand
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const ais = game.rules.houseRules.rando.current;
|
||||
for (const ai of ais) {
|
||||
const player = game.players.get(ai) as Player;
|
||||
const plays = game.round.plays;
|
||||
const playId = play.id();
|
||||
plays.push({
|
||||
id: playId,
|
||||
play: player.hand.slice(0, slotCount),
|
||||
playedBy: ai,
|
||||
revealed: false
|
||||
});
|
||||
events.push(event.targetAll(playSubmitted.of(ai)));
|
||||
}
|
||||
|
||||
const timeouts = [];
|
||||
const timeout = finishedPlaying.ifNeeded(game.round);
|
||||
if (timeout !== undefined) {
|
||||
timeouts.push({
|
||||
timeout: timeout,
|
||||
after: server.config.timeouts.finishedPlayingDelay
|
||||
});
|
||||
}
|
||||
|
||||
return { game, events, timeouts };
|
||||
};
|
||||
|
||||
const canBeCzar = (user: User): boolean =>
|
||||
user.control !== "Computer" &&
|
||||
user.presence === "Joined" &&
|
||||
user.role === "Player";
|
||||
|
||||
const czarAfter = (lobby: lobby.WithActiveGame, nextIndex: number): user.Id => {
|
||||
const game = lobby.game;
|
||||
const potentialCzar =
|
||||
game.playerOrder[nextIndex >= game.playerOrder.length ? 0 : nextIndex];
|
||||
return canBeCzar(lobby.users.get(potentialCzar) as User)
|
||||
? potentialCzar
|
||||
: czarAfter(lobby, nextIndex + 1);
|
||||
};
|
||||
|
||||
export const nextCzar = (lobby: lobby.WithActiveGame): user.Id => {
|
||||
const game = lobby.game;
|
||||
const current = game.round.czar;
|
||||
const nextIndex = game.playerOrder.findIndex(id => id === current) + 1;
|
||||
return czarAfter(lobby, nextIndex);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Game } from "./game";
|
||||
*/
|
||||
export interface Player {
|
||||
hand: Hand;
|
||||
control: Control;
|
||||
score: Score;
|
||||
}
|
||||
|
||||
@@ -15,7 +14,6 @@ export interface Player {
|
||||
* A player containing only state all users can see.
|
||||
*/
|
||||
export interface Public {
|
||||
control: Control;
|
||||
score: Score;
|
||||
}
|
||||
|
||||
@@ -24,11 +22,6 @@ export interface Public {
|
||||
*/
|
||||
export type Role = "Czar" | "Player";
|
||||
|
||||
/**
|
||||
* Who controls the player.
|
||||
*/
|
||||
export type Control = "Human" | "Computer";
|
||||
|
||||
/**
|
||||
* How many points the player has scored.
|
||||
* @TJS-type integer
|
||||
@@ -40,7 +33,6 @@ export type Score = number;
|
||||
* Produce a public version of the given player.
|
||||
*/
|
||||
export const censor = (player: Player): Public => ({
|
||||
control: player.control,
|
||||
score: player.score
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
/** The rules for a standard game.*/
|
||||
import { HouseRules } from "./rules/houseRules";
|
||||
import * as houseRules from "./rules/houseRules";
|
||||
import * as rando from "./rules/rando";
|
||||
|
||||
/** The rules for a standard game.
|
||||
*/
|
||||
export interface Rules {
|
||||
/**
|
||||
* The number of cards in each player's hand.
|
||||
@@ -21,13 +26,7 @@ export interface Rules {
|
||||
export interface Public {
|
||||
handSize: number;
|
||||
scoreLimit?: number;
|
||||
houseRules: HouseRules;
|
||||
}
|
||||
|
||||
export interface HouseRules {
|
||||
packingHeat?: PackingHeat;
|
||||
reboot?: Reboot;
|
||||
rando?: Rando;
|
||||
houseRules: houseRules.Public;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,13 +35,12 @@ export interface HouseRules {
|
||||
export const create = (): Rules => ({
|
||||
handSize: 10,
|
||||
scoreLimit: 25,
|
||||
houseRules: {}
|
||||
houseRules: houseRules.create()
|
||||
});
|
||||
|
||||
/**
|
||||
* Configuration for the "Packing Heat" house rule.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface PackingHeat {}
|
||||
|
||||
/**
|
||||
@@ -60,18 +58,9 @@ export interface Reboot {
|
||||
cost: number;
|
||||
}
|
||||
|
||||
export interface Rando {
|
||||
/**
|
||||
* The number of AI players to add to the game.
|
||||
* @TJS-type integer
|
||||
* @minimum 1
|
||||
* @maximum 10
|
||||
*/
|
||||
number: number;
|
||||
}
|
||||
|
||||
export const censor = (rules: Rules): Public => ({
|
||||
...rules
|
||||
...rules,
|
||||
houseRules: houseRules.censor(rules.houseRules)
|
||||
});
|
||||
|
||||
export interface ChangeBase<Name extends string, HouseRule> {
|
||||
@@ -80,7 +69,7 @@ export interface ChangeBase<Name extends string, HouseRule> {
|
||||
}
|
||||
|
||||
export type ChangePackingHeat = ChangeBase<"PackingHeat", PackingHeat>;
|
||||
export type ChangeRando = ChangeBase<"Rando", Rando>;
|
||||
export type ChangeRando = ChangeBase<"Rando", rando.Public>;
|
||||
export type ChangeReboot = ChangeBase<"Reboot", Reboot>;
|
||||
|
||||
export type Change = ChangePackingHeat | ChangeRando | ChangeReboot;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { PackingHeat, Reboot, Rules } from "../rules";
|
||||
import { Rando } from "./rando";
|
||||
import * as rando from "./rando";
|
||||
|
||||
/**
|
||||
* Non-standard rules that can be applied to a game.
|
||||
*/
|
||||
export interface HouseRules {
|
||||
packingHeat?: PackingHeat;
|
||||
reboot?: Reboot;
|
||||
rando: Rando;
|
||||
}
|
||||
|
||||
/**
|
||||
* The public view of the internal model.
|
||||
*/
|
||||
export interface Public {
|
||||
packingHeat?: PackingHeat;
|
||||
reboot?: Reboot;
|
||||
rando?: rando.Public;
|
||||
}
|
||||
|
||||
export const create = (): HouseRules => ({
|
||||
rando: rando.create()
|
||||
});
|
||||
|
||||
export const censor = (houseRules: HouseRules): Public => ({
|
||||
...houseRules,
|
||||
rando: rando.censor(houseRules.rando)
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
import { RegisterUser } from "../../action/initial/register-user";
|
||||
import * as event from "../../event";
|
||||
import * as presenceChanged from "../../events/lobby-event/presence-changed";
|
||||
import { Lobby } from "../../lobby";
|
||||
import * as lobby from "../../lobby";
|
||||
import { User } from "../../user";
|
||||
import * as util from "../../util";
|
||||
import * as user from "../../user";
|
||||
|
||||
/**
|
||||
* The maximum number of AI players allowed in a single game.
|
||||
*/
|
||||
const max = 10;
|
||||
|
||||
/**
|
||||
* The default name for an AI.
|
||||
*/
|
||||
export const aiName = "Rando Cardrissian";
|
||||
|
||||
/**
|
||||
* Apart from the one inherited from Cards against Humanity (a reference to
|
||||
* Star Wars), we use some references to famous AIs and computers from films
|
||||
* and games.
|
||||
*/
|
||||
export const aiNames = new Set([
|
||||
"HAL 9000", // 2001: A Space Odyssey
|
||||
"GLaDOS", // Portal
|
||||
"Wheatley", // Portal
|
||||
"TEC-XX", // Paper Mario
|
||||
"EDI", // Mass Effect
|
||||
"343 Guilty Spark", // Halo
|
||||
"Cortana", // Halo
|
||||
"J.A.R.V.I.S.", // MCU
|
||||
"Deep Thought", // HHGTTG
|
||||
"Gibson", // Hackers
|
||||
"Skynet", // Terminator
|
||||
"Project 2501", // GITS
|
||||
"SHODAN", // System Shock
|
||||
"Mr. House" // Fallout: New Vegas
|
||||
]);
|
||||
|
||||
/**
|
||||
* The internal model of the Rando house rule.
|
||||
*/
|
||||
export interface Rando {
|
||||
/**
|
||||
* The ids of active AI players in the game.
|
||||
*/
|
||||
current: user.Id[];
|
||||
/**
|
||||
* The names or ids of potential AI players that are not currently active in
|
||||
* the game.
|
||||
*/
|
||||
unused: (user.Id | RegisterUser)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the initial model.
|
||||
*/
|
||||
export const create = (): Rando => ({
|
||||
current: [],
|
||||
unused: [aiName]
|
||||
.concat(util.shuffled(aiNames).slice(0, max - 1))
|
||||
.map(name => ({ name }))
|
||||
});
|
||||
|
||||
/**
|
||||
* The public view of the Rando house rule.
|
||||
*/
|
||||
export interface Public {
|
||||
/**
|
||||
* The number of AI players to add to the game.
|
||||
* @TJS-type integer
|
||||
* @minimum 1
|
||||
* @maximum 10
|
||||
*/
|
||||
number: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public view of the given internal model.
|
||||
*/
|
||||
export const censor = (rando: Rando): Public | undefined =>
|
||||
rando.current.length > 0 ? { number: rando.current.length } : undefined;
|
||||
|
||||
const isId = (ai: user.Id | RegisterUser): ai is user.Id =>
|
||||
typeof ai === "string";
|
||||
|
||||
export const createIfNeeded = (
|
||||
inLobby: Lobby,
|
||||
ai: user.Id | RegisterUser
|
||||
): { user: user.Id; events: Iterable<event.Distributor> } => {
|
||||
if (isId(ai)) {
|
||||
return {
|
||||
user: ai,
|
||||
events: [
|
||||
event.targetAll(
|
||||
presenceChanged.joined(ai, inLobby.users.get(ai) as User)
|
||||
)
|
||||
]
|
||||
};
|
||||
} else {
|
||||
return lobby.addUser(inLobby, ai, user => ({
|
||||
...user,
|
||||
control: "Computer"
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the internal model to match the given public representation.
|
||||
*/
|
||||
export function* change(
|
||||
inLobby: Lobby,
|
||||
config: Rando,
|
||||
changeTo?: Public
|
||||
): Iterable<event.Distributor> | null {
|
||||
const want = changeTo !== undefined ? changeTo.number : 0;
|
||||
const have = config.current.length;
|
||||
const eventsCollection = [];
|
||||
if (want === have) {
|
||||
return null;
|
||||
}
|
||||
if (want > have) {
|
||||
const toAdd = want - have;
|
||||
const added = config.unused
|
||||
.splice(0, toAdd)
|
||||
.map(ai => createIfNeeded(inLobby, ai));
|
||||
for (const { user, events } of added) {
|
||||
const userData = inLobby.users.get(user) as User;
|
||||
userData.presence = "Joined";
|
||||
config.current.push(user);
|
||||
eventsCollection.push(...events);
|
||||
}
|
||||
} else if (have > want) {
|
||||
const toRemove = have - want;
|
||||
const removed = config.current.splice(
|
||||
config.current.length - toRemove,
|
||||
toRemove
|
||||
);
|
||||
for (const ai of removed) {
|
||||
const user = inLobby.users.get(ai) as User;
|
||||
user.presence = "Left";
|
||||
eventsCollection.push(event.targetAll(presenceChanged.left(ai)));
|
||||
}
|
||||
config.unused.splice(0, 0, ...removed);
|
||||
}
|
||||
yield* eventsCollection;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ async function main(): Promise<void> {
|
||||
return {
|
||||
change: {
|
||||
lobby,
|
||||
events: [event.targetAll(presenceChanged.joined(id, newUser.name))],
|
||||
events: [event.targetAll(presenceChanged.joined(id, newUser))],
|
||||
timeouts: [
|
||||
{
|
||||
timeout: userDisconnect.of(id),
|
||||
|
||||
+22
-1
@@ -1,4 +1,7 @@
|
||||
import { CreateLobby } from "./action/initial/create-lobby";
|
||||
import { RegisterUser } from "./action/initial/register-user";
|
||||
import * as event from "./event";
|
||||
import * as presenceChanged from "./events/lobby-event/presence-changed";
|
||||
import * as game from "./games/game";
|
||||
import { Game } from "./games/game";
|
||||
import * as rules from "./games/rules";
|
||||
@@ -8,7 +11,6 @@ import { GameCode } from "./lobby/game-code";
|
||||
import * as user from "./user";
|
||||
import { User } from "./user";
|
||||
import * as util from "./util";
|
||||
import * as token from "./user/token";
|
||||
|
||||
/**
|
||||
* A game lobby.
|
||||
@@ -132,3 +134,22 @@ export const censor = (lobby: Lobby): Public => ({
|
||||
config: config.censor(lobby.config),
|
||||
...(lobby.game === undefined ? {} : { game: game.censor(lobby.game) })
|
||||
});
|
||||
|
||||
export const addUser = (
|
||||
lobby: Lobby,
|
||||
registration: RegisterUser,
|
||||
change?: (user: User) => User
|
||||
): {
|
||||
user: user.Id;
|
||||
events: Iterable<event.Distributor>;
|
||||
} => {
|
||||
const newUser = user.create(registration);
|
||||
const changedUser = change === undefined ? newUser : change(newUser);
|
||||
const id = lobby.nextUserId.toString();
|
||||
lobby.nextUserId += 1;
|
||||
lobby.users.set(id, changedUser);
|
||||
return {
|
||||
user: id,
|
||||
events: [event.targetAll(presenceChanged.joined(id, changedUser))]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,12 +15,16 @@ export abstract class TaskBase<T> implements Task {
|
||||
}
|
||||
|
||||
protected abstract async begin(server: ServerState): Promise<T>;
|
||||
protected abstract resolve(lobby: Lobby, work: T): Change;
|
||||
protected abstract resolve(
|
||||
lobby: Lobby,
|
||||
work: T,
|
||||
server: ServerState
|
||||
): Change;
|
||||
|
||||
public async handle(server: ServerState): Promise<void> {
|
||||
const work = await this.begin(server);
|
||||
await change.apply(server, this.gameCode, lobby =>
|
||||
this.resolve(lobby, work)
|
||||
this.resolve(lobby, work, server)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import wu from "wu";
|
||||
import * as event from "../event";
|
||||
import * as gameStarted from "../events/game-event/game-started";
|
||||
import * as decks from "../games/cards/decks";
|
||||
import * as source from "../games/cards/source";
|
||||
import * as sources from "../games/cards/sources";
|
||||
import * as game from "../games/game";
|
||||
import * as round from "../games/game/round";
|
||||
import { Lobby } from "../lobby";
|
||||
import { Change } from "../lobby/change";
|
||||
import { GameCode } from "../lobby/game-code";
|
||||
@@ -28,20 +25,23 @@ export class StartGame extends task.TaskBase<decks.Templates[]> {
|
||||
);
|
||||
}
|
||||
|
||||
protected resolve(lobby: Lobby, work: decks.Templates[]): Change {
|
||||
protected resolve(
|
||||
lobby: Lobby,
|
||||
work: decks.Templates[],
|
||||
server: ServerState
|
||||
): Change {
|
||||
if (lobby.game !== undefined) {
|
||||
return {};
|
||||
}
|
||||
const lobbyGame = game.start(work, lobby.users.keys(), lobby.config.rules);
|
||||
const gameRound = round.censor(lobbyGame.round);
|
||||
const baseEvent = gameStarted.of(gameRound);
|
||||
const events = [
|
||||
event.playerSpecificAddition(baseEvent, (id, user, player) => ({
|
||||
hand: player.hand
|
||||
}))
|
||||
];
|
||||
lobby.game = lobbyGame;
|
||||
return { lobby, events };
|
||||
|
||||
const atStartOfRound = game.atStartOfRound(server, true, lobbyGame);
|
||||
lobby.game = atStartOfRound.game;
|
||||
return {
|
||||
lobby,
|
||||
events: atStartOfRound.events,
|
||||
timeouts: atStartOfRound.timeouts
|
||||
};
|
||||
}
|
||||
|
||||
// This is super unlikely timing-wise, and if it happens, the user just has
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import wu from "wu";
|
||||
import * as event from "../event";
|
||||
import * as roundStarted from "../events/game-event/round-started";
|
||||
import { Game } from "../games/game";
|
||||
import * as game from "../games/game";
|
||||
import * as lobby from "../lobby";
|
||||
import { Playing } from "../games/game/round";
|
||||
import * as timeout from "../timeout";
|
||||
import * as card from "../games/cards/card";
|
||||
import * as player from "../games/player";
|
||||
|
||||
/**
|
||||
* Indicates that the round should start if it is still appropriate to do so.
|
||||
@@ -21,15 +20,14 @@ export const handle: timeout.Handler<RoundStart> = (
|
||||
server,
|
||||
timeout,
|
||||
gameCode,
|
||||
lobby
|
||||
inLobby
|
||||
) => {
|
||||
const lobbyGame = lobby.game;
|
||||
if (lobbyGame !== undefined) {
|
||||
if (lobby.hasActiveGame(inLobby)) {
|
||||
const lobbyGame = inLobby.game;
|
||||
const gameRound = lobbyGame.round;
|
||||
if (gameRound.stage === "Complete") {
|
||||
const czar = game.nextCzar(lobbyGame);
|
||||
const czar = game.nextCzar(inLobby);
|
||||
const [call] = lobbyGame.decks.calls.replace(gameRound.call);
|
||||
const slotCount = card.slotCount(call);
|
||||
const roundId = gameRound.id + 1;
|
||||
const playersInRound = new Set(
|
||||
wu(lobbyGame.playerOrder).filter(id => id !== czar)
|
||||
@@ -37,37 +35,24 @@ export const handle: timeout.Handler<RoundStart> = (
|
||||
lobbyGame.decks.responses.discard(
|
||||
gameRound.plays.flatMap(play => play.play)
|
||||
);
|
||||
lobbyGame.round = {
|
||||
stage: "Playing",
|
||||
id: roundId,
|
||||
czar: czar,
|
||||
players: playersInRound,
|
||||
call: call,
|
||||
plays: []
|
||||
};
|
||||
const playersArray = Array.from(playersInRound);
|
||||
const baseEvent = roundStarted.of(roundId, czar, playersArray, call);
|
||||
|
||||
let events;
|
||||
if (
|
||||
slotCount > 2 ||
|
||||
(slotCount === 2 &&
|
||||
lobbyGame.rules.houseRules.packingHeat !== undefined)
|
||||
) {
|
||||
const responseDeck = lobbyGame.decks.responses;
|
||||
const drawnByPlayer = new Map();
|
||||
for (const [id, playerState] of lobbyGame.players) {
|
||||
if (player.role(lobbyGame, id) === "Player") {
|
||||
const drawn = responseDeck.draw(slotCount - 1);
|
||||
drawnByPlayer.set(id, { drawn });
|
||||
playerState.hand.push(...drawn);
|
||||
}
|
||||
const updatedGame: Game & { round: Playing } = {
|
||||
...lobbyGame,
|
||||
round: {
|
||||
stage: "Playing",
|
||||
id: roundId,
|
||||
czar: czar,
|
||||
players: playersInRound,
|
||||
call: call,
|
||||
plays: []
|
||||
}
|
||||
events = [event.additionally(baseEvent, drawnByPlayer)];
|
||||
} else {
|
||||
events = [event.targetAll(baseEvent)];
|
||||
}
|
||||
return { lobby, events };
|
||||
};
|
||||
const atStartOfRound = game.atStartOfRound(server, false, updatedGame);
|
||||
inLobby.game = atStartOfRound.game;
|
||||
return {
|
||||
inLobby,
|
||||
events: atStartOfRound.events,
|
||||
timeouts: atStartOfRound.timeouts
|
||||
};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
+17
-15
@@ -6,6 +6,7 @@ export interface User {
|
||||
presence: Presence;
|
||||
connection: Connection;
|
||||
privilege: Privilege;
|
||||
control: Control;
|
||||
role: Role;
|
||||
}
|
||||
|
||||
@@ -17,6 +18,7 @@ export interface Public {
|
||||
presence: Presence;
|
||||
connection: Connection;
|
||||
privilege: Privilege;
|
||||
control: Control;
|
||||
role: Role;
|
||||
}
|
||||
|
||||
@@ -52,6 +54,11 @@ export type Connection = "Connected" | "Disconnected";
|
||||
*/
|
||||
export type Role = "Spectator" | "Player";
|
||||
|
||||
/**
|
||||
* Who/what is controlling the player—a human or the computer?
|
||||
*/
|
||||
export type Control = "Human" | "Computer";
|
||||
|
||||
/**
|
||||
* If the user is playing.
|
||||
*/
|
||||
@@ -69,26 +76,21 @@ export const isSpectating: (user: User) => boolean = user =>
|
||||
* @param registration The details of the user to create.
|
||||
* @param privilege The level of privilege the user has.
|
||||
*/
|
||||
export function create(
|
||||
export const create = (
|
||||
registration: RegisterUser,
|
||||
privilege: Privilege = "Unprivileged"
|
||||
): User {
|
||||
return {
|
||||
name: registration.name,
|
||||
presence: "Joined",
|
||||
connection: "Connected",
|
||||
privilege: privilege,
|
||||
role: "Player"
|
||||
};
|
||||
}
|
||||
): User => ({
|
||||
name: registration.name,
|
||||
presence: "Joined",
|
||||
connection: "Connected",
|
||||
privilege: privilege,
|
||||
control: "Human",
|
||||
role: "Player"
|
||||
});
|
||||
|
||||
/**
|
||||
* Gives a version of the user with only publicly visible properties.
|
||||
*/
|
||||
export const censor: (user: User) => Public = user => ({
|
||||
name: user.name,
|
||||
presence: user.presence,
|
||||
connection: user.connection,
|
||||
privilege: user.privilege,
|
||||
role: user.role
|
||||
...user
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user