Implemented AI (“Rando”), refactored card rendering.

This commit is contained in:
Gareth Latty
2019-07-28 18:42:45 +01:00
parent 3836ef8b90
commit 110acdb70f
51 changed files with 1534 additions and 875 deletions
+300 -132
View File
@@ -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
View File
@@ -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"
}
}
+8 -3
View File
@@ -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 }
+8
View File
@@ -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 -}
+25 -171
View File
@@ -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
+103
View File
@@ -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 []
+20 -14
View File
@@ -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
]
+46 -27
View File
@@ -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
+2 -2
View File
@@ -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
}
+2 -11
View File
@@ -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) []
+17 -15
View File
@@ -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
+96 -71
View File
@@ -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
+10 -8
View File
@@ -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
+10 -9
View File
@@ -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
+4
View File
@@ -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
+9
View File
@@ -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
}
+2
View File
@@ -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;
+22 -2
View File
@@ -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;
}
+4
View File
@@ -121,6 +121,10 @@
li {
margin-top: 0.5em;
}
.left {
opacity: 0.3;
}
}
@media screen and (max-width: $users-width * 2) {