This commit is contained in:
Jose Conde 2024-07-22 21:04:51 +02:00
parent a8d6129d3e
commit cd5f4ad91a
38 changed files with 1311 additions and 182 deletions

2
.gitignore vendored
View File

@ -29,4 +29,4 @@ coverage
*.tsbuildinfo *.tsbuildinfo
.history .history
.env .env*

8
.hmrc
View File

@ -2,7 +2,7 @@
"path": "G:\\Other\\Development\\Projects\\[ideas]\\domino-client", "path": "G:\\Other\\Development\\Projects\\[ideas]\\domino-client",
"name": "domino-client", "name": "domino-client",
"initialVersion": "0.1.4", "initialVersion": "0.1.4",
"version": "0.1.8", "version": "0.1.12",
"docker": { "docker": {
"useRegistry": true, "useRegistry": true,
"registry": "192.168.1.115:5000", "registry": "192.168.1.115:5000",
@ -84,7 +84,7 @@
}, },
"_backup": { "_backup": {
"name": "domino-client", "name": "domino-client",
"version": "0.1.7", "version": "0.1.11",
"private": true, "private": true,
"type": "commonjs", "type": "commonjs",
"scripts": { "scripts": {
@ -97,8 +97,8 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/", "format": "prettier --write src/",
"docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-client:latest .", "docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-client:latest .",
"docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-client:latest 192.168.1.115:5000/arhuako/domino-client:0.1.7", "docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-client:latest 192.168.1.115:5000/arhuako/domino-client:0.1.11",
"docker-push": "docker push 192.168.1.115:5000/arhuako/domino-client:latest && docker push 192.168.1.115:5000/arhuako/domino-client:0.1.7", "docker-push": "docker push 192.168.1.115:5000/arhuako/domino-client:latest && docker push 192.168.1.115:5000/arhuako/domino-client:0.1.11",
"publish": "npm run docker-build && npm run docker-tag && npm run docker-push", "publish": "npm run docker-build && npm run docker-tag && npm run docker-push",
"serve": "npm run build-only && http-server ./dist -c-1 -s ", "serve": "npm run build-only && http-server ./dist -c-1 -s ",
"tauri": "tauri" "tauri": "tauri"

View File

@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file.
## Unreleased ## Unreleased
## 0.1.12 - 2024-07-22
### Added
- I18n translations
- Win conditions
### Fixed
- Multiplayer join button not accessible
## 0.1.10 - 2024-07-20
### Added
- Updater
- Refresh authentication when expires
- Match summary page phase 1
## 0.1.9 - 2024-07-19
## 0.1.8 - 2024-07-18 ## 0.1.8 - 2024-07-18
## 0.1.7 - 2024-07-17 ## 0.1.7 - 2024-07-17

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "domino-client", "name": "domino-client",
"version": "0.1.7", "version": "0.1.8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "domino-client", "name": "domino-client",
"version": "0.1.7", "version": "0.1.8",
"dependencies": { "dependencies": {
"@pixi/sound": "^6.0.0", "@pixi/sound": "^6.0.0",
"@tauri-apps/api": "^1.6.0", "@tauri-apps/api": "^1.6.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "domino-client", "name": "domino-client",
"version": "0.1.8", "version": "0.1.12",
"private": true, "private": true,
"type": "commonjs", "type": "commonjs",
"scripts": { "scripts": {
@ -13,8 +13,8 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/", "format": "prettier --write src/",
"docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-client:latest .", "docker-build": "docker build -t 192.168.1.115:5000/arhuako/domino-client:latest .",
"docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-client:latest 192.168.1.115:5000/arhuako/domino-client:0.1.8", "docker-tag": "docker tag 192.168.1.115:5000/arhuako/domino-client:latest 192.168.1.115:5000/arhuako/domino-client:0.1.12",
"docker-push": "docker push 192.168.1.115:5000/arhuako/domino-client:latest && docker push 192.168.1.115:5000/arhuako/domino-client:0.1.8", "docker-push": "docker push 192.168.1.115:5000/arhuako/domino-client:latest && docker push 192.168.1.115:5000/arhuako/domino-client:0.1.12",
"publish": "npm run docker-build && npm run docker-tag && npm run docker-push", "publish": "npm run docker-build && npm run docker-tag && npm run docker-push",
"serve": "npm run build-only && http-server ./dist -c-1 -s ", "serve": "npm run build-only && http-server ./dist -c-1 -s ",
"tauri": "tauri" "tauri": "tauri"

View File

@ -1,5 +1,23 @@
<h1>Changelog</h1> <h1>Changelog</h1>
<p>All notable changes to this project will be documented in this file.</p> <p>All notable changes to this project will be documented in this file.</p>
<h2>0.1.12 - 2024-07-22</h2>
<h3>Added</h3>
<ul>
<li>I18n translations</li>
<li>Win conditions</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Multiplayer join button not accessible</li>
</ul>
<h2>0.1.10 - 2024-07-20</h2>
<h3>Added</h3>
<ul>
<li>Updater</li>
<li>Refresh authentication when expires</li>
<li>Match summary page phase 1</li>
</ul>
<h2>0.1.9 - 2024-07-19</h2>
<h2>0.1.8 - 2024-07-18</h2> <h2>0.1.8 - 2024-07-18</h2>
<h2>0.1.7 - 2024-07-17</h2> <h2>0.1.7 - 2024-07-17</h2>
<h2>0.1.6 - 2024-07-17</h2> <h2>0.1.6 - 2024-07-17</h2>

511
src-tauri/Cargo.lock generated
View File

@ -216,6 +216,9 @@ name = "bytes"
version = "1.6.1" version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
@ -630,7 +633,7 @@ dependencies = [
"rustc_version", "rustc_version",
"toml 0.8.15", "toml 0.8.15",
"vswhom", "vswhom",
"winreg", "winreg 0.52.0",
] ]
[[package]] [[package]]
@ -794,6 +797,12 @@ dependencies = [
"syn 2.0.71", "syn 2.0.71",
] ]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.30" version = "0.3.30"
@ -807,8 +816,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-io",
"futures-macro", "futures-macro",
"futures-sink",
"futures-task", "futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
@ -1120,6 +1132,25 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "h2"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap 2.2.6",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -1190,12 +1221,72 @@ dependencies = [
"itoa 1.0.11", "itoa 1.0.11",
] ]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]] [[package]]
name = "http-range" name = "http-range"
version = "0.1.5" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa 1.0.11",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.60" version = "0.1.60"
@ -1313,6 +1404,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "ipnet"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.8" version = "0.4.8"
@ -1519,6 +1616,18 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minisign-verify"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.4" version = "0.7.4"
@ -1529,6 +1638,34 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "mio"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
[[package]]
name = "native-tls"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.6.0" version = "0.6.0"
@ -1635,6 +1772,17 @@ dependencies = [
"objc_exception", "objc_exception",
] ]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]] [[package]]
name = "objc_exception" name = "objc_exception"
version = "0.1.2" version = "0.1.2"
@ -1668,6 +1816,50 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.71",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -2151,6 +2343,72 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-util",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"winreg 0.50.0",
]
[[package]]
name = "rfd"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
dependencies = [
"block",
"dispatch",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"lazy_static",
"log",
"objc",
"objc-foundation",
"objc_id",
"raw-window-handle",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows 0.37.0",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -2179,6 +2437,15 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.17" version = "1.0.17"
@ -2200,6 +2467,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "schannel"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "scoped-tls" name = "scoped-tls"
version = "1.0.1" version = "1.0.1"
@ -2212,6 +2488,29 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "selectors" name = "selectors"
version = "0.22.0" version = "0.22.0"
@ -2293,6 +2592,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa 1.0.11",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.9.0" version = "3.9.0"
@ -2402,6 +2713,16 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "soup2" name = "soup2"
version = "0.2.1" version = "0.2.1"
@ -2499,6 +2820,33 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "5.0.0" version = "5.0.0"
@ -2607,6 +2955,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "336bc661a3f3250853fa83c6e5245449ed1c26dce5dcb28bdee7efedf6278806" checksum = "336bc661a3f3250853fa83c6e5245449ed1c26dce5dcb28bdee7efedf6278806"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.21.7",
"bytes",
"cocoa", "cocoa",
"dirs-next", "dirs-next",
"dunce", "dunce",
@ -2621,11 +2971,15 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"http", "http",
"ignore", "ignore",
"indexmap 1.9.3",
"minisign-verify",
"objc", "objc",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"rand 0.8.5", "rand 0.8.5",
"raw-window-handle", "raw-window-handle",
"reqwest",
"rfd",
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
@ -2639,12 +2993,14 @@ dependencies = [
"tauri-utils", "tauri-utils",
"tempfile", "tempfile",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"url", "url",
"uuid", "uuid",
"webkit2gtk", "webkit2gtk",
"webview2-com", "webview2-com",
"windows 0.39.0", "windows 0.39.0",
"zip",
] ]
[[package]] [[package]]
@ -2899,8 +3255,35 @@ checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"libc",
"mio",
"num_cpus", "num_cpus",
"pin-project-lite", "pin-project-lite",
"socket2",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
] ]
[[package]] [[package]]
@ -2971,6 +3354,12 @@ dependencies = [
"winnow 0.6.13", "winnow 0.6.13",
] ]
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.40" version = "0.1.40"
@ -3032,6 +3421,12 @@ dependencies = [
"tracing-log", "tracing-log",
] ]
[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"
@ -3098,6 +3493,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.0.11" version = "0.0.11"
@ -3146,6 +3547,15 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.9.0+wasi-snapshot-preview1"
@ -3183,6 +3593,18 @@ dependencies = [
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.92" version = "0.2.92"
@ -3212,6 +3634,29 @@ version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wasm-streams"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "webkit2gtk" name = "webkit2gtk"
version = "0.18.2" version = "0.18.2"
@ -3328,6 +3773,19 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
dependencies = [
"windows_aarch64_msvc 0.37.0",
"windows_i686_gnu 0.37.0",
"windows_i686_msvc 0.37.0",
"windows_x86_64_gnu 0.37.0",
"windows_x86_64_msvc 0.37.0",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.39.0" version = "0.39.0"
@ -3462,6 +3920,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.39.0" version = "0.39.0"
@ -3480,6 +3944,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.39.0" version = "0.39.0"
@ -3504,6 +3974,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.39.0" version = "0.39.0"
@ -3522,6 +3998,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.39.0" version = "0.39.0"
@ -3552,6 +4034,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.39.0" version = "0.39.0"
@ -3588,6 +4076,16 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.52.0" version = "0.52.0"
@ -3667,3 +4165,14 @@ dependencies = [
"linux-raw-sys", "linux-raw-sys",
"rustix", "rustix",
] ]
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
]

View File

@ -17,7 +17,7 @@ tauri-build = { version = "1.5.3", features = [] }
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.7.0", features = [] } tauri = { version = "1.7.0", features = [ "fs-all", "path-all", "updater"] }
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View File

@ -8,11 +8,26 @@
}, },
"package": { "package": {
"productName": "domino-client", "productName": "domino-client",
"version": "0.1.8" "version": "0.1.11"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {
"all": false "path": {
"all": true
},
"fs": {
"copyFile": true,
"createDir": true,
"exists": true,
"readDir": true,
"readFile": true,
"removeDir": true,
"removeFile": true,
"renameFile": true,
"scope": ["$APPDATA/*", "$APP/*"],
"writeFile": true,
"all": true
}
}, },
"bundle": { "bundle": {
"active": true, "active": true,
@ -51,14 +66,19 @@
"csp": null "csp": null
}, },
"updater": { "updater": {
"active": false "active": true,
"dialog": true,
"endpoints": [
"https://domserv.xintanalabs.net/api/updater/{{target}}/{{arch}}/{{current_version}}"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQwRTBDNUU1QzIyNEYzQUQKUldTdDh5VEM1Y1hnUVA2M01pcEZMbVltVUpuMlhpN09Rc3BnN1A3NC9BOGs5OE92MndRZzVXSm4K"
}, },
"windows": [ "windows": [
{ {
"fullscreen": false, "fullscreen": false,
"height": 720, "height": 720,
"resizable": true, "resizable": true,
"title": "Domino", "title": "Domino v0.1.11",
"width": 1280, "width": 1280,
"minHeight": 720, "minHeight": 720,
"minWidth": 1280, "minWidth": 1280,

View File

@ -57,8 +57,6 @@ onUnmounted(() => {
}) })
</script> </script>
<template> <template><RouterView /></template>
<RouterView />
</template>
<style scoped></style> <style scoped></style>

View File

@ -111,3 +111,34 @@ export function copyToclipboard(value: string) {
const { toClipboard } = useClipboard() const { toClipboard } = useClipboard()
toClipboard(value) toClipboard(value)
} }
export function transposeMatrix(matrix: any[][]): any[][] {
// Get the number of rows and columns
const numRows = matrix.length
const numCols = matrix[0].length
// Create a new matrix with transposed dimensions
const transposed = Array.from({ length: numCols }, () => Array(numRows).fill(null))
// Transpose the matrix
for (let row = 0; row < numRows; row++) {
for (let col = 0; col < numCols; col++) {
transposed[col][row] = matrix[row][col]
}
}
return transposed
}
export function createStringMatrix(
rows: number,
cols: number,
initialValue: string = '',
): string[][] {
// Create an array of arrays (matrix)
const matrix: string[][] = Array.from({ length: rows }, () =>
Array.from({ length: cols }, () => initialValue),
)
return matrix
}

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { MatchSessionOptions } from '@/common/interfaces' import type { MatchSessionOptions } from '@/common/interfaces'
import { ref } from 'vue' import { computed, ref } from 'vue'
const emit = defineEmits(['createMatch']) const emit = defineEmits(['createMatch', 'startSingleMatch'])
let options = ref<MatchSessionOptions>({ let options = ref<MatchSessionOptions>({
background: 'green', background: 'green',
@ -14,9 +14,42 @@ let options = ref<MatchSessionOptions>({
numPlayers: 1, numPlayers: 1,
}) })
const winTargetPointsList = [20, 50, 80, 100, 150, 200]
const winTargetRoundsList = [1, 2, 3, 4, 5, 6]
const backgroundOptiopnList = [
{
label: 'green-fabric',
value: 'green',
},
{
label: 'gray-fabric',
value: 'gray',
},
{
label: 'blue-fabric',
value: 'blue',
},
{
label: 'yellow-fabric',
value: 'yellow',
},
{
label: 'red-fabric',
value: 'red',
},
]
const isSinglePlayer = computed(() => options.value.numPlayers === 1)
const isMultiPlayer = computed(() => options.value.numPlayers > 1)
function createMatch() { function createMatch() {
emit('createMatch', options.value) emit('createMatch', options.value)
} }
function startSingleMatch() {
emit('startSingleMatch', options.value)
}
</script> </script>
<template> <template>
@ -29,7 +62,7 @@ function createMatch() {
<div class="buttons has-addons"> <div class="buttons has-addons">
<button <button
class="button" class="button"
:class="{ 'is-primary is-selected': options.numPlayers === 1 }" :class="{ 'is-primary is-selected': isSinglePlayer }"
@click=" @click="
() => { () => {
console.log('options :>> ', options) console.log('options :>> ', options)
@ -41,7 +74,7 @@ function createMatch() {
</button> </button>
<button <button
class="button" class="button"
:class="{ 'is-primary is-selected': options.numPlayers > 1 }" :class="{ 'is-primary is-selected': isMultiPlayer }"
@click=" @click="
() => { () => {
console.log('options :>> ', options) console.log('options :>> ', options)
@ -56,7 +89,7 @@ function createMatch() {
</div> </div>
</div> </div>
<div class="cell"> <div class="cell">
<div class="field" v-if="options.numPlayers > 1"> <div class="field" v-if="isMultiPlayer">
<label class="label">{{ $t('players-number') }}</label> <label class="label">{{ $t('players-number') }}</label>
<div class="control"> <div class="control">
<div class="buttons has-addons"> <div class="buttons has-addons">
@ -80,7 +113,7 @@ function createMatch() {
</div> </div>
</div> </div>
<div class="cell"> <div class="cell">
<div class="field" v-if="options.numPlayers > 1"> <div class="field" v-if="isMultiPlayer">
<div class="control"> <div class="control">
<label for="teamed" class="checkbox"> <label for="teamed" class="checkbox">
<input v-model="options.teamed" name="teamed" type="checkbox" /> <input v-model="options.teamed" name="teamed" type="checkbox" />
@ -128,12 +161,13 @@ function createMatch() {
<div class="control"> <div class="control">
<div class="select"> <div class="select">
<select v-model="options.background" name="background"> <select v-model="options.background" name="background">
<option value="wood-1">{{ $t('wood-1') }}</option> <option
<option value="green">{{ $t('green-fabric') }}</option> v-bind:key="option.value"
<option value="gray">{{ $t('gray-fabric') }}</option> v-for="option in backgroundOptiopnList"
<option value="blue">{{ $t('blue-fabric') }}</option> :value="option.value"
<option value="yellow">{{ $t('yellow-fabric') }}</option> >
<option value="red">{{ $t('red-fabric') }}</option> {{ $t(option.label) }}
</option>
</select> </select>
</div> </div>
</div> </div>
@ -178,22 +212,24 @@ function createMatch() {
<div class="control"> <div class="control">
<div class="select" v-if="options.winType === 'points'"> <div class="select" v-if="options.winType === 'points'">
<select v-model="options.winTarget" name="winTarget"> <select v-model="options.winTarget" name="winTarget">
<option value="20">{{ $t('n-points', [20]) }}</option> <option
<option value="50">{{ $t('n-points', [50]) }}</option> v-bind:key="winTarget"
<option value="80">{{ $t('n-points', [80]) }}</option> v-for="winTarget in winTargetPointsList"
<option value="100">{{ $t('n-points', [100]) }}</option> :value="winTarget"
<option value="150">{{ $t('n-points', [150]) }}</option> >
<option value="200">{{ $t('n-points', [200]) }}</option> {{ $t('n-points', winTarget) }}
</option>
</select> </select>
</div> </div>
<div class="select" v-if="options.winType === 'rounds'"> <div class="select" v-if="options.winType === 'rounds'">
<select v-model="options.winTarget" name="winTarget"> <select v-model="options.winTarget" name="winTarget">
<option value="1">{{ $t('n-of-m-rounds', [1, 1]) }}</option> <option
<option value="2">{{ $t('n-of-m-rounds', [2, 3]) }}</option> v-bind:key="winTarget"
<option value="3">{{ $t('n-of-m-rounds', [3, 5]) }}</option> v-for="winTarget in winTargetRoundsList"
<option value="4">{{ $t('n-of-m-rounds', [4, 7]) }}</option> :value="winTarget"
<option value="5">{{ $t('n-of-m-rounds', [5, 9]) }}</option> >
<option value="6">{{ $t('n-of-m-rounds', [6, 11]) }}</option> {{ $t('n-of-m-rounds', [winTarget, winTarget * 2 - 1]) }}
</option>
</select> </select>
</div> </div>
</div> </div>
@ -201,9 +237,12 @@ function createMatch() {
</div> </div>
</div> </div>
<div class="buttons mt-6"> <div class="buttons mt-6">
<button class="button is-primary" @click.prevent="createMatch"> <button class="button is-primary" @click.prevent="createMatch" v-if="isMultiPlayer">
{{ $t('create-match-session') }} {{ $t('create-match-session') }}
</button> </button>
<button class="button is-primary" @click.prevent="startSingleMatch" v-if="isSinglePlayer">
{{ $t('start-game') }}
</button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -0,0 +1,24 @@
<template>
<h3 class="title is-5">{{ title }}</h3>
<div v-if="scoreboard">
<div v-bind:key="$index" v-for="(score, $index) in sortedScoreboard">
<p class="">
<span class="title is-5">{{ score.name }}</span>
<span class="is-size-5 ml-4">{{ score.score }}</span>
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const { title, scoreboard } = defineProps(['scoreboard', 'title'])
const sortedScoreboard = computed(() => {
const copy = [...(scoreboard || [])]
return copy.sort((a, b) => b.score - a.score)
})
</script>
<style scoped></style>

View File

@ -0,0 +1,77 @@
<script setup lang="ts">
import { createStringMatrix } from '@/common/helpers'
import { computed } from 'vue'
const props = defineProps(['games', 'finalScore', 'winner'])
const playerNames = computed<any[]>(() =>
(props.finalScore || []).map((score: any) => {
const type = score.name === props.winner.name ? 'winner-name' : 'name'
return { type, value: score.name }
}),
)
const totals = computed<any[]>(() =>
props.finalScore.map((score: any) => {
const type = score.name === props.winner.name ? 'winner-final-score' : 'final-score'
return { type, value: `${score.score}` }
}),
)
const matrix = computed<any[][]>(() => {
if (props.games === undefined) {
return []
}
const m = props.games.map((game: any) => {
const winner = game.winner.name
return game.players.map((player: any) => {
const type = player.name === winner ? 'winner-score' : 'score'
return { type, value: `${player.score}` }
})
})
m.unshift(playerNames.value)
m.push(totals.value)
const rows = m.length
const cols = m[0].length
const t: any[][] = createStringMatrix(cols, rows)
try {
for (let row = 0; row < m.length; row++) {
for (let col = 0; col < m[0].length; col++) {
t[col][row] = m[row][col]
}
}
} catch (error) {
console.error('error :>> ', error)
}
return t
})
function getCellClasses(value: any) {
const { type } = value
return {
'has-text-weight-bold':
type === 'name' ||
type === 'final-score' ||
type === 'winner-final-score' ||
type === 'winner-name',
'has-text-primary':
type === 'winner-score' || type === 'winner-name' || type === 'winner-final-score',
}
}
</script>
<template>
<table class="table is-striped is-fullwidth is-hoverable">
<thead>
<th>{{ $t('player') }}</th>
<th v-for="(game, $index) in games" :key="game">{{ $t('round-index', [$index]) }}</th>
<th>{{ $t('final-score') }}</th>
</thead>
<tbody>
<tr v-for="(row, $index) in matrix" :key="$index">
<td :class="getCellClasses(col)" v-for="(col, $index) in row" :key="$index">
{{ col.value }}
</td>
</tr>
</tbody>
</table>
</template>
<style scoped></style>

View File

@ -1,24 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { RouterLink, RouterView } from 'vue-router' import { RouterView } from 'vue-router'
const router = useRouter() const router = useRouter()
const props = defineProps({
navbar: Boolean,
})
defineOptions({ defineOptions({
name: 'AuthenticatedLayout' name: 'AuthenticatedLayout',
}) })
function logout() { function logout() {
localStorage.removeItem('isLoggedIn')
router.push({ name: 'landing' }) router.push({ name: 'landing' })
} }
</script> </script>
<template> <template>
<div class="authenticated-layout"> <div class="authenticated-layout">
<header> <header v-if="props.navbar">
<nav> <nav class="navbar">
<!-- <button @click="logout">Logout</button> --> <div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<button class="button is-primary" @click="logout">Logout</button>
</div>
</div>
</div>
</nav> </nav>
</header> </header>
<main> <main>

View File

@ -162,6 +162,7 @@ export class Board extends EventEmitter {
this.nextTile = tile this.nextTile = tile
lastMove.tile = tile.toPlain() lastMove.tile = tile.toPlain()
this.movements.push(lastMove) this.movements.push(lastMove)
console.log('this.movements :>> ', this.movements)
await this.addTile(tile, lastMove) await this.addTile(tile, lastMove)
this.setFreeEnd(lastMove) this.setFreeEnd(lastMove)
} }

View File

@ -17,14 +17,8 @@ import { Actions } from 'pixi-actions'
import { OtherHand } from './OtherHand' import { OtherHand } from './OtherHand'
import { GameSummayView } from './GameSummayView' import { GameSummayView } from './GameSummayView'
import Config from './Config' import Config from './Config'
import { createText, grayStyle } from './utilities/fonts'
interface GameOptions { import { t } from '@/i18n'
boardScale: number
handScale: number
width: number
height: number
background: string
}
export class Game extends EventEmitter { export class Game extends EventEmitter {
public board!: Board public board!: Board
@ -91,6 +85,25 @@ export class Game extends EventEmitter {
const background = new TilingSprite(Assets.get(`bg-${this.options.background}`)) const background = new TilingSprite(Assets.get(`bg-${this.options.background}`))
this.backgroundLayer.addChild(background) this.backgroundLayer.addChild(background)
const actor = this.options.teamed ? t('team') : t('player')
const type =
this.options.winType === 'points'
? t('n-points', this.options.winTarget)
: t('n-rounds', this.options.winTarget)
const helptext = t('first-actor-to-win-this-options-wintarget-this-options-wintype', [
actor.toLowerCase(),
type.toLowerCase(),
])
this.backgroundLayer.addChild(
createText({
text: `${helptext}`,
x: this.app.canvas.width / 2,
y: 120,
style: grayStyle(14, 'lighter', false),
}),
)
} }
initPlayers(players: PlayerDto[]) { initPlayers(players: PlayerDto[]) {

View File

@ -2,6 +2,7 @@ import { createButton, createContainer } from '@/common/helpers'
import type { GameSummary, MatchSessionDto, MatchSessionOptions } from '@/common/interfaces' import type { GameSummary, MatchSessionDto, MatchSessionOptions } from '@/common/interfaces'
import { EventEmitter, type Application, type Container } from 'pixi.js' import { EventEmitter, type Application, type Container } from 'pixi.js'
import { createText, whiteStyle, yellowStyle } from './utilities/fonts' import { createText, whiteStyle, yellowStyle } from './utilities/fonts'
import { t } from '@/i18n'
export class GameSummayView extends EventEmitter { export class GameSummayView extends EventEmitter {
public width: number public width: number
@ -59,7 +60,7 @@ export class GameSummayView extends EventEmitter {
let line = y + 12 let line = y + 12
this.layer.addChild( this.layer.addChild(
createText({ createText({
text: `Winner: ${this.gameSummary.winner.name}`, text: t('winner-name', [this.gameSummary.winner.name]),
x: this.width / 2, x: this.width / 2,
y: line, y: line,
style: whiteStyle(20), style: whiteStyle(20),
@ -70,7 +71,7 @@ export class GameSummayView extends EventEmitter {
line += 30 line += 30
this.layer.addChild( this.layer.addChild(
createText({ createText({
text: '(Blocked)', text: `(${t('blocked')})`,
x: this.width / 2, x: this.width / 2,
y: line, y: line,
style: whiteStyle(), style: whiteStyle(),
@ -78,15 +79,29 @@ export class GameSummayView extends EventEmitter {
) )
} }
if (this.options.winType === 'points') {
line += 30 line += 30
this.layer.addChild( this.layer.addChild(
createText({ createText({
text: `Points this round: ${this.gameSummary.winner.score}`, text: `Points this round: ${this.gameSummary.winner.score}`,
// text: `Points this round: ${this.gameSummary.winner.score}, needed to win: ${this.options.winTarget}`,
x: this.width / 2, x: this.width / 2,
y: line, y: line,
style: whiteStyle(20), style: whiteStyle(20),
}), }),
) )
}
// } else if (this.options.winType === 'rounds') {
// line += 30
// this.layer.addChild(
// createText({
// text: `Rounds needed to win: ${this.options.winTarget}`,
// x: this.width / 2,
// y: line,
// style: whiteStyle(20),
// }),
// )
// }
return line + 16 return line + 16
} }
@ -159,7 +174,7 @@ export class GameSummayView extends EventEmitter {
} }
render() { render() {
const title: string = this.type === 'round' ? 'Round Summary' : 'Match Finished!' const title: string = this.type === 'round' ? t('round-summary') : t('match-finished')
this.layer.removeChildren() this.layer.removeChildren()
let y = this.renderTitle(30, title.toUpperCase()) let y = this.renderTitle(30, title.toUpperCase())
y = this.renderWinner(y) y = this.renderWinner(y)

View File

@ -57,19 +57,19 @@ export const scoreText = new TextStyle({
function getStyle(styleOptions: TextStyleOptions = {}) { function getStyle(styleOptions: TextStyleOptions = {}) {
const { const {
fill = 0xa2a2a2, fill = 0xa2a2a2,
stroke = 0x565656,
fontSize = 15, fontSize = 15,
fontFamily = 'Arial, Helvetica, sans-serif', fontFamily = 'Arial, Helvetica, sans-serif',
fontWeight = 'normal', fontWeight = 'normal',
fontStyle = 'normal', fontStyle = 'normal',
dropShadow, dropShadow,
letterSpacing = 1, letterSpacing = 1,
stroke,
} = styleOptions } = styleOptions
const style = new TextStyle({ const style = new TextStyle({
fill, fill,
fontFamily, fontFamily,
letterSpacing, letterSpacing,
stroke, stroke: stroke ? stroke : undefined,
fontSize, fontSize,
fontStyle, fontStyle,
fontWeight: fontWeight as any, fontWeight: fontWeight as any,
@ -78,6 +78,20 @@ function getStyle(styleOptions: TextStyleOptions = {}) {
return style return style
} }
const styleFactory = (fill: number) => {
return (
fontSize: number = 15,
fontWeight: TextStyleFontWeight = 'normal',
dropShadow: boolean = false,
) =>
getStyle({
fill,
fontSize,
fontWeight,
dropShadow,
})
}
export const whiteStyle = ( export const whiteStyle = (
fontSize: number = 15, fontSize: number = 15,
fontWeight: TextStyleFontWeight = 'normal', fontWeight: TextStyleFontWeight = 'normal',
@ -101,6 +115,8 @@ export const yellowStyle = (
dropShadow, dropShadow,
}) })
export const grayStyle = styleFactory(0x444444)
interface TextOptions { interface TextOptions {
text: string text: string
x: number x: number

View File

@ -55,9 +55,20 @@
"win-type": "Win unit", "win-type": "Win unit",
"points": "Points", "points": "Points",
"rounds": "Rounds", "rounds": "Rounds",
"n-points": "{value} Points", "n-points": "{count} Points",
"n-rounds": "One Round|{count} Rounds",
"n-of-m-rounds": "{0} of {1} Rounds", "n-of-m-rounds": "{0} of {1} Rounds",
"create-session": "Create Session", "create-session": "Create Session",
"join-a-multiplayer-session": "Join a Multiplayer Session", "join-a-multiplayer-session": "Join a Multiplayer Session (No sessions)|Join a Multiplayer Session ({count})|Join a Multiplayer Session ({count})",
"tournaments": "Tournaments" "tournaments": "Tournaments",
"start-game": "Start Game",
"player": "Player",
"final-score": "Final Score",
"round-index": "Round #{0}",
"first-actor-to-win-this-options-wintarget-this-options-wintype": "First {0} to win {1}",
"team": "team",
"winner-name": "Winner: {0}",
"blocked": "Blocked",
"round-summary": "Round Summary",
"match-finished": "Match Finished"
} }

View File

@ -55,9 +55,20 @@
"win-type": "Unidad de puntaje", "win-type": "Unidad de puntaje",
"points": "Puntos", "points": "Puntos",
"rounds": "Rondas", "rounds": "Rondas",
"n-points": "{0} puntos", "n-points": "{count} puntos",
"n-of-m-rounds": "{0} de {1} rondas", "n-of-m-rounds": "{0} de {1} rondas",
"create-session": "Crear sesión", "create-session": "Crear sesión",
"join-a-multiplayer-session": "Únete a una sesión multijugador", "join-a-multiplayer-session": "Únete a una sesión multijugador|Únete a una sesión multijugador ({count})|Únete a una sesión multijugador ({count})",
"tournaments": "Torneos" "tournaments": "Torneos",
"start-game": "Empezar la partida",
"player": "Jugador",
"final-score": "Puntuación final",
"round-index": "Juego #{0}",
"first-actor-to-win-this-options-wintarget-this-options-wintype": "Primer {0} en ganar {1}",
"n-rounds": "Una ronda|{count} rondas",
"winner-name": "Ganador: {0}",
"blocked": "Cerrado",
"round-summary": "Resumen de la ronda",
"match-finished": "Partida terminado",
"team": "equipo"
} }

View File

@ -12,6 +12,7 @@ import { SocketIoClientService } from '@/services/SocketIoClientService'
import { LoggingService } from '@/services/LoggingService' import { LoggingService } from '@/services/LoggingService'
import { AuthenticationService } from './services/AuthenticationService' import { AuthenticationService } from './services/AuthenticationService'
import { GameService } from './services/GameService' import { GameService } from './services/GameService'
import { PersistenceService } from './services/PersistenceService'
const app = createApp(App) const app = createApp(App)
@ -23,5 +24,6 @@ app.provide('socket', new SocketIoClientService(import.meta.env.VITE_SOCKET_URL)
app.provide('logger', new LoggingService()) app.provide('logger', new LoggingService())
app.provide('auth', new AuthenticationService()) app.provide('auth', new AuthenticationService())
app.provide('game', new GameService()) app.provide('game', new GameService())
PersistenceService.getInstance()
app.mount('#app') app.mount('#app')

View File

@ -3,6 +3,9 @@ import AuthenticatedLayout from '@/components/layouts/AuthenticatedLayout.vue'
import UnauthenticatedLayout from '@/components/layouts/UnauthenticatedLayout.vue' import UnauthenticatedLayout from '@/components/layouts/UnauthenticatedLayout.vue'
import HomeView from '@/views/HomeView.vue' import HomeView from '@/views/HomeView.vue'
import LandingView from '@/views/LandingView.vue' import LandingView from '@/views/LandingView.vue'
import { PersistenceService } from '@/services/PersistenceService'
import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@ -19,6 +22,7 @@ const router = createRouter({
], ],
}, },
{ {
props: { navbar: true },
path: '/home', path: '/home',
component: AuthenticatedLayout, component: AuthenticatedLayout,
children: [ children: [
@ -37,6 +41,7 @@ const router = createRouter({
// component: () => import('../views/AboutView.vue') // component: () => import('../views/AboutView.vue')
}, },
{ {
props: { navbar: true },
path: '/match/:id', path: '/match/:id',
component: AuthenticatedLayout, component: AuthenticatedLayout,
children: [ children: [
@ -51,6 +56,7 @@ const router = createRouter({
], ],
}, },
{ {
props: { navbar: false },
path: '/game/:id', path: '/game/:id',
component: AuthenticatedLayout, component: AuthenticatedLayout,
children: [ children: [
@ -68,8 +74,13 @@ const router = createRouter({
}) })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const isLoggedIn = !!sessionStorage.getItem('token') const auth = useAuthStore()
if (to.matched.some((record) => record.meta.requiresAuth) && !isLoggedIn) { const { user } = storeToRefs(auth)
console.log('user.value :>> ', user.value)
const isLoggedIn = user.value === undefined ? false : true
if (to.name === 'landing' && isLoggedIn) {
next({ name: 'home' })
} else if (to.matched.some((record) => record.meta.requiresAuth) && !isLoggedIn) {
next({ name: 'landing' }) next({ name: 'landing' })
} else { } else {
next() next()

View File

@ -3,9 +3,12 @@ import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { NetworkService } from '@/services/NetworkService' import { NetworkService } from '@/services/NetworkService'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { PersistenceService } from './PersistenceService'
export class AuthenticationService extends ServiceBase { export class AuthenticationService extends ServiceBase {
private networkService = new NetworkService() private networkService = new NetworkService()
private auth = useAuthStore()
private persistanceService: PersistenceService = PersistenceService.getInstance()
isAuthenticated() { isAuthenticated() {
const auth = useAuthStore() const auth = useAuthStore()
@ -14,17 +17,13 @@ export class AuthenticationService extends ServiceBase {
} }
async login(username: string, password: string) { async login(username: string, password: string) {
try { const response = await this.networkService.post({
const res = await this.networkService.post({
uri: '/login', uri: '/login',
body: { username, password } body: { username, password },
}) })
const { token } = res const { token, refreshToken } = response
this.persist(token) this.persist(token, refreshToken)
return token return token
} catch (error) {
console.error(error)
}
} }
async logout() { async logout() {
@ -32,20 +31,32 @@ export class AuthenticationService extends ServiceBase {
} }
private removePersistence() { private removePersistence() {
const auth = useAuthStore() const { clearUser, clearToken, clearRefreshToken } = this.auth
const { setJwt, setUser } = auth clearToken()
setJwt(undefined) clearUser()
setUser(undefined) clearRefreshToken()
sessionStorage.removeItem('token') this.persistanceService.saveToken('')
this.persistanceService.saveRefreshToken('')
} }
private persist(jwt: string) { async persist(jwt: string, refreshJwt?: string) {
const auth = useAuthStore() const { setToken, setUser, setRefreshToken } = this.auth
const { setJwt, setUser } = auth
const loggedUser = this.parseJwt(jwt) const loggedUser = this.parseJwt(jwt)
setJwt(jwt) setToken(jwt)
setUser(loggedUser) setUser(loggedUser)
sessionStorage.setItem('token', jwt) try {
await this.persistanceService.saveToken(jwt)
} catch (error) {
this.logger.error(error, 'Error saving token')
}
if (refreshJwt) {
setRefreshToken(refreshJwt)
try {
await this.persistanceService.saveRefreshToken(refreshJwt)
} catch (error) {
this.logger.error(error, 'Error saving refresh token')
}
}
} }
private parseJwt(token: string) { private parseJwt(token: string) {
@ -57,9 +68,14 @@ export class AuthenticationService extends ServiceBase {
return JSON.parse(window.atob(base64)) return JSON.parse(window.atob(base64))
} }
fromStorage() { async fromStorage() {
const token = sessionStorage.getItem('token') console.log('fromStorage')
if (token) { const auth = useAuthStore()
const { setToken, setUser } = auth
const token = await this.persistanceService.readToken()
const refreshToken = await this.persistanceService.readRefreshToken()
if (token && refreshToken) {
try { try {
const parsed = this.parseJwt(token) const parsed = this.parseJwt(token)
const isAfter = dayjs().isAfter(parsed.exp * 1000) const isAfter = dayjs().isAfter(parsed.exp * 1000)
@ -67,7 +83,8 @@ export class AuthenticationService extends ServiceBase {
this.removePersistence() this.removePersistence()
return return
} }
this.persist(token) setToken(token)
setUser(parsed)
this.logger.debug('Token loaded from storage', parsed) this.logger.debug('Token loaded from storage', parsed)
} catch (error) { } catch (error) {
this.logger.error(error, 'Error parsing token') this.logger.error(error, 'Error parsing token')

View File

@ -0,0 +1,30 @@
import type { StorageInterface } from './StorageInterface'
export class LocalStorageService implements StorageInterface {
async saveUserDataText(fileName: string, content: any) {
localStorage.setItem(`net.xintanalabs.domino.${fileName}`, content)
}
async saveUserDataJson(fileName: string, content: any) {
localStorage.setItem(`net.xintanalabs.domino.${fileName}`, JSON.stringify(content))
}
async saveConfigData(content: any) {
localStorage.setItem('net.xintanalabs.domino.config', JSON.stringify(content))
}
async readUserDataText(fileName: string) {
const content: string | null = localStorage.getItem(`net.xintanalabs.domino.${fileName}`)
return content || ''
}
async readUserDataJson(fileName: string) {
const content: string | null = localStorage.getItem(`net.xintanalabs.domino.${fileName}`)
return JSON.parse(content || 'null')
}
async readConfigData() {
const content: string | null = localStorage.getItem('net.xintanalabs.domino.config')
return JSON.parse(content || '{}')
}
}

View File

@ -1,5 +1,6 @@
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import type { AuthenticationService } from './AuthenticationService'
interface RequestOptions { interface RequestOptions {
uri: string uri: string
@ -45,12 +46,20 @@ export class NetworkService {
} }
const fetchOptions = this.getFetchOptions(options) const fetchOptions = this.getFetchOptions(options)
const urlParams = this.getURLParams(params) const urlParams = this.getURLParams(params)
const res = await fetch(`${this.API_URL}${uri}${urlParams}`, fetchOptions) let response = await fetch(`${this.API_URL}${uri}${urlParams}`, fetchOptions)
if (!res.ok) { if (response.status === 401) {
const newAccessToken = await this.refresh()
if (newAccessToken) {
fetchOptions.headers.Authorization = newAccessToken
response = await fetch(`${this.API_URL}${uri}${urlParams}`, fetchOptions)
}
}
if (!response.ok) {
throw new Error('Network response was not ok') throw new Error('Network response was not ok')
} }
const text = await res.text() const text = await response.text()
if (text === '') { if (text === '') {
return return
@ -59,6 +68,18 @@ export class NetworkService {
} }
} }
async refresh() {
const { refreshToken } = storeToRefs(this.auth)
const { setToken } = this.auth
const response = await await this.post({
uri: '/refresh',
body: { token: refreshToken.value },
})
const { token } = response
setToken(token)
return token
}
getURLParams(params: any) { getURLParams(params: any) {
if (!params) { if (!params) {
return '' return ''
@ -72,7 +93,7 @@ export class NetworkService {
const { body, auth, method = 'GET' } = opts const { body, auth, method = 'GET' } = opts
const options: any = { const options: any = {
method, method,
headers: this.getHeaders({ auth }) headers: this.getHeaders({ auth }),
} }
if (!['GET', 'HEAD'].includes(method) && body) { if (!['GET', 'HEAD'].includes(method) && body) {
options.body = typeof body === 'string' ? body : JSON.stringify(body) options.body = typeof body === 'string' ? body : JSON.stringify(body)
@ -84,7 +105,7 @@ export class NetworkService {
getHeaders({ auth = true }): any { getHeaders({ auth = true }): any {
const { jwt } = storeToRefs(this.auth) const { jwt } = storeToRefs(this.auth)
const headers: any = { const headers: any = {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} }
if (auth) { if (auth) {
headers.Authorization = jwt.value headers.Authorization = jwt.value

View File

@ -0,0 +1,58 @@
import { LocalStorageService } from './LocalStorageService'
import type { StorageInterface } from './StorageInterface'
import { TauriFileStorageService } from './TauriFileStorageService'
export class PersistenceService {
private static instance: PersistenceService
private isTauri: boolean = false
private storage: StorageInterface
private constructor() {
this.isTauri = window.__TAURI_METADATA__ ? true : false
this.storage = this.isTauri ? new TauriFileStorageService() : new LocalStorageService()
console.log('PersistenceService created', this.isTauri)
}
static getInstance(): PersistenceService {
if (!PersistenceService.instance) {
PersistenceService.instance = new PersistenceService()
}
return PersistenceService.instance
}
async saveToken(token: string) {
await this.storage.saveUserDataText('token', token)
}
async saveRefreshToken(refreshToken: string) {
await this.storage.saveUserDataText('refreshToken', refreshToken)
}
async readToken(): Promise<string> {
try {
const token = await this.storage.readUserDataText('token')
return token
} catch (error) {
console.error(error)
return ''
}
}
async readRefreshToken() {
try {
const refreshToken = await this.storage.readUserDataText('refreshToken')
return refreshToken
} catch (error) {
console.error(error)
return ''
}
}
async saveConfig(config: any) {
await this.storage.saveConfigData(config)
}
async readConfig() {
const config = await this.storage.readConfigData()
return config
}
}

View File

@ -0,0 +1,8 @@
export interface StorageInterface {
saveUserDataText(fileName: string, content: any): Promise<void>
saveUserDataJson(fileName: string, content: any): Promise<void>
saveConfigData(content: any): Promise<void>
readUserDataText(fileName: string): Promise<string>
readUserDataJson(fileName: string): Promise<any>
readConfigData(): Promise<any>
}

View File

@ -0,0 +1,94 @@
import { appConfigDir, appLocalDataDir, appCacheDir, appDataDir, join } from '@tauri-apps/api/path'
import { writeTextFile, readTextFile, exists, createDir } from '@tauri-apps/api/fs'
import type { StorageInterface } from './StorageInterface'
import { ServiceBase } from './ServiceBase'
export class TauriFileStorageService extends ServiceBase implements StorageInterface {
constructor() {
super()
this.showDirs()
}
async showDirs() {
this.logger.debug(`=> appConfigDir ${await appConfigDir()}`)
this.logger.debug(`=> appLocalDataDir ${await appLocalDataDir()}`)
this.logger.debug(`=> appCacheDir ${await appCacheDir()}`)
this.logger.debug(`=> appDataDir ${await appDataDir()}`)
}
async saveUserDataText(fileName: string, content: any) {
const userAppDir = await appDataDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, fileName + '.txt')
await this.write(filePath, content, false)
}
async saveUserDataJson(fileName: string, content: any) {
const userAppDir = await appDataDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, fileName + '.json')
await this.write(filePath, content, true)
}
async readUserDataText(fileName: string) {
const userAppDir = await appDataDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, fileName + '.txt')
const content = await this.read(filePath, false)
return content
}
async readUserDataJson(fileName: string) {
const userAppDir = await appDataDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, fileName + '.json')
const content = await this.read(filePath, true)
return content
}
async saveConfigData(content: any) {
const userAppDir = await appConfigDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, 'config.json')
await this.write(filePath, content)
}
async readConfigData() {
const userAppDir = await appConfigDir()
await this.ensureDirExists(userAppDir)
const filePath = await join(userAppDir, 'config.json')
const content = await this.read(filePath, true)
return content
}
private async ensureDirExists(dir: string) {
const dirExists = await exists(dir)
if (!dirExists) {
await createDir(dir)
}
}
private async write(filePath: string, content: any, json: boolean = false) {
this.logger.trace(`write ${filePath}`, content)
if (json) {
return writeTextFile(filePath, JSON.stringify(content, null, 2))
}
return writeTextFile(filePath, content)
}
private async read(filePath: string, json: boolean = false) {
let content = null
try {
if (await exists(filePath)) {
content = await readTextFile(filePath)
if (json) {
content = JSON.parse(content as string)
}
}
} catch (error) {
this.logger.error(error)
}
return content
}
}

View File

@ -3,15 +3,42 @@ import { computed, ref } from 'vue'
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const jwt = ref<string | undefined>(undefined) const jwt = ref<string | undefined>(undefined)
const refreshJwt = ref<string | undefined>(undefined)
const user = ref<any | undefined>(undefined) const user = ref<any | undefined>(undefined)
const roles = ref<string[]>([]) const roles = ref<string[]>([])
const isLoggedIn = computed(() => jwt.value !== undefined) const isLoggedIn = computed(() => jwt.value !== undefined)
function setJwt(token: string | undefined) { const token = computed(() => {
if (jwt.value) {
return jwt.value
}
return sessionStorage.getItem('token')
})
const refreshToken = computed(() => {
if (refreshJwt.value) {
return refreshJwt.value
}
return sessionStorage.getItem('token_refresh')
})
function setToken(token: string | undefined) {
jwt.value = token jwt.value = token
} }
function clearToken() {
jwt.value = undefined
}
function setRefreshToken(token: string | undefined) {
refreshJwt.value = token
}
function clearRefreshToken() {
refreshJwt.value = undefined
}
function setUser(userIn: any | undefined) { function setUser(userIn: any | undefined) {
user.value = userIn user.value = userIn
setRoles(userIn?.roles ?? []) setRoles(userIn?.roles ?? [])
@ -21,13 +48,23 @@ export const useAuthStore = defineStore('auth', () => {
roles.value = rolesIn roles.value = rolesIn
} }
function clearUser() {
user.value = undefined
}
return { return {
jwt, jwt,
user, user,
roles, roles,
isLoggedIn, isLoggedIn,
setJwt, token,
refreshToken,
setToken,
clearToken,
setRefreshToken,
clearRefreshToken,
setUser, setUser,
setRoles clearUser,
setRoles,
} }
}) })

View File

@ -9,7 +9,7 @@ import { useRouter } from 'vue-router'
const { toClipboard } = useClipboard() const { toClipboard } = useClipboard()
const gameStore = useGameStore() const gameStore = useGameStore()
const { moveToMake, canMakeMove, sessionState, gameState, playerState } = storeToRefs(gameStore) const { moveToMake, canMakeMove, sessionState, playerState } = storeToRefs(gameStore)
onMounted(async () => { onMounted(async () => {
// startMatch() // startMatch()
@ -39,7 +39,7 @@ function copySeed() {
<template> <template>
<div class="block"> <div class="block">
<section class="block info"> <!-- <section class="block info">
<p>Running: {{ sessionState?.sessionInProgress }}</p> <p>Running: {{ sessionState?.sessionInProgress }}</p>
<p>Seed: {{ sessionState?.seed }}</p> <p>Seed: {{ sessionState?.seed }}</p>
<p> <p>
@ -50,7 +50,7 @@ function copySeed() {
<p>Score: {{ sessionState?.scoreboard }}</p> <p>Score: {{ sessionState?.scoreboard }}</p>
<p v-if="sessionState?.id">SessionID: {{ sessionState.id }}</p> <p v-if="sessionState?.id">SessionID: {{ sessionState.id }}</p>
<p>PlayerID: {{ playerState?.id }}</p> <p>PlayerID: {{ playerState?.id }}</p>
</section> </section> -->
<section class="block"> <section class="block">
<div class="game-container"> <div class="game-container">
<GameComponent :playerId="playerState?.id" :canMakeMove="canMakeMove" @move="makeMove" /> <GameComponent :playerId="playerState?.id" :canMakeMove="canMakeMove" @move="makeMove" />

View File

@ -8,7 +8,7 @@ import type { GameService } from '@/services/GameService'
import type { MatchSessionOptions, MatchSessionDto } from '@/common/interfaces' import type { MatchSessionOptions, MatchSessionDto } from '@/common/interfaces'
import { useEventBusStore } from '@/stores/eventBus' import { useEventBusStore } from '@/stores/eventBus'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { copyToclipboard } from '@/common/helpers' import { copyToclipboard, wait } from '@/common/helpers'
import { useGameOptionsStore } from '@/stores/gameOptions' import { useGameOptionsStore } from '@/stores/gameOptions'
import MatchConfiguration from '@/components/MatchConfiguration.vue' import MatchConfiguration from '@/components/MatchConfiguration.vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -127,14 +127,20 @@ const canStart = computed(() => {
return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady) return (!options?.teamed && allReady) || (options?.teamed && !!teamedWith.value && allReady)
}) })
const isMultiplayer = computed(
() => (sessionState?.value?.options?.numPlayers || gameOptions.value?.numPlayers || 0) > 1,
)
async function loadData() { async function loadData() {
loadingSessions.value = true
const listResponse = await gameService.listMatchSessions() const listResponse = await gameService.listMatchSessions()
loadingSessions.value = false
matchSessions.value = listResponse.data matchSessions.value = listResponse.data
} }
onMounted(() => { onMounted(() => {
// loadData() loadData()
// dataInterval = setInterval(loadData, 5000) dataInterval = setInterval(loadData, 5000)
}) })
onUnmounted(() => { onUnmounted(() => {
@ -145,31 +151,34 @@ function copy(sessionSeed: string) {
copyToclipboard(sessionSeed) copyToclipboard(sessionSeed)
} }
let tabs = ref<any[]>([ let selectedTab = ref('create-tab')
{ label: t('create-session'), id: 'create-tab', active: true, disabled: false },
{ label: t('join-a-multiplayer-session'), id: 'join-tab', active: false, disabled: false },
{ label: t('tournaments'), id: 'torunaments-tab', active: false, disabled: true },
])
const selectedTab = computed(() => tabs.value.find((t) => t.active)?.id)
const isCreateTab = computed(() => selectedTab.value === 'create-tab') const isCreateTab = computed(() => selectedTab.value === 'create-tab')
const isJoinTab = computed(() => selectedTab.value === 'join-tab') const isJoinTab = computed(() => selectedTab.value === 'join-tab')
const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab') const isTournamentTab = computed(() => selectedTab.value === 'torunaments-tab')
const tabs = computed<any[]>((): any => [
{ label: t('create-session'), id: 'create-tab', disabled: false },
{
label: t('join-a-multiplayer-session', matchSessions.value.length),
id: 'join-tab',
disabled: matchSessions.value.length <= 0,
},
{ label: t('tournaments'), id: 'torunaments-tab', disabled: true },
])
async function tabClick(tab: any) { async function tabClick(tab: any) {
tabs.value.forEach((t) => (t.active = t === tab)) selectedTab.value = tab.id
if (tab.id === 'join-tab') {
loadingSessions.value = true
await loadData()
dataInterval = setInterval(loadData, 5000)
} else {
clearInterval(dataInterval)
}
} }
function onCreateMatch(options: MatchSessionOptions) { function onCreateMatch(options: MatchSessionOptions) {
console.log('Creating match', options)
createMatch(options) createMatch(options)
} }
async function onStartSingleMatch(options: MatchSessionOptions) {
await createMatch(options)
await wait(1000)
startMatch()
}
</script> </script>
<template> <template>
@ -185,7 +194,7 @@ function onCreateMatch(options: MatchSessionOptions) {
<li <li
v-bind:key="tab.label" v-bind:key="tab.label"
v-for="tab in tabs" v-for="tab in tabs"
:class="{ 'is-active': tab.active, 'is-disabled': tab.disabled }" :class="{ 'is-active': selectedTab === tab.id, 'is-disabled': tab.disabled }"
> >
<a @click="() => tabClick(tab)">{{ tab.label }}</a> <a @click="() => tabClick(tab)">{{ tab.label }}</a>
</li> </li>
@ -194,7 +203,10 @@ function onCreateMatch(options: MatchSessionOptions) {
<!-- Tabs End --> <!-- Tabs End -->
<!-- Match Configuration --> <!-- Match Configuration -->
<section class="section" v-if="isCreateTab"> <section class="section" v-if="isCreateTab">
<MatchConfiguration @create-match="onCreateMatch" /> <MatchConfiguration
@create-match="onCreateMatch"
@start-single-match="onStartSingleMatch"
/>
</section> </section>
<!-- Match Configuration End --> <!-- Match Configuration End -->
<!-- Join a Multiplayer Session --> <!-- Join a Multiplayer Session -->
@ -244,8 +256,7 @@ function onCreateMatch(options: MatchSessionOptions) {
<section class="section" v-if="isTournamentTab"></section> <section class="section" v-if="isTournamentTab"></section>
<!-- Tournaments End --> <!-- Tournaments End -->
</div> </div>
<div class="block" v-if="isSessionStarted && isMultiplayer">
<div class="block" v-if="isSessionStarted">
<h2 class="title is-4">{{ sessionState?.name }}</h2> <h2 class="title is-4">{{ sessionState?.name }}</h2>
<h6 class="title is-size-5">Players</h6> <h6 class="title is-size-5">Players</h6>
<div v-for="player in sessionState?.players" :key="player.id"> <div v-for="player in sessionState?.players" :key="player.id">

View File

@ -3,11 +3,14 @@ import { AuthenticationService } from '@/services/AuthenticationService'
import { inject, ref } from 'vue' import { inject, ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { emit, listen } from '@tauri-apps/api/event'
const router = useRouter() const router = useRouter()
const username = ref('') const username = ref('')
const password = ref('') const password = ref('')
const errorLogin = ref(false)
const { t } = useI18n() const { t } = useI18n()
const isTauri = window.__TAURI_METADATA__ ? true : false
const authService = inject<AuthenticationService>('auth') const authService = inject<AuthenticationService>('auth')
@ -16,7 +19,7 @@ async function login() {
await authService?.login(username.value, password.value) await authService?.login(username.value, password.value)
router.push({ name: 'home' }) router.push({ name: 'home' })
} catch (error) { } catch (error) {
alert(t('invalid-username-or-password')) errorLogin.value = true
} }
// if (username.value === 'admin' && password.value === 'password') { // if (username.value === 'admin' && password.value === 'password') {
// localStorage.setItem('token', 'true') // localStorage.setItem('token', 'true')
@ -25,11 +28,43 @@ async function login() {
// alert('Invalid username or password') // alert('Invalid username or password')
// } // }
} }
// // Listen for update available event
// listen('tauri://update-available', () => {
// console.log('Update is available!')
// // You can show a dialog or notify the user here
// })
// // Listen for update not available event
// listen('tauri://update-not-available', () => {
// console.log('No update available.')
// })
// // Listen for update download progress
// listen('tauri://update-download-progress', (event) => {
// console.log('Update download progress:', event.payload)
// // You can update a progress bar here
// })
// // Listen for update download finished
// listen('tauri://update-download-finished', () => {
// console.log('Update download finished.')
// // You can notify the user to restart the app
// })
function checkForUpdates() {
emit('tauri://update')
}
</script> </script>
<template> <template>
<div class="login"> <div class="login">
<h1 class="title">{{ $t('login') }}</h1> <h1 class="title">{{ $t('login') }}</h1>
<div class="message is-danger">
<div class="message-body" v-if="errorLogin">
{{ $t('invalid-username-or-password') }}
</div>
</div>
<form class="form" @submit.prevent="login"> <form class="form" @submit.prevent="login">
<div class="field"> <div class="field">
<label class="label">{{ $t('username') }}</label> <label class="label">{{ $t('username') }}</label>
@ -63,5 +98,6 @@ async function login() {
</div> --> </div> -->
</div> </div>
</form> </form>
<a href="#" @click="checkForUpdates" v-if="isTauri">Update</a>
</div> </div>
</template> </template>

View File

@ -2,14 +2,17 @@
import type { MatchSessionDto } from '@/common/interfaces' import type { MatchSessionDto } from '@/common/interfaces'
import type { GameService } from '@/services/GameService' import type { GameService } from '@/services/GameService'
import type { LoggingService } from '@/services/LoggingService' import type { LoggingService } from '@/services/LoggingService'
import ScoreboardTableComponent from '@/components/ScoreboardTableComponent.vue'
import { inject, onBeforeMount, ref, toRaw } from 'vue' import { inject, onBeforeMount, ref, toRaw } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useGameStore } from '@/stores/game'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const gameStore = useGameStore()
const gameService: GameService = inject<GameService>('game') as GameService const gameService: GameService = inject<GameService>('game') as GameService
const logger: LoggingService = inject<LoggingService>('logger') as LoggingService const logger: LoggingService = inject<LoggingService>('logger') as LoggingService
const { updateSessionState } = gameStore
let sessionId: string let sessionId: string
let matchSession = ref<MatchSessionDto | undefined>(undefined) let matchSession = ref<MatchSessionDto | undefined>(undefined)
@ -28,57 +31,45 @@ onBeforeMount(() => {
router.push({ name: 'home' }) router.push({ name: 'home' })
} }
}) })
function gotoHome() {
updateSessionState(undefined)
router.push({ name: 'home' })
}
</script> </script>
<template> <template>
<div class="container"> <div class="container">
<h1 class="title is-1">{{ $t('match-page') }}</h1> <h1 class="title is-1">{{ $t('match-page') }}</h1>
<h2 class="title is-3">{{ matchSession?.name }}</h2> <h2 class="title is-3">{{ matchSession?.name }}</h2>
<div class="block mt-6">
<p class="mb-4"> <div class="level">
<span class="title is-5">{{ $t('winner') }}</span> <div class="level-item has-text-centered">
<span class="is-size-5 ml-4">{{ matchSession?.matchWinner?.name }}</span> <div>
</p> <p class="heading">{{ $t('winner') }}</p>
<p class="mb-4"> <p class="title is-size-3">{{ matchSession?.matchWinner?.name }}</p>
<span class="title is-5">{{ $t('win-type') }}</span>
<span class="is-size-5 ml-4">{{ matchSession?.options.winType }}</span>
</p>
<p class="mb-4">
<span class="title is-5">{{ $t('points-to-win') }}</span>
<span class="is-size-5 ml-4">{{ matchSession?.options.winTarget }}</span>
</p>
<h3 class="title is-5">{{ $t('final-scoreboard') }}</h3>
<div v-bind:key="$index" v-for="(score, $index) in matchSession?.scoreboard">
<p class="">
<span class="title is-5">{{ score.name }}</span>
<span class="is-size-5 ml-4">{{ score.score }}</span>
</p>
</div> </div>
</div> </div>
<div class="grid"> <div class="level-item has-text-centered">
<div <div>
class="cell" <p class="heading">{{ $t('win-type') }}</p>
v-bind:key="$index" <p class="title">{{ matchSession?.options.winType }}</p>
v-for="(summary, $index) in matchSession?.gameSummaries" </div>
> </div>
<div class="block mt-6"> <div class="level-item has-text-centered">
<h3 class="title is-5">{{ $t('round-index-1', [$index + 1]) }}</h3> <div>
<p class="mb-4"> <p class="heading">{{ $t('points-to-win') }}</p>
<span class="title is-5">{{ $t('winner') }}</span> <p class="title">{{ matchSession?.options.winTarget }}</p>
<span class="is-size-5 ml-4">{{ summary.winner?.name }}</span>
</p>
<h4 class="title is-6">{{ $t('scoreboard') }}</h4>
<div v-bind:key="$index" v-for="(gameScore, $index) in summary.score">
<p class="">
<span class="title is-5">{{ gameScore.name }}</span>
<span class="is-size-5 ml-4">{{ gameScore.score }}</span>
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
<ScoreboardTableComponent
:games="matchSession?.gameSummaries"
:final-score="matchSession?.scoreboard"
:winner="matchSession?.matchWinner"
/>
<div class="buttons"> <div class="buttons">
<button class="button is-primary" @click="router.push({ name: 'home' })"> <button class="button is-primary" @click="gotoHome">
{{ $t('back') }} {{ $t('back') }}
</button> </button>
</div> </div>

View File

@ -1,6 +1,6 @@
{ {
"extends": "@vue/tsconfig/tsconfig.dom.json", "extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"], "include": ["types/env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"], "exclude": ["src/**/__tests__/*"],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,

View File

@ -3,6 +3,7 @@
"types": ["vite/client"] "types": ["vite/client"]
}, },
"files": [], "files": [],
"typeRoots": ["./src/types"],
"references": [ "references": [
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.node.json"

View File

View File

@ -3,3 +3,7 @@ declare module 'socket.io' {
data: any data: any
} }
} }
declare interface Window {
__TAURI__: any
}