Upgrade to latest Seed, remove JS WebSocket, remove webpack dependency
This commit is contained in:
parent
9625cde0f6
commit
39ee462e33
358
Cargo.lock
generated
358
Cargo.lock
generated
@ -15,7 +15,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"futures 0.3.4",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"pin-project",
|
||||
"smallvec",
|
||||
@ -35,7 +35,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
@ -54,7 +54,7 @@ dependencies = [
|
||||
"either",
|
||||
"futures 0.3.4",
|
||||
"http",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"trust-dns-proto",
|
||||
"trust-dns-resolver",
|
||||
]
|
||||
@ -84,10 +84,10 @@ dependencies = [
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"v_htmlescape",
|
||||
]
|
||||
|
||||
@ -124,12 +124,12 @@ dependencies = [
|
||||
"indexmap",
|
||||
"language-tags",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"rand 0.7.3",
|
||||
"regex 1.3.6",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
@ -161,7 +161,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"futures 0.3.4",
|
||||
"httparse",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mime",
|
||||
"time 0.1.42",
|
||||
"twoway",
|
||||
@ -175,8 +175,8 @@ checksum = "9d7a10ca4d94e8c8e7a87c5173aba1b97ba9a6563ca02b0e1cd23531093d3ec8"
|
||||
dependencies = [
|
||||
"bytestring",
|
||||
"http",
|
||||
"log 0.4.8",
|
||||
"regex 1.3.6",
|
||||
"log",
|
||||
"regex",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -204,7 +204,7 @@ dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mio",
|
||||
"mio-uds",
|
||||
"net2",
|
||||
@ -233,7 +233,7 @@ dependencies = [
|
||||
"actix-server",
|
||||
"actix-service",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"net2",
|
||||
]
|
||||
|
||||
@ -246,7 +246,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"futures-channel",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"threadpool",
|
||||
@ -265,7 +265,7 @@ dependencies = [
|
||||
"derive_more",
|
||||
"either",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -281,7 +281,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"either",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"pin-project",
|
||||
"slab",
|
||||
]
|
||||
@ -310,16 +310,16 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"futures 0.3.4",
|
||||
"fxhash",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mime",
|
||||
"net2",
|
||||
"pin-project",
|
||||
"regex 1.3.6",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"time 0.1.42",
|
||||
"url 2.1.1",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -365,22 +365,13 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
dependencies = [
|
||||
"memchr 0.1.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
|
||||
dependencies = [
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -464,9 +455,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-core",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -709,7 +700,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"regex 1.3.6",
|
||||
"regex",
|
||||
"twoway",
|
||||
"typed-arena",
|
||||
"unicode_categories",
|
||||
@ -733,12 +724,12 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.12.0"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5"
|
||||
checksum = "0c60ef6d0bbf56ad2674249b6bb74f2c6aeb98b98dd57b5d3e37cace33011d69"
|
||||
dependencies = [
|
||||
"time 0.1.42",
|
||||
"url 1.7.2",
|
||||
"percent-encoding",
|
||||
"time 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1025,16 +1016,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
|
||||
dependencies = [
|
||||
"log 0.3.9",
|
||||
"regex 0.1.80",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@ -1043,8 +1024,8 @@ checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log 0.4.8",
|
||||
"regex 1.3.6",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -1261,7 +1242,7 @@ dependencies = [
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
@ -1304,6 +1285,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1312,12 +1294,37 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-events"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088514ec8ef284891c762c88a66b639b3a730134714692ee31829765c5bc814f"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-file"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9fecfe46b5dc3cc46f58e98ba580cc714f2c93860796d002eb3527a465ef49"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"gloo-events",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
@ -1336,7 +1343,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -1448,7 +1455,7 @@ dependencies = [
|
||||
"http-body",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"net2",
|
||||
"pin-project",
|
||||
"time 0.1.42",
|
||||
@ -1470,17 +1477,6 @@ dependencies = [
|
||||
"tokio-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
@ -1600,21 +1596,20 @@ dependencies = [
|
||||
"chrono",
|
||||
"diesel",
|
||||
"dotenv",
|
||||
"env_logger 0.7.1",
|
||||
"env_logger",
|
||||
"futures 0.3.4",
|
||||
"ipnetwork",
|
||||
"jirs-data",
|
||||
"lettre",
|
||||
"lettre_email",
|
||||
"libc",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"pq-sys",
|
||||
"pretty_env_logger",
|
||||
"quickcheck",
|
||||
"r2d2",
|
||||
"rusoto_core",
|
||||
"rusoto_s3",
|
||||
@ -1622,7 +1617,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"time 0.1.42",
|
||||
"toml",
|
||||
"url 2.1.1",
|
||||
"url",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
@ -1647,9 +1642,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
|
||||
checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@ -1692,7 +1687,7 @@ dependencies = [
|
||||
"bufstream",
|
||||
"fast_chemail",
|
||||
"hostname 0.1.5",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"native-tls",
|
||||
"nom",
|
||||
"serde",
|
||||
@ -1735,15 +1730,6 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
dependencies = [
|
||||
"log 0.4.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
@ -1792,15 +1778,6 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
@ -1844,7 +1821,7 @@ dependencies = [
|
||||
"iovec",
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"miow 0.2.1",
|
||||
"net2",
|
||||
"slab",
|
||||
@ -1858,7 +1835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
|
||||
dependencies = [
|
||||
"lazycell",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mio",
|
||||
"slab",
|
||||
]
|
||||
@ -1869,7 +1846,7 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
|
||||
dependencies = [
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"mio",
|
||||
"miow 0.3.3",
|
||||
"winapi 0.3.8",
|
||||
@ -1916,7 +1893,7 @@ checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
@ -1943,7 +1920,7 @@ version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
dependencies = [
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
"version_check 0.1.5",
|
||||
]
|
||||
|
||||
@ -2068,12 +2045,6 @@ dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -2182,8 +2153,8 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger 0.7.1",
|
||||
"log 0.4.8",
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2209,13 +2180,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.6.1"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c205cc82214f3594e2d50686730314f817c67ffa80fe800cf0db78c3c2b9d9e"
|
||||
checksum = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
@ -2225,17 +2196,6 @@ version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02c2411d418cea2364325b18a205664f9ef8252e06b2e911db97c0b0d98b1406"
|
||||
dependencies = [
|
||||
"env_logger 0.3.5",
|
||||
"log 0.3.9",
|
||||
"rand 0.3.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
@ -2251,21 +2211,11 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af"
|
||||
dependencies = [
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand 0.4.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
@ -2293,7 +2243,7 @@ dependencies = [
|
||||
"rand_isaac",
|
||||
"rand_jitter",
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_pcg 0.1.2",
|
||||
"rand_xorshift",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
@ -2309,6 +2259,7 @@ dependencies = [
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
"rand_pcg 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2417,6 +2368,15 @@ dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
@ -2452,37 +2412,18 @@ dependencies = [
|
||||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3",
|
||||
"memchr 0.1.11",
|
||||
"regex-syntax 0.3.9",
|
||||
"thread_local 0.2.7",
|
||||
"utf8-ranges",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.10",
|
||||
"memchr 2.3.3",
|
||||
"regex-syntax 0.6.17",
|
||||
"thread_local 1.0.1",
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.17"
|
||||
@ -2523,9 +2464,9 @@ dependencies = [
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"md5",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"rusoto_credential",
|
||||
"rusoto_signature",
|
||||
@ -2549,7 +2490,7 @@ dependencies = [
|
||||
"futures 0.3.4",
|
||||
"hyper",
|
||||
"pin-project",
|
||||
"regex 1.3.6",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
@ -2583,9 +2524,9 @@ dependencies = [
|
||||
"hmac",
|
||||
"http",
|
||||
"hyper",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"md5",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"rusoto_credential",
|
||||
"rustc_version",
|
||||
@ -2699,21 +2640,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "seed"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd5d8fd7f12f565f639caf4b6d74a3853d5d7234d0543ec3beae81311492623e"
|
||||
checksum = "882f4569a394bbb2f15f2fc410e0fbcef178fe24fc2d91599607a598443c6df8"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"cookie",
|
||||
"dbg",
|
||||
"enclose",
|
||||
"futures 0.3.4",
|
||||
"gloo-file",
|
||||
"gloo-timers",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"pulldown-cmark",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid 0.8.1",
|
||||
"version_check 0.9.1",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@ -2737,18 +2681,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.105"
|
||||
version = "1.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff"
|
||||
checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.105"
|
||||
version = "1.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8"
|
||||
checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2757,9 +2701,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.48"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||
checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -2775,7 +2719,7 @@ dependencies = [
|
||||
"dtoa",
|
||||
"itoa",
|
||||
"serde",
|
||||
"url 2.1.1",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2970,25 +2914,6 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
dependencies = [
|
||||
"thread-id",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
@ -3068,7 +2993,7 @@ dependencies = [
|
||||
"iovec",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
"mio",
|
||||
"mio-named-pipes",
|
||||
"mio-uds",
|
||||
@ -3109,7 +3034,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
@ -3139,14 +3064,14 @@ dependencies = [
|
||||
"enum-as-inner",
|
||||
"failure",
|
||||
"futures 0.3.4",
|
||||
"idna 0.2.0",
|
||||
"idna",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"smallvec",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"url 2.1.1",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3160,7 +3085,7 @@ dependencies = [
|
||||
"futures 0.3.4",
|
||||
"ipconfig",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"lru-cache",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
@ -3180,7 +3105,7 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc"
|
||||
dependencies = [
|
||||
"memchr 2.3.3",
|
||||
"memchr",
|
||||
"unchecked-index",
|
||||
]
|
||||
|
||||
@ -3259,34 +3184,17 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
|
||||
dependencies = [
|
||||
"idna 0.1.5",
|
||||
"matches",
|
||||
"percent-encoding 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||
dependencies = [
|
||||
"idna 0.2.0",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding 2.1.0",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.6.5"
|
||||
@ -3388,7 +3296,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||
dependencies = [
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
@ -3400,9 +3308,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.60"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f"
|
||||
checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"serde",
|
||||
@ -3412,13 +3320,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.60"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd"
|
||||
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log 0.4.8",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -3427,9 +3335,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.10"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a"
|
||||
checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@ -3439,9 +3347,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.60"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4"
|
||||
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -3449,9 +3357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.60"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931"
|
||||
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3462,9 +3370,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.60"
|
||||
version = "0.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639"
|
||||
checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
@ -3492,9 +3400,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.37"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb"
|
||||
checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -19,7 +19,7 @@ opt-level = 's'
|
||||
|
||||
[dependencies]
|
||||
jirs-data = { path = "../jirs-data" }
|
||||
seed = { version = "*" }
|
||||
seed = { version = "0.7.0" }
|
||||
serde = "*"
|
||||
serde_json = "*"
|
||||
bincode = "1.2.1"
|
||||
|
21
jirs-client/jirs.nginx
Normal file
21
jirs-client/jirs.nginx
Normal file
@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name jirs.lvh.me;
|
||||
|
||||
charset utf-8;
|
||||
root /home/eraden/code/eraden/jirs/jirs-client/tmp;
|
||||
|
||||
location ~ .wasm {
|
||||
default_type application/wasm;
|
||||
}
|
||||
|
||||
location *.js {
|
||||
default_type application/javascript;
|
||||
}
|
||||
|
||||
location / {
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 404 =200 /index.html;
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
.styledInput {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.styledInput > .inputElement {
|
||||
min-height: 32px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0 7px;
|
||||
|
@ -2,85 +2,10 @@ const getWsHostName = () => process.env.JIRS_SERVER_BIND === "0.0.0.0" ? 'localh
|
||||
const getProtocol = () => window.location.protocol.replace(/^http/, 'ws');
|
||||
const wsUrl = () => `${ getProtocol() }//${ getWsHostName() }:${ process.env.JIRS_SERVER_PORT }/ws/`;
|
||||
|
||||
import("../pkg/index.js").then(module => {
|
||||
let queue = [];
|
||||
let ws;
|
||||
|
||||
const buildWebSocket = () => {
|
||||
ws = new WebSocket(wsUrl());
|
||||
ws.binaryType = 'blob';
|
||||
ws.onopen = event => {
|
||||
console.log('open', event);
|
||||
module.reconnected();
|
||||
};
|
||||
ws.onerror = event => {
|
||||
console.error(event);
|
||||
};
|
||||
ws.onmessage = async event => {
|
||||
const arrayBuffer = await event.data.arrayBuffer();
|
||||
const array = new Uint8Array(arrayBuffer);
|
||||
module.handle_ws_message(array);
|
||||
};
|
||||
ws.onclose = () => {
|
||||
setTimeout(() => buildWebSocket(), 600);
|
||||
};
|
||||
};
|
||||
buildWebSocket();
|
||||
|
||||
window.send_bin_code = code => queue.push(code);
|
||||
window.inspectQueue = () => queue;
|
||||
|
||||
let wsCheckDelay = 100;
|
||||
const flush = () => {
|
||||
if (queue.length >= 1000) {
|
||||
ws.close();
|
||||
throw new Error("Message queue overflow");
|
||||
}
|
||||
// if (queue.length && wsCheckDelay <= 0) console.log(ws.readyState, queue);
|
||||
switch (ws.readyState) {
|
||||
case 1: {
|
||||
const [ code, ...rest ] = queue;
|
||||
queue = rest;
|
||||
if (code) {
|
||||
// console.log('open', code);
|
||||
ws.send(Uint8Array.from(code).buffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
window.requestAnimationFrame(flush);
|
||||
};
|
||||
window.flush = flush;
|
||||
|
||||
const keepWsOpen = () => {
|
||||
if (wsCheckDelay > 0) {
|
||||
wsCheckDelay -= 1;
|
||||
} else {
|
||||
wsCheckDelay = 100;
|
||||
switch (ws.readyState) {
|
||||
case 1: {
|
||||
// console.log('sending ping');
|
||||
// ws.send(Uint8Array.from([ 0, 0, 0, 0 ]).buffer);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
case 2:
|
||||
break;
|
||||
case 3:
|
||||
throw new Error('web socket has been closed');
|
||||
buildWebSocket();
|
||||
break;
|
||||
}
|
||||
}
|
||||
window.requestAnimationFrame(keepWsOpen);
|
||||
};
|
||||
|
||||
keepWsOpen();
|
||||
flush();
|
||||
|
||||
import("/jirs.js").then(async module => {
|
||||
window.module = module;
|
||||
console.log(module)
|
||||
await module.default();
|
||||
const host_url = `${ location.protocol }//${ process.env.JIRS_SERVER_BIND }:${ process.env.JIRS_SERVER_PORT }`;
|
||||
module.set_host_url(host_url);
|
||||
module.render();
|
||||
module.render(host_url, wsUrl());
|
||||
});
|
||||
|
15
jirs-client/js/template.html
Normal file
15
jirs-client/js/template.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<link rel="icon" href="/logo.svg">
|
||||
<title>JIRS</title>
|
||||
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="app"></main>
|
||||
<script type="module" src="/index.js"></script>
|
||||
</body>
|
||||
</html>
|
16
jirs-client/scripts/dev.sh
Executable file
16
jirs-client/scripts/dev.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. .env
|
||||
|
||||
rm -Rf tmp
|
||||
mkdir tmp
|
||||
|
||||
wasm-pack build --mode normal --dev --out-name jirs --out-dir ./tmp --target web
|
||||
../target/debug/jirs-css -i ./js/styles.css -O ./tmp/styles.css
|
||||
|
||||
cat ./js/index.js \
|
||||
| sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" \
|
||||
| sed -e "s/process.env.JIRS_SERVER_PORT/'$JIRS_SERVER_PORT'/g" &> ./tmp/index.js
|
||||
cp ./js/template.html ./tmp/index.html
|
||||
|
||||
cp -r ./dev/* ./tmp
|
@ -1,10 +0,0 @@
|
||||
use jirs_data::*;
|
||||
|
||||
pub fn send_ws_msg(msg: WsMsg) {
|
||||
use crate::send_bin_code;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
let binary = bincode::serialize(&msg).unwrap();
|
||||
let data = JsValue::from_serde(&binary).unwrap();
|
||||
send_bin_code(data);
|
||||
}
|
67
jirs-client/src/changes.rs
Normal file
67
jirs-client/src/changes.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use crate::FieldId;
|
||||
use jirs_data::{IssueId, IssueStatusId};
|
||||
|
||||
use crate::shared::styled_editor::Mode as TabMode;
|
||||
use seed::prelude::WebSocketMessage;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum FieldChange {
|
||||
LinkCopied(FieldId, bool),
|
||||
TabChanged(FieldId, TabMode),
|
||||
ToggleCommentForm(FieldId, bool),
|
||||
EditComment(FieldId, i32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum BoardPageChange {
|
||||
// dragging
|
||||
IssueDragStarted(IssueId),
|
||||
IssueDragStopped(IssueId),
|
||||
DragLeave(IssueId),
|
||||
ExchangePosition(IssueId),
|
||||
IssueDragOverStatus(IssueStatusId),
|
||||
IssueDropZone(IssueStatusId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum UsersPageChange {
|
||||
ResetForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ProjectPageChange {
|
||||
ResetForm,
|
||||
SubmitForm,
|
||||
// dragging
|
||||
ColumnDragStarted(IssueStatusId),
|
||||
ColumnDragStopped(IssueStatusId),
|
||||
ColumnDragLeave(IssueStatusId),
|
||||
ColumnExchangePosition(IssueStatusId),
|
||||
ColumnDragOverStatus(IssueStatusId),
|
||||
ColumnDropZone(IssueStatusId),
|
||||
// edit issue status name
|
||||
EditIssueStatusName(Option<IssueStatusId>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ProfilePageChange {
|
||||
SubmitForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PageChanged {
|
||||
Users(UsersPageChange),
|
||||
ProjectSettings(ProjectPageChange),
|
||||
Profile(ProfilePageChange),
|
||||
Board(BoardPageChange),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WebSocketChanged {
|
||||
WsMsg(jirs_data::WsMsg),
|
||||
WebSocketMessage(WebSocketMessage),
|
||||
WebSocketMessageLoaded(Vec<u8>),
|
||||
WebSocketOpened,
|
||||
WebSocketClosed,
|
||||
SendPing,
|
||||
}
|
121
jirs-client/src/fields.rs
Normal file
121
jirs-client/src/fields.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use jirs_data::{
|
||||
CommentFieldId, InviteFieldId, IssueFieldId, ProjectFieldId, SignInFieldId, SignUpFieldId,
|
||||
UsersFieldId,
|
||||
};
|
||||
|
||||
pub type AvatarFilterActive = bool;
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum EditIssueModalSection {
|
||||
Issue(IssueFieldId),
|
||||
Comment(CommentFieldId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum FieldId {
|
||||
SignIn(SignInFieldId),
|
||||
SignUp(SignUpFieldId),
|
||||
Invite(InviteFieldId),
|
||||
Users(UsersFieldId),
|
||||
Profile(UsersFieldId),
|
||||
// issue
|
||||
AddIssueModal(IssueFieldId),
|
||||
EditIssueModal(EditIssueModalSection),
|
||||
// project boards
|
||||
TextFilterBoard,
|
||||
CopyButtonLabel,
|
||||
|
||||
ProjectSettings(ProjectFieldId),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FieldId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FieldId::EditIssueModal(sub) => match sub {
|
||||
EditIssueModalSection::Issue(IssueFieldId::Type) => {
|
||||
f.write_str("issueTypeEditModalTop")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Title) => {
|
||||
f.write_str("titleIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Description) => {
|
||||
f.write_str("descriptionIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::IssueStatusId) => {
|
||||
f.write_str("statusIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Assignees) => {
|
||||
f.write_str("assigneesIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Reporter) => {
|
||||
f.write_str("reporterIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Priority) => {
|
||||
f.write_str("priorityIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Estimate) => {
|
||||
f.write_str("estimateIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::TimeSpent) => {
|
||||
f.write_str("timeSpendIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::TimeRemaining) => {
|
||||
f.write_str("timeRemainingIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Comment(CommentFieldId::Body) => {
|
||||
f.write_str("editIssue-commentBody")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::ListPosition) => {
|
||||
f.write_str("editIssue-listPosition")
|
||||
}
|
||||
},
|
||||
FieldId::AddIssueModal(sub) => match sub {
|
||||
IssueFieldId::Type => f.write_str("issueTypeAddIssueModal"),
|
||||
IssueFieldId::Title => f.write_str("summaryAddIssueModal"),
|
||||
IssueFieldId::Description => f.write_str("descriptionAddIssueModal"),
|
||||
IssueFieldId::Reporter => f.write_str("reporterAddIssueModal"),
|
||||
IssueFieldId::Assignees => f.write_str("assigneesAddIssueModal"),
|
||||
IssueFieldId::Priority => f.write_str("issuePriorityAddIssueModal"),
|
||||
IssueFieldId::IssueStatusId => f.write_str("addIssueModal-status"),
|
||||
IssueFieldId::Estimate => f.write_str("addIssueModal-estimate"),
|
||||
IssueFieldId::TimeSpent => f.write_str("addIssueModal-timeSpend"),
|
||||
IssueFieldId::TimeRemaining => f.write_str("addIssueModal-timeRemaining"),
|
||||
IssueFieldId::ListPosition => f.write_str("addIssueModal-listPosition"),
|
||||
},
|
||||
FieldId::TextFilterBoard => f.write_str("textFilterBoard"),
|
||||
FieldId::CopyButtonLabel => f.write_str("copyButtonLabel"),
|
||||
FieldId::ProjectSettings(sub) => match sub {
|
||||
ProjectFieldId::Name => f.write_str("projectSettings-name"),
|
||||
ProjectFieldId::Url => f.write_str("projectSettings-url"),
|
||||
ProjectFieldId::Description => f.write_str("projectSettings-description"),
|
||||
ProjectFieldId::Category => f.write_str("projectSettings-category"),
|
||||
ProjectFieldId::TimeTracking => f.write_str("projectSettings-timeTracking"),
|
||||
ProjectFieldId::IssueStatusName => f.write_str("projectSettings-issueStatusName"),
|
||||
},
|
||||
FieldId::SignIn(sub) => match sub {
|
||||
SignInFieldId::Email => f.write_str("login-email"),
|
||||
SignInFieldId::Username => f.write_str("login-username"),
|
||||
SignInFieldId::Token => f.write_str("login-token"),
|
||||
},
|
||||
FieldId::SignUp(sub) => match sub {
|
||||
SignUpFieldId::Username => f.write_str("signUp-email"),
|
||||
SignUpFieldId::Email => f.write_str("signUp-username"),
|
||||
},
|
||||
FieldId::Invite(sub) => match sub {
|
||||
InviteFieldId::Token => f.write_str("invite-token"),
|
||||
},
|
||||
FieldId::Users(sub) => match sub {
|
||||
UsersFieldId::Username => f.write_str("users-username"),
|
||||
UsersFieldId::Email => f.write_str("users-email"),
|
||||
UsersFieldId::UserRole => f.write_str("users-userRole"),
|
||||
UsersFieldId::Avatar => f.write_str("users-avatar"),
|
||||
},
|
||||
FieldId::Profile(sub) => match sub {
|
||||
UsersFieldId::Username => f.write_str("profile-username"),
|
||||
UsersFieldId::Email => f.write_str("profile-email"),
|
||||
UsersFieldId::UserRole => f.write_str("profile-userRole"),
|
||||
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
use std::sync::RwLock;
|
||||
|
||||
use seed::{prelude::*, *};
|
||||
use web_sys::File;
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{ModalType, Model, Page};
|
||||
use crate::shared::styled_editor::Mode as TabMode;
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
use crate::shared::{go_to_board, go_to_login};
|
||||
use crate::ws::{open_socket, read_incoming, send_ws_msg};
|
||||
|
||||
mod api;
|
||||
mod changes;
|
||||
mod fields;
|
||||
mod invite;
|
||||
mod modal;
|
||||
mod model;
|
||||
@ -24,179 +23,13 @@ mod users;
|
||||
pub mod validations;
|
||||
mod ws;
|
||||
|
||||
pub type AvatarFilterActive = bool;
|
||||
pub use changes::*;
|
||||
pub use fields::*;
|
||||
|
||||
pub type AppType = App<Msg, Model, Node<Msg>>;
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum EditIssueModalSection {
|
||||
Issue(IssueFieldId),
|
||||
Comment(CommentFieldId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum FieldId {
|
||||
SignIn(SignInFieldId),
|
||||
SignUp(SignUpFieldId),
|
||||
Invite(InviteFieldId),
|
||||
Users(UsersFieldId),
|
||||
Profile(UsersFieldId),
|
||||
// issue
|
||||
AddIssueModal(IssueFieldId),
|
||||
EditIssueModal(EditIssueModalSection),
|
||||
// project boards
|
||||
TextFilterBoard,
|
||||
CopyButtonLabel,
|
||||
|
||||
ProjectSettings(ProjectFieldId),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FieldId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FieldId::EditIssueModal(sub) => match sub {
|
||||
EditIssueModalSection::Issue(IssueFieldId::Type) => {
|
||||
f.write_str("issueTypeEditModalTop")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Title) => {
|
||||
f.write_str("titleIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Description) => {
|
||||
f.write_str("descriptionIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::IssueStatusId) => {
|
||||
f.write_str("statusIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Assignees) => {
|
||||
f.write_str("assigneesIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Reporter) => {
|
||||
f.write_str("reporterIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Priority) => {
|
||||
f.write_str("priorityIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::Estimate) => {
|
||||
f.write_str("estimateIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::TimeSpent) => {
|
||||
f.write_str("timeSpendIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::TimeRemaining) => {
|
||||
f.write_str("timeRemainingIssueEditModal")
|
||||
}
|
||||
EditIssueModalSection::Comment(CommentFieldId::Body) => {
|
||||
f.write_str("editIssue-commentBody")
|
||||
}
|
||||
EditIssueModalSection::Issue(IssueFieldId::ListPosition) => {
|
||||
f.write_str("editIssue-listPosition")
|
||||
}
|
||||
},
|
||||
FieldId::AddIssueModal(sub) => match sub {
|
||||
IssueFieldId::Type => f.write_str("issueTypeAddIssueModal"),
|
||||
IssueFieldId::Title => f.write_str("summaryAddIssueModal"),
|
||||
IssueFieldId::Description => f.write_str("descriptionAddIssueModal"),
|
||||
IssueFieldId::Reporter => f.write_str("reporterAddIssueModal"),
|
||||
IssueFieldId::Assignees => f.write_str("assigneesAddIssueModal"),
|
||||
IssueFieldId::Priority => f.write_str("issuePriorityAddIssueModal"),
|
||||
IssueFieldId::IssueStatusId => f.write_str("addIssueModal-status"),
|
||||
IssueFieldId::Estimate => f.write_str("addIssueModal-estimate"),
|
||||
IssueFieldId::TimeSpent => f.write_str("addIssueModal-timeSpend"),
|
||||
IssueFieldId::TimeRemaining => f.write_str("addIssueModal-timeRemaining"),
|
||||
IssueFieldId::ListPosition => f.write_str("addIssueModal-listPosition"),
|
||||
},
|
||||
FieldId::TextFilterBoard => f.write_str("textFilterBoard"),
|
||||
FieldId::CopyButtonLabel => f.write_str("copyButtonLabel"),
|
||||
FieldId::ProjectSettings(sub) => match sub {
|
||||
ProjectFieldId::Name => f.write_str("projectSettings-name"),
|
||||
ProjectFieldId::Url => f.write_str("projectSettings-url"),
|
||||
ProjectFieldId::Description => f.write_str("projectSettings-description"),
|
||||
ProjectFieldId::Category => f.write_str("projectSettings-category"),
|
||||
ProjectFieldId::TimeTracking => f.write_str("projectSettings-timeTracking"),
|
||||
ProjectFieldId::IssueStatusName => f.write_str("projectSettings-issueStatusName"),
|
||||
},
|
||||
FieldId::SignIn(sub) => match sub {
|
||||
SignInFieldId::Email => f.write_str("login-email"),
|
||||
SignInFieldId::Username => f.write_str("login-username"),
|
||||
SignInFieldId::Token => f.write_str("login-token"),
|
||||
},
|
||||
FieldId::SignUp(sub) => match sub {
|
||||
SignUpFieldId::Username => f.write_str("signUp-email"),
|
||||
SignUpFieldId::Email => f.write_str("signUp-username"),
|
||||
},
|
||||
FieldId::Invite(sub) => match sub {
|
||||
InviteFieldId::Token => f.write_str("invite-token"),
|
||||
},
|
||||
FieldId::Users(sub) => match sub {
|
||||
UsersFieldId::Username => f.write_str("users-username"),
|
||||
UsersFieldId::Email => f.write_str("users-email"),
|
||||
UsersFieldId::UserRole => f.write_str("users-userRole"),
|
||||
UsersFieldId::Avatar => f.write_str("users-avatar"),
|
||||
},
|
||||
FieldId::Profile(sub) => match sub {
|
||||
UsersFieldId::Username => f.write_str("profile-username"),
|
||||
UsersFieldId::Email => f.write_str("profile-email"),
|
||||
UsersFieldId::UserRole => f.write_str("profile-userRole"),
|
||||
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum FieldChange {
|
||||
LinkCopied(FieldId, bool),
|
||||
TabChanged(FieldId, TabMode),
|
||||
ToggleCommentForm(FieldId, bool),
|
||||
EditComment(FieldId, i32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum BoardPageChange {
|
||||
// dragging
|
||||
IssueDragStarted(IssueId),
|
||||
IssueDragStopped(IssueId),
|
||||
DragLeave(IssueId),
|
||||
ExchangePosition(IssueId),
|
||||
IssueDragOverStatus(IssueStatusId),
|
||||
IssueDropZone(IssueStatusId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum UsersPageChange {
|
||||
ResetForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ProjectPageChange {
|
||||
ResetForm,
|
||||
SubmitForm,
|
||||
// dragging
|
||||
ColumnDragStarted(IssueStatusId),
|
||||
ColumnDragStopped(IssueStatusId),
|
||||
ColumnDragLeave(IssueStatusId),
|
||||
ColumnExchangePosition(IssueStatusId),
|
||||
ColumnDragOverStatus(IssueStatusId),
|
||||
ColumnDropZone(IssueStatusId),
|
||||
// edit issue status name
|
||||
EditIssueStatusName(Option<IssueStatusId>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ProfilePageChange {
|
||||
SubmitForm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PageChanged {
|
||||
Users(UsersPageChange),
|
||||
ProjectSettings(ProjectPageChange),
|
||||
Profile(ProfilePageChange),
|
||||
Board(BoardPageChange),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {
|
||||
NoOp,
|
||||
GlobalKeyDown {
|
||||
key: String,
|
||||
shift: bool,
|
||||
@ -248,35 +81,82 @@ pub enum Msg {
|
||||
DeleteComment(CommentId),
|
||||
|
||||
// profile
|
||||
AvatarUpdateFetched(seed::fetch::FetchObject<String>),
|
||||
AvatarUpdateFetched(String),
|
||||
|
||||
// modals
|
||||
ModalOpened(Box<ModalType>),
|
||||
ModalDropped,
|
||||
ModalChanged(FieldChange),
|
||||
|
||||
WsMsg(jirs_data::WsMsg),
|
||||
// WebSocket
|
||||
WebSocketChange(WebSocketChanged),
|
||||
}
|
||||
|
||||
fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::NoOp => return,
|
||||
_ => (),
|
||||
if model.ws.is_none() {
|
||||
open_socket(model, orders);
|
||||
}
|
||||
|
||||
let msg = match msg {
|
||||
Msg::WebSocketChange(change) => {
|
||||
match change {
|
||||
WebSocketChanged::WebSocketOpened => {
|
||||
authorize_or_redirect(model);
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref());
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::SendPing => {
|
||||
send_ws_msg(WsMsg::Ping, model.ws.as_ref());
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketMessage(incoming) => {
|
||||
orders.perform_cmd(read_incoming(incoming));
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WsMsg(ref ws_msg) => {
|
||||
ws::update(ws_msg, model, orders);
|
||||
}
|
||||
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
||||
if let Ok(m) = bincode::deserialize(v.clone().as_slice()) {
|
||||
match m {
|
||||
WsMsg::Ping | WsMsg::Pong => {
|
||||
orders.perform_cmd(cmds::timeout(1000, || {
|
||||
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
orders
|
||||
.skip()
|
||||
.send_msg(Msg::WebSocketChange(WebSocketChanged::WsMsg(m)));
|
||||
}
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
WebSocketChanged::WebSocketClosed => {
|
||||
open_socket(model, orders);
|
||||
}
|
||||
};
|
||||
Msg::WebSocketChange(change)
|
||||
}
|
||||
_ => msg,
|
||||
};
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
log!(msg);
|
||||
}
|
||||
|
||||
match &msg {
|
||||
Msg::AuthTokenStored => {
|
||||
seed::push_route(vec!["dashboard"]);
|
||||
go_to_board();
|
||||
orders.skip().send_msg(Msg::ChangePage(Page::Project));
|
||||
authorize_or_redirect();
|
||||
authorize_or_redirect(model);
|
||||
return;
|
||||
}
|
||||
Msg::AuthTokenErased => {
|
||||
seed::push_route(vec!["login"]);
|
||||
go_to_login();
|
||||
orders.skip().send_msg(Msg::ChangePage(Page::SignIn));
|
||||
authorize_or_redirect();
|
||||
authorize_or_redirect(model);
|
||||
return;
|
||||
}
|
||||
Msg::ChangePage(page) => {
|
||||
@ -287,7 +167,6 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
crate::ws::update(&msg, model, orders);
|
||||
crate::modal::update(&msg, model, orders);
|
||||
match model.page {
|
||||
Page::Project | Page::AddIssue | Page::EditIssue(..) => project::update(msg, model, orders),
|
||||
@ -317,116 +196,98 @@ fn view(model: &model::Model) -> Node<Msg> {
|
||||
}
|
||||
|
||||
fn routes(url: Url) -> Option<Msg> {
|
||||
if url.path.is_empty() {
|
||||
return Some(Msg::ChangePage(model::Page::Project));
|
||||
match resolve_page(url) {
|
||||
Some(page) => Some(Msg::ChangePage(page)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_page(url: Url) -> Option<Page> {
|
||||
if url.path().is_empty() {
|
||||
return Some(Page::Project);
|
||||
}
|
||||
|
||||
match url.path[0].as_ref() {
|
||||
"board" => Some(Msg::ChangePage(model::Page::Project)),
|
||||
"issues" => match url.path.get(1).as_ref().map(|s| s.parse::<i32>()) {
|
||||
Some(Ok(id)) => Some(Msg::ChangePage(model::Page::EditIssue(id))),
|
||||
_ => None,
|
||||
let page = match url.path()[0].as_ref() {
|
||||
"board" => Page::Project,
|
||||
"issues" => match url.path().get(1).as_ref().map(|s| s.parse::<i32>()) {
|
||||
Some(Ok(id)) => Page::EditIssue(id),
|
||||
_ => return None,
|
||||
},
|
||||
"profile" => Some(Msg::ChangePage(Page::Profile)),
|
||||
"add-issue" => Some(Msg::ChangePage(Page::AddIssue)),
|
||||
"project-settings" => Some(Msg::ChangePage(model::Page::ProjectSettings)),
|
||||
"login" => Some(Msg::ChangePage(model::Page::SignIn)),
|
||||
"register" => Some(Msg::ChangePage(model::Page::SignUp)),
|
||||
"invite" => Some(Msg::ChangePage(model::Page::Invite)),
|
||||
"users" => Some(Msg::ChangePage(model::Page::Users)),
|
||||
_ => Some(Msg::ChangePage(model::Page::Project)),
|
||||
}
|
||||
"profile" => Page::Profile,
|
||||
"add-issue" => Page::AddIssue,
|
||||
"project-settings" => Page::ProjectSettings,
|
||||
"login" => Page::SignIn,
|
||||
"register" => Page::SignUp,
|
||||
"invite" => Page::Invite,
|
||||
"users" => Page::Users,
|
||||
_ => Page::Project,
|
||||
};
|
||||
Some(page)
|
||||
}
|
||||
|
||||
pub static mut HOST_URL: String = String::new();
|
||||
pub static mut APP: Option<RwLock<App<Msg, Model, Node<Msg>>>> = None;
|
||||
pub static mut WS_URL: String = String::new();
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_host_url(url: String) {
|
||||
pub fn render(host_url: String, ws_url: String) {
|
||||
unsafe {
|
||||
HOST_URL = url;
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn handle_ws_message(value: &wasm_bindgen::JsValue) {
|
||||
let a = js_sys::Uint8Array::new(value);
|
||||
let mut v = Vec::new();
|
||||
for idx in 0..a.length() {
|
||||
v.push(a.get_index(idx));
|
||||
}
|
||||
if let Ok(msg) = bincode::deserialize(v.as_slice()) {
|
||||
ws::handle(msg);
|
||||
};
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn reconnected() {
|
||||
authorize_or_redirect();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
pub fn send_bin_code(data: wasm_bindgen::JsValue);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn render() {
|
||||
seed::set_interval(
|
||||
Box::new(|| {
|
||||
let binary = bincode::serialize(&jirs_data::WsMsg::Ping).unwrap();
|
||||
let data = JsValue::from_serde(&binary).unwrap();
|
||||
send_bin_code(data);
|
||||
}) as Box<dyn Fn()>,
|
||||
5000,
|
||||
);
|
||||
|
||||
if let Some(body) = seed::html_document().body() {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let body = body.dyn_ref::<web_sys::HtmlBodyElement>().unwrap().clone();
|
||||
let key_up_closure =
|
||||
wasm_bindgen::closure::Closure::wrap(Box::new(|event: web_sys::KeyboardEvent| {
|
||||
if let Some(Ok(app)) = unsafe { APP.as_mut().map(|app| app.write()) } {
|
||||
let msg = Msg::GlobalKeyDown {
|
||||
key: event.key(),
|
||||
shift: event.shift_key(),
|
||||
ctrl: event.ctrl_key(),
|
||||
alt: event.alt_key(),
|
||||
};
|
||||
app.update(msg);
|
||||
}
|
||||
})
|
||||
as Box<dyn Fn(web_sys::KeyboardEvent)>);
|
||||
body.add_event_listener_with_callback("keyup", key_up_closure.as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
key_up_closure.forget();
|
||||
HOST_URL = host_url;
|
||||
WS_URL = ws_url;
|
||||
}
|
||||
|
||||
let app = seed::App::builder(update, view)
|
||||
// if let Some(body) = seed::html_document().body() {
|
||||
// use wasm_bindgen::JsCast;
|
||||
// let body = body.dyn_ref::<web_sys::HtmlBodyElement>().unwrap().clone();
|
||||
// let key_up_closure =
|
||||
// wasm_bindgen::closure::Closure::wrap(Box::new(|event: web_sys::KeyboardEvent| {
|
||||
// if let Some(Ok(app)) = unsafe { APP.as_mut().map(|app| app.write()) } {
|
||||
// let msg = Msg::GlobalKeyDown {
|
||||
// key: event.key(),
|
||||
// shift: event.shift_key(),
|
||||
// ctrl: event.ctrl_key(),
|
||||
// alt: event.alt_key(),
|
||||
// };
|
||||
// app.update(msg);
|
||||
// }
|
||||
// })
|
||||
// as Box<dyn Fn(web_sys::KeyboardEvent)>);
|
||||
// body.add_event_listener_with_callback("keyup", key_up_closure.as_ref().unchecked_ref())
|
||||
// .unwrap();
|
||||
// key_up_closure.forget();
|
||||
// }
|
||||
|
||||
let _app = seed::App::builder(update, view)
|
||||
.routes(routes)
|
||||
.after_mount(after_mount)
|
||||
.build_and_start();
|
||||
}
|
||||
|
||||
authorize_or_redirect();
|
||||
|
||||
let cell_app = std::sync::RwLock::new(app);
|
||||
fn after_mount(url: Url, orders: &mut impl Orders<Msg>) -> AfterMount<Model> {
|
||||
let host_url = unsafe { HOST_URL.clone() };
|
||||
let ws_url = unsafe { WS_URL.clone() };
|
||||
let mut model = Model::new(host_url, ws_url);
|
||||
unsafe {
|
||||
APP = Some(cell_app);
|
||||
};
|
||||
HOST_URL = "".to_string();
|
||||
WS_URL = "".to_string();
|
||||
}
|
||||
model.page = resolve_page(url).unwrap_or_else(|| Page::Project);
|
||||
log!(model);
|
||||
open_socket(&mut model, orders);
|
||||
AfterMount::new(model).url_handling(UrlHandling::PassToRoutes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn authorize_or_redirect() {
|
||||
fn authorize_or_redirect(model: &Model) {
|
||||
match crate::shared::read_auth_token() {
|
||||
Ok(token) => {
|
||||
send_ws_msg(WsMsg::AuthorizeRequest(token));
|
||||
send_ws_msg(WsMsg::AuthorizeRequest(token), model.ws.as_ref());
|
||||
}
|
||||
Err(..) => {
|
||||
let pathname = seed::document().location().unwrap().pathname().unwrap();
|
||||
match pathname.as_str() {
|
||||
"/login" | "/register" | "/invite" => {}
|
||||
_ => {
|
||||
seed::push_route(vec!["login"]);
|
||||
go_to_login();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::IssueFieldId;
|
||||
use jirs_data::{IssueFieldId, WsMsg};
|
||||
use jirs_data::{IssuePriority, IssueType, ToVec};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{AddIssueModal, ModalType, Model};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
@ -14,7 +13,8 @@ use crate::shared::styled_select::StyledSelect;
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
use crate::shared::styled_textarea::StyledTextarea;
|
||||
use crate::shared::{ToChild, ToNode};
|
||||
use crate::{FieldId, Msg};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{FieldId, Msg, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||
let modal = model.modals.iter_mut().find(|modal| match modal {
|
||||
@ -50,9 +50,12 @@ pub fn update(msg: &Msg, model: &mut crate::model::Model, orders: &mut impl Orde
|
||||
user_ids: modal.user_ids.clone(),
|
||||
reporter_id: modal.reporter_id.unwrap_or_else(|| user_id),
|
||||
};
|
||||
send_ws_msg(jirs_data::WsMsg::IssueCreateRequest(payload));
|
||||
send_ws_msg(
|
||||
jirs_data::WsMsg::IssueCreateRequest(payload),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::WsMsg(jirs_data::WsMsg::IssueCreated(issue)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueCreated(issue))) => {
|
||||
model.issues.push(issue.clone());
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ use seed::prelude::*;
|
||||
|
||||
use jirs_data::{IssueStatusId, WsMsg};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{DeleteIssueStatusModal, ModalType, Model};
|
||||
use crate::shared::styled_confirm_modal::StyledConfirmModal;
|
||||
use crate::shared::ToNode;
|
||||
use crate::{model, Msg};
|
||||
use crate::{model, Msg, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let _modal: &mut Box<DeleteIssueStatusModal> =
|
||||
@ -20,9 +19,12 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
|
||||
match msg {
|
||||
Msg::DeleteIssueStatus(issue_status_id) => {
|
||||
send_ws_msg(WsMsg::IssueStatusDelete(*issue_status_id));
|
||||
crate::ws::send_ws_msg(
|
||||
WsMsg::IssueStatusDelete(*issue_status_id),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueStatusDelete(_)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueStatusDelete(_))) => {
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
}
|
||||
_ => (),
|
||||
|
@ -2,7 +2,6 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::modal::time_tracking::time_tracking_field;
|
||||
use crate::model::{CommentForm, EditIssueModal, ModalType, Model};
|
||||
use crate::shared::styled_avatar::StyledAvatar;
|
||||
@ -15,7 +14,8 @@ use crate::shared::styled_select::{StyledSelect, StyledSelectChange};
|
||||
use crate::shared::styled_textarea::StyledTextarea;
|
||||
use crate::shared::tracking_widget::tracking_link;
|
||||
use crate::shared::{ToChild, ToNode};
|
||||
use crate::{EditIssueModalSection, FieldChange, FieldId, Msg};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{EditIssueModalSection, FieldChange, FieldId, Msg, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
let modal: &mut EditIssueModal = match model.modals.get_mut(0) {
|
||||
@ -35,7 +35,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
modal.time_remaining_select.update(msg, orders);
|
||||
|
||||
match msg {
|
||||
Msg::WsMsg(WsMsg::IssueUpdated(issue)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
||||
modal.payload = issue.clone().into();
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
@ -43,44 +43,56 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
StyledSelectChange::Changed(value),
|
||||
) => {
|
||||
modal.payload.issue_type = (*value).into();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Type,
|
||||
PayloadVariant::IssueType(modal.payload.issue_type),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Type,
|
||||
PayloadVariant::IssueType(modal.payload.issue_type),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::IssueStatusId)),
|
||||
StyledSelectChange::Changed(value),
|
||||
) => {
|
||||
modal.payload.issue_status_id = *value as IssueStatusId;
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::IssueStatusId,
|
||||
PayloadVariant::I32(modal.payload.issue_status_id),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::IssueStatusId,
|
||||
PayloadVariant::I32(modal.payload.issue_status_id),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Reporter)),
|
||||
StyledSelectChange::Changed(value),
|
||||
) => {
|
||||
modal.payload.reporter_id = *value as i32;
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Reporter,
|
||||
PayloadVariant::I32(modal.payload.reporter_id),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Reporter,
|
||||
PayloadVariant::I32(modal.payload.reporter_id),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Assignees)),
|
||||
StyledSelectChange::Changed(value),
|
||||
) => {
|
||||
modal.payload.user_ids.push(*value as i32);
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Assignees,
|
||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Assignees,
|
||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Assignees)),
|
||||
@ -94,33 +106,42 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
modal.payload.user_ids.push(id);
|
||||
}
|
||||
}
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Assignees,
|
||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Assignees,
|
||||
PayloadVariant::VecI32(modal.payload.user_ids.clone()),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Priority)),
|
||||
StyledSelectChange::Changed(value),
|
||||
) => {
|
||||
modal.payload.priority = (*value).into();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Priority,
|
||||
PayloadVariant::IssuePriority(modal.payload.priority),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Priority,
|
||||
PayloadVariant::IssuePriority(modal.payload.priority),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StrInputChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Title)),
|
||||
value,
|
||||
) => {
|
||||
modal.payload.title = value.clone();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Title,
|
||||
PayloadVariant::String(modal.payload.title.clone()),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Title,
|
||||
PayloadVariant::String(modal.payload.title.clone()),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StrInputChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Description)),
|
||||
@ -128,18 +149,21 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
) => {
|
||||
modal.payload.description = Some(value.clone());
|
||||
modal.payload.description_text = Some(value.clone());
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Description,
|
||||
PayloadVariant::String(
|
||||
modal
|
||||
.payload
|
||||
.description
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Description,
|
||||
PayloadVariant::String(
|
||||
modal
|
||||
.payload
|
||||
.description
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
),
|
||||
));
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
// TimeSpent
|
||||
Msg::StrInputChanged(
|
||||
@ -147,22 +171,28 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
..,
|
||||
) => {
|
||||
modal.payload.time_spent = modal.time_spent.represent_f64_as_i32();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeSpent,
|
||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeSpent,
|
||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeSpent)),
|
||||
StyledSelectChange::Changed(..),
|
||||
) => {
|
||||
modal.payload.time_spent = modal.time_spent_select.values.get(0).map(|n| *n as i32);
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeSpent,
|
||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeSpent,
|
||||
PayloadVariant::OptionI32(modal.payload.time_spent),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
// Time Remaining
|
||||
Msg::StrInputChanged(
|
||||
@ -170,11 +200,14 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
..,
|
||||
) => {
|
||||
modal.payload.time_remaining = modal.time_remaining.represent_f64_as_i32();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeRemaining,
|
||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeRemaining,
|
||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::TimeRemaining)),
|
||||
@ -182,11 +215,14 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
) => {
|
||||
modal.payload.time_remaining =
|
||||
modal.time_remaining_select.values.get(0).map(|n| *n as i32);
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeRemaining,
|
||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::TimeRemaining,
|
||||
PayloadVariant::OptionI32(modal.payload.time_remaining),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
// Estimate
|
||||
Msg::StrInputChanged(
|
||||
@ -194,22 +230,28 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
..,
|
||||
) => {
|
||||
modal.payload.estimate = modal.estimate.represent_f64_as_i32();
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Estimate,
|
||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Estimate,
|
||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::StyledSelectChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Estimate)),
|
||||
StyledSelectChange::Changed(..),
|
||||
) => {
|
||||
modal.payload.estimate = modal.estimate_select.values.get(0).map(|n| *n as i32);
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Estimate,
|
||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
modal.id,
|
||||
IssueFieldId::Estimate,
|
||||
PayloadVariant::OptionI32(modal.payload.estimate),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::ModalChanged(FieldChange::TabChanged(
|
||||
FieldId::EditIssueModal(EditIssueModalSection::Issue(IssueFieldId::Description)),
|
||||
@ -248,7 +290,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
issue_id: modal.id,
|
||||
}),
|
||||
};
|
||||
send_ws_msg(msg);
|
||||
send_ws_msg(msg, model.ws.as_ref());
|
||||
orders
|
||||
.skip()
|
||||
.send_msg(Msg::ModalChanged(FieldChange::ToggleCommentForm(
|
||||
@ -272,7 +314,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
modal.comment_form.creating = true;
|
||||
}
|
||||
Msg::DeleteComment(comment_id) => {
|
||||
send_ws_msg(WsMsg::CommentDeleteRequest(*comment_id));
|
||||
send_ws_msg(WsMsg::CommentDeleteRequest(*comment_id), model.ws.as_ref());
|
||||
orders.skip().send_msg(Msg::ModalDropped);
|
||||
}
|
||||
|
||||
@ -314,15 +356,13 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
let issue_id = *id;
|
||||
|
||||
let click_handler = mouse_ev(Ev::Click, move |_| {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
let link = format!("http://localhost:7000/issues/{id}", id = issue_id);
|
||||
let el = match seed::html_document().create_element("textarea") {
|
||||
Ok(el) => el
|
||||
.dyn_ref::<web_sys::HtmlTextAreaElement>()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
_ => return Msg::NoOp,
|
||||
_ => return None as Option<Msg>,
|
||||
};
|
||||
seed::body().append_child(&el).unwrap();
|
||||
el.set_text_content(Some(link.as_str()));
|
||||
@ -330,7 +370,10 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
el.set_selection_range(0, 9999).unwrap();
|
||||
seed::html_document().exec_command("copy").unwrap();
|
||||
seed::body().remove_child(&el).unwrap();
|
||||
Msg::ModalChanged(FieldChange::LinkCopied(FieldId::CopyButtonLabel, true))
|
||||
Some(Msg::ModalChanged(FieldChange::LinkCopied(
|
||||
FieldId::CopyButtonLabel,
|
||||
true,
|
||||
)))
|
||||
});
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::ModalDropped);
|
||||
let delete_confirmation_handler = mouse_ev(Ev::Click, move |_| {
|
||||
@ -579,7 +622,7 @@ fn comment(model: &Model, modal: &EditIssueModal, comment: &Comment) -> Option<N
|
||||
div![
|
||||
class!["content"],
|
||||
div![class!["userName"], user.name.as_str()],
|
||||
p![class!["body"], comment.body],
|
||||
p![class!["body"], comment.body.as_str()],
|
||||
buttons,
|
||||
]
|
||||
};
|
||||
|
@ -2,12 +2,12 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::{TimeTracking, WsMsg};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{AddIssueModal, EditIssueModal, ModalType, Model, Page};
|
||||
use crate::shared::styled_confirm_modal::StyledConfirmModal;
|
||||
use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
||||
use crate::shared::{find_issue, ToNode};
|
||||
use crate::{model, FieldChange, FieldId, Msg};
|
||||
use crate::shared::{find_issue, go_to_board, ToNode};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{model, FieldChange, FieldId, Msg, WebSocketChanged};
|
||||
|
||||
mod add_issue;
|
||||
mod confirm_delete_issue;
|
||||
@ -19,7 +19,7 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
||||
match msg {
|
||||
Msg::ModalDropped => match model.modals.pop() {
|
||||
Some(ModalType::EditIssue(..)) | Some(ModalType::AddIssue(..)) => {
|
||||
seed::push_route(vec!["board"]);
|
||||
go_to_board();
|
||||
orders.send_msg(Msg::ChangePage(Page::Project));
|
||||
}
|
||||
_ => (),
|
||||
@ -37,12 +37,14 @@ pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>
|
||||
model.modals.push(modal_type.as_ref().clone());
|
||||
}
|
||||
|
||||
Msg::WsMsg(jirs_data::WsMsg::ProjectIssuesLoaded(_issues)) => match model.page {
|
||||
Page::EditIssue(issue_id) if model.modals.is_empty() => {
|
||||
push_edit_modal(issue_id, model)
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::ProjectIssuesLoaded(_issues))) => {
|
||||
match model.page {
|
||||
Page::EditIssue(issue_id) if model.modals.is_empty() => {
|
||||
push_edit_modal(issue_id, model)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
|
||||
Msg::ChangePage(Page::EditIssue(issue_id)) => {
|
||||
push_edit_modal(*issue_id, model);
|
||||
@ -116,6 +118,6 @@ fn push_edit_modal(issue_id: i32, model: &mut Model) {
|
||||
Box::new(EditIssueModal::new(issue, time_tracking_type)),
|
||||
)
|
||||
};
|
||||
send_ws_msg(WsMsg::IssueCommentsRequest(issue_id));
|
||||
send_ws_msg(WsMsg::IssueCommentsRequest(issue_id), model.ws.as_ref());
|
||||
model.modals.push(modal);
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ use crate::shared::styled_editor::Mode;
|
||||
use crate::shared::styled_image_input::StyledImageInputState;
|
||||
use crate::shared::styled_input::StyledInputState;
|
||||
use crate::shared::styled_select::StyledSelectState;
|
||||
use crate::{EditIssueModalSection, FieldId, ProjectFieldId, HOST_URL};
|
||||
use crate::{EditIssueModalSection, FieldId, ProjectFieldId /*HOST_URL*/};
|
||||
use seed::browser::web_socket::WebSocket;
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
pub enum ModalType {
|
||||
@ -269,6 +270,7 @@ pub struct ProjectSettingsPage {
|
||||
pub time_tracking: StyledCheckboxState,
|
||||
pub column_drag: DragState,
|
||||
pub edit_column_id: Option<IssueStatusId>,
|
||||
pub creating_issue_status: bool,
|
||||
pub name: StyledInputState,
|
||||
}
|
||||
|
||||
@ -304,6 +306,7 @@ impl ProjectSettingsPage {
|
||||
),
|
||||
column_drag: Default::default(),
|
||||
edit_column_id: None,
|
||||
creating_issue_status: false,
|
||||
name: StyledInputState::new(
|
||||
FieldId::ProjectSettings(ProjectFieldId::IssueStatusName),
|
||||
"",
|
||||
@ -424,7 +427,9 @@ pub enum PageContent {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Model {
|
||||
pub ws: Option<WebSocket>,
|
||||
pub host_url: String,
|
||||
pub ws_url: String,
|
||||
pub access_token: Option<Uuid>,
|
||||
pub about_tooltip_visible: bool,
|
||||
|
||||
@ -451,10 +456,10 @@ pub struct Model {
|
||||
pub issue_statuses: Vec<IssueStatus>,
|
||||
}
|
||||
|
||||
impl Default for Model {
|
||||
fn default() -> Self {
|
||||
let host_url = unsafe { HOST_URL.clone() };
|
||||
impl Model {
|
||||
pub fn new(host_url: String, ws_url: String) -> Self {
|
||||
Self {
|
||||
ws: None,
|
||||
access_token: None,
|
||||
user: None,
|
||||
issue_form: None,
|
||||
@ -465,6 +470,7 @@ impl Default for Model {
|
||||
comments_by_project_id: Default::default(),
|
||||
page: Page::Project,
|
||||
host_url,
|
||||
ws_url,
|
||||
page_content: PageContent::Project(Box::new(ProjectPage::default())),
|
||||
modals: vec![],
|
||||
project: None,
|
||||
|
@ -3,7 +3,6 @@ use web_sys::FormData;
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{Model, Page, PageContent, ProfilePage};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
@ -11,7 +10,8 @@ use crate::shared::styled_form::StyledForm;
|
||||
use crate::shared::styled_image_input::StyledImageInput;
|
||||
use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::{inner_layout, ToNode};
|
||||
use crate::{FieldId, Msg, PageChanged, ProfilePageChange, HOST_URL};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{FieldId, Msg, PageChanged, ProfilePageChange, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||
let user = match model.user {
|
||||
@ -20,8 +20,9 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
};
|
||||
|
||||
match msg {
|
||||
Msg::WsMsg(WsMsg::AuthorizeLoaded(..)) | Msg::ChangePage(Page::Profile) => {
|
||||
send_ws_msg(WsMsg::ProjectRequest);
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||
| Msg::ChangePage(Page::Profile) => {
|
||||
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref());
|
||||
model.page_content = PageContent::Profile(Box::new(ProfilePage::new(user)));
|
||||
}
|
||||
_ => (),
|
||||
@ -50,10 +51,13 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
fd.set_with_str("token", format!("{}", token).as_str())
|
||||
.unwrap();
|
||||
fd.set_with_blob("avatar", file).unwrap();
|
||||
orders.perform_cmd(update_avatar(fd));
|
||||
orders.perform_cmd(update_avatar(fd, model.host_url.clone()));
|
||||
orders.skip();
|
||||
}
|
||||
Msg::WsMsg(WsMsg::AvatarUrlChanged(user_id, avatar_url)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AvatarUrlChanged(
|
||||
user_id,
|
||||
avatar_url,
|
||||
))) => {
|
||||
if let Some(me) = model.user.as_mut() {
|
||||
if me.id == user_id {
|
||||
profile_page.avatar.url = Some(avatar_url.clone());
|
||||
@ -61,10 +65,13 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
}
|
||||
}
|
||||
Msg::PageChanged(PageChanged::Profile(ProfilePageChange::SubmitForm)) => {
|
||||
send_ws_msg(WsMsg::ProfileUpdate(
|
||||
profile_page.email.value.clone(),
|
||||
profile_page.name.value.clone(),
|
||||
))
|
||||
send_ws_msg(
|
||||
WsMsg::ProfileUpdate(
|
||||
profile_page.email.value.clone(),
|
||||
profile_page.name.value.clone(),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -132,12 +139,17 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
inner_layout(model, "profile", vec![content], empty![])
|
||||
}
|
||||
|
||||
async fn update_avatar(data: FormData) -> Result<Msg, Msg> {
|
||||
let host_url = unsafe { HOST_URL.clone() };
|
||||
async fn update_avatar(data: FormData, host_url: String) -> Option<Msg> {
|
||||
let path = format!("{}/avatar/", host_url);
|
||||
Request::new(path)
|
||||
let result = Request::new(path)
|
||||
.method(Method::Post)
|
||||
.body(data.into())
|
||||
.fetch_string(Msg::AvatarUpdateFetched)
|
||||
.await
|
||||
.fetch()
|
||||
.await;
|
||||
let response = match result {
|
||||
Ok(r) => r,
|
||||
Err(_) => return None,
|
||||
};
|
||||
let text = response.text().await.ok()?;
|
||||
Some(Msg::AvatarUpdateFetched(text))
|
||||
}
|
||||
|
@ -3,15 +3,15 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{ModalType, Model, Page, PageContent, ProjectPage};
|
||||
use crate::shared::styled_avatar::StyledAvatar;
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
use crate::shared::{drag_ev, inner_layout, ToNode};
|
||||
use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged};
|
||||
use crate::shared::{inner_layout, ToNode};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{BoardPageChange, EditIssueModalSection, FieldId, Msg, PageChanged, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
|
||||
if model.user.is_none() {
|
||||
@ -33,16 +33,16 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
};
|
||||
|
||||
match msg {
|
||||
Msg::WsMsg(WsMsg::AuthorizeLoaded(..))
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)))
|
||||
| Msg::ChangePage(Page::Project)
|
||||
| Msg::ChangePage(Page::AddIssue)
|
||||
| Msg::ChangePage(Page::EditIssue(..)) => {
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectRequest);
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectIssuesRequest);
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectUsersRequest);
|
||||
send_ws_msg(jirs_data::WsMsg::IssueStatusesRequest);
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectRequest, model.ws.as_ref());
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectIssuesRequest, model.ws.as_ref());
|
||||
send_ws_msg(jirs_data::WsMsg::ProjectUsersRequest, model.ws.as_ref());
|
||||
send_ws_msg(jirs_data::WsMsg::IssueStatusesRequest, model.ws.as_ref());
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueUpdated(issue)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueUpdated(issue))) => {
|
||||
let mut old: Vec<Issue> = vec![];
|
||||
std::mem::swap(&mut old, &mut model.issues);
|
||||
for is in old {
|
||||
@ -53,7 +53,7 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
}
|
||||
}
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueDeleted(id)) => {
|
||||
Msg::WebSocketChange(WebSocketChanged::WsMsg(WsMsg::IssueDeleted(id))) => {
|
||||
let mut old: Vec<Issue> = vec![];
|
||||
std::mem::swap(&mut old, &mut model.issues);
|
||||
for is in old {
|
||||
@ -123,7 +123,10 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
||||
project_page.issue_drag.clear_last();
|
||||
}
|
||||
Msg::DeleteIssue(issue_id) => {
|
||||
send_ws_msg(jirs_data::WsMsg::IssueDeleteRequest(issue_id));
|
||||
send_ws_msg(
|
||||
jirs_data::WsMsg::IssueDeleteRequest(issue_id),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -306,16 +309,16 @@ fn project_issue_list(model: &Model, status: &jirs_data::IssueStatus) -> Node<Ms
|
||||
let send_status = status.id;
|
||||
let drop_handler = drag_ev(Ev::Drop, move |ev| {
|
||||
ev.prevent_default();
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDropZone(
|
||||
send_status,
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::IssueDropZone(send_status),
|
||||
)))
|
||||
});
|
||||
|
||||
let send_status = status.id;
|
||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||
ev.prevent_default();
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragOverStatus(
|
||||
send_status,
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::IssueDragOverStatus(send_status),
|
||||
)))
|
||||
});
|
||||
|
||||
@ -395,25 +398,27 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
||||
|
||||
let issue_id = issue.id;
|
||||
let drag_started = drag_ev(Ev::DragStart, move |_| {
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStarted(
|
||||
issue_id,
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::IssueDragStarted(issue_id),
|
||||
)))
|
||||
});
|
||||
let drag_stopped = drag_ev(Ev::DragEnd, move |_| {
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::IssueDragStopped(
|
||||
issue_id,
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::IssueDragStopped(issue_id),
|
||||
)))
|
||||
});
|
||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::ExchangePosition(
|
||||
issue_id,
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::ExchangePosition(issue_id),
|
||||
)))
|
||||
});
|
||||
let issue_id = issue.id;
|
||||
let drag_out = drag_ev(Ev::DragLeave, move |_| {
|
||||
Msg::PageChanged(PageChanged::Board(BoardPageChange::DragLeave(issue_id)))
|
||||
Some(Msg::PageChanged(PageChanged::Board(
|
||||
BoardPageChange::DragLeave(issue_id),
|
||||
)))
|
||||
});
|
||||
|
||||
let class_list = vec!["issue"];
|
||||
@ -428,7 +433,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
||||
drag_stopped,
|
||||
drag_over_handler,
|
||||
drag_out,
|
||||
p![attrs![At::Class => "title"], issue.title,],
|
||||
p![attrs![At::Class => "title"], issue.title.as_str()],
|
||||
div![
|
||||
attrs![At::Class => "bottom"],
|
||||
div![
|
||||
|
@ -5,7 +5,6 @@ use jirs_data::{
|
||||
IssueStatus, IssueStatusId, ProjectCategory, TimeTracking, ToVec, UpdateProjectPayload, WsMsg,
|
||||
};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{
|
||||
DeleteIssueStatusModal, ModalType, Model, Page, PageContent, ProjectSettingsPage,
|
||||
};
|
||||
@ -18,9 +17,12 @@ use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_select::{StyledSelect, StyledSelectChange};
|
||||
use crate::shared::styled_textarea::StyledTextarea;
|
||||
use crate::shared::{drag_ev, inner_layout, ToChild, ToNode};
|
||||
use crate::shared::{inner_layout, ToChild, ToNode};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::FieldChange::TabChanged;
|
||||
use crate::{model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange};
|
||||
use crate::{
|
||||
model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange, WebSocketChanged,
|
||||
};
|
||||
|
||||
static TIME_TRACKING_FIBONACCI: &'static str = "Tracking employees’ time carries the risk of having them feel like they are being spied on. This is one of the most common fears that employees have when a time tracking system is implemented. No one likes to feel like they’re always being watched.";
|
||||
static TIME_TRACKING_HOURLY: &'static str = "Employees may feel intimidated by demands to track their time. Or they could feel that they’re constantly being watched and evaluated. And for overly ambitious managers, employee time tracking may open the doors to excessive micromanaging.";
|
||||
@ -31,22 +33,25 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
}
|
||||
|
||||
match msg {
|
||||
Msg::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
||||
send_ws_msg(WsMsg::ProjectRequest);
|
||||
send_ws_msg(WsMsg::IssueStatusesRequest);
|
||||
send_ws_msg(WsMsg::ProjectIssuesRequest);
|
||||
}
|
||||
Msg::WebSocketChange(ref change) => match change {
|
||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(..)) => {
|
||||
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref());
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::ProjectLoaded(..)) => {
|
||||
build_page_content(model);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Msg::ChangePage(Page::ProjectSettings) => {
|
||||
build_page_content(model);
|
||||
if model.user.is_some() {
|
||||
send_ws_msg(WsMsg::ProjectRequest);
|
||||
send_ws_msg(WsMsg::IssueStatusesRequest);
|
||||
send_ws_msg(WsMsg::ProjectIssuesRequest);
|
||||
send_ws_msg(WsMsg::ProjectRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::IssueStatusesRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::ProjectIssuesRequest, model.ws.as_ref());
|
||||
}
|
||||
}
|
||||
Msg::WsMsg(WsMsg::ProjectLoaded(..)) => {
|
||||
build_page_content(model);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -86,14 +91,17 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
page.description_mode = mode;
|
||||
}
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::SubmitForm)) => {
|
||||
send_ws_msg(WsMsg::ProjectUpdateRequest(UpdateProjectPayload {
|
||||
id: page.payload.id,
|
||||
name: page.payload.name.clone(),
|
||||
url: page.payload.url.clone(),
|
||||
description: page.payload.description.clone(),
|
||||
category: page.payload.category.clone(),
|
||||
time_tracking: Some(page.time_tracking.value.into()),
|
||||
}));
|
||||
send_ws_msg(
|
||||
WsMsg::ProjectUpdateRequest(UpdateProjectPayload {
|
||||
id: page.payload.id,
|
||||
name: page.payload.name.clone(),
|
||||
url: page.payload.url.clone(),
|
||||
description: page.payload.description.clone(),
|
||||
category: page.payload.category.clone(),
|
||||
time_tracking: Some(page.time_tracking.value.into()),
|
||||
}),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted(
|
||||
issue_status_id,
|
||||
@ -128,7 +136,10 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
.find(|is| Some(is.id) == old_id)
|
||||
.map(|is| (is.id, is.position))
|
||||
{
|
||||
send_ws_msg(WsMsg::IssueStatusUpdate(id, name.to_string(), pos))
|
||||
send_ws_msg(
|
||||
WsMsg::IssueStatusUpdate(id, name.to_string(), pos),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
page.name.value = model
|
||||
@ -259,91 +270,15 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
let columns: Vec<Node<Msg>> = model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.map(|is| {
|
||||
let id = is.id;
|
||||
let drag_started = drag_ev(Ev::DragStart, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStarted(
|
||||
id,
|
||||
)))
|
||||
});
|
||||
let drag_stopped = drag_ev(Ev::DragEnd, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragStopped(
|
||||
id,
|
||||
)))
|
||||
});
|
||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnExchangePosition(
|
||||
id,
|
||||
)))
|
||||
});
|
||||
let drag_out = drag_ev(Ev::DragLeave, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::ColumnDragLeave(id)))
|
||||
});
|
||||
|
||||
if page.edit_column_id == Some(id) {
|
||||
let blur = ev("focusout", |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(None)))
|
||||
});
|
||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
.state(&page.name)
|
||||
.primary()
|
||||
.auto_focus()
|
||||
.on_input_ev(blur)
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
div![
|
||||
class!["columnPreview"],
|
||||
div![class!["columnName"], input]
|
||||
]
|
||||
} else {
|
||||
let on_edit = mouse_ev(Ev::Click, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(ProjectPageChange::EditIssueStatusName(Some(id))))
|
||||
});
|
||||
let issue_count_in_column = per_column_issue_count.get(&id).cloned().unwrap_or_default();
|
||||
let delete_row = if issue_count_in_column == 0 {
|
||||
let on_delete = mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Msg::ModalOpened(Box::new(ModalType::DeleteIssueStatusModal(Box::new(DeleteIssueStatusModal::new(id)))))
|
||||
});
|
||||
let delete = StyledButton::build()
|
||||
.primary()
|
||||
.add_class("removeColumn")
|
||||
.icon(Icon::Trash)
|
||||
.on_click(on_delete)
|
||||
.build()
|
||||
.into_node();
|
||||
div![class!["removeColumn"], delete]
|
||||
} else {
|
||||
div![class!["issueCount"], format!("Issues in column: {}", issue_count_in_column)]
|
||||
};
|
||||
|
||||
div![
|
||||
class!["columnPreview"],
|
||||
attrs![At::Style => column_style.as_str(), At::Draggable => "true", At::DropZone => "true"],
|
||||
div![class!["columnName"], span![is.name], on_edit, delete_row],
|
||||
drag_started,
|
||||
drag_stopped,
|
||||
drag_over_handler,
|
||||
drag_out,
|
||||
]
|
||||
}
|
||||
})
|
||||
.map(|is| column_preview(is, page, &per_column_issue_count, column_style.as_str()))
|
||||
.collect();
|
||||
let add_column = StyledIcon::build(Icon::Plus).build().into_node();
|
||||
|
||||
let columns_section = section![
|
||||
class!["columnsSection"],
|
||||
div![
|
||||
class!["columns"],
|
||||
columns,
|
||||
div![
|
||||
class!["columnPreview"],
|
||||
attrs![At::Style => column_style.as_str()],
|
||||
div![class!["columnName addColumn"], add_column]
|
||||
]
|
||||
add_column(page, column_style.as_str())
|
||||
]
|
||||
];
|
||||
let columns_field = StyledField::build()
|
||||
@ -451,7 +386,10 @@ fn sync(model: &mut Model) {
|
||||
Some(is) => is,
|
||||
_ => continue,
|
||||
};
|
||||
send_ws_msg(WsMsg::IssueStatusUpdate(*id, name.clone(), *position))
|
||||
send_ws_msg(
|
||||
WsMsg::IssueStatusUpdate(*id, name.clone(), *position),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -462,3 +400,137 @@ fn build_page_content(model: &mut Model) {
|
||||
};
|
||||
model.page_content = PageContent::ProjectSettings(Box::new(ProjectSettingsPage::new(project)));
|
||||
}
|
||||
|
||||
fn add_column(page: &ProjectSettingsPage, column_style: &str) -> Node<Msg> {
|
||||
let on_click = mouse_ev(Ev::Click, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::EditIssueStatusName(Some(0)),
|
||||
))
|
||||
});
|
||||
|
||||
if page.edit_column_id == Some(0) {
|
||||
let blur = ev("focusout", |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::EditIssueStatusName(None),
|
||||
))
|
||||
});
|
||||
|
||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
.state(&page.name)
|
||||
.primary()
|
||||
.auto_focus()
|
||||
.on_input_ev(blur)
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
div![class!["columnPreview"], div![class!["columnName"], input]]
|
||||
} else {
|
||||
let add_column = StyledIcon::build(Icon::Plus).build().into_node();
|
||||
div![
|
||||
class!["columnPreview"],
|
||||
attrs![At::Style => column_style],
|
||||
div![class!["columnName addColumn"], add_column],
|
||||
on_click,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn column_preview(
|
||||
is: &IssueStatus,
|
||||
page: &ProjectSettingsPage,
|
||||
per_column_issue_count: &HashMap<i32, i32>,
|
||||
column_style: &str,
|
||||
) -> Node<Msg> {
|
||||
if page.edit_column_id == Some(is.id) {
|
||||
let blur = ev("focusout", |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::EditIssueStatusName(None),
|
||||
))
|
||||
});
|
||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
.state(&page.name)
|
||||
.primary()
|
||||
.auto_focus()
|
||||
.on_input_ev(blur)
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
div![class!["columnPreview"], div![class!["columnName"], input]]
|
||||
} else {
|
||||
show_column_preview(is, per_column_issue_count, column_style)
|
||||
}
|
||||
}
|
||||
|
||||
fn show_column_preview(
|
||||
is: &IssueStatus,
|
||||
per_column_issue_count: &HashMap<i32, i32>,
|
||||
column_style: &str,
|
||||
) -> Node<Msg> {
|
||||
let id = is.id;
|
||||
let drag_started = drag_ev(Ev::DragStart, move |_| {
|
||||
Some(Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::ColumnDragStarted(id),
|
||||
)))
|
||||
});
|
||||
let drag_stopped = drag_ev(Ev::DragEnd, move |_| {
|
||||
Some(Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::ColumnDragStopped(id),
|
||||
)))
|
||||
});
|
||||
let drag_over_handler = drag_ev(Ev::DragOver, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Some(Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::ColumnExchangePosition(id),
|
||||
)))
|
||||
});
|
||||
let drag_out = drag_ev(Ev::DragLeave, move |_| {
|
||||
Some(Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::ColumnDragLeave(id),
|
||||
)))
|
||||
});
|
||||
|
||||
let on_edit = mouse_ev(Ev::Click, move |_| {
|
||||
Msg::PageChanged(PageChanged::ProjectSettings(
|
||||
ProjectPageChange::EditIssueStatusName(Some(id)),
|
||||
))
|
||||
});
|
||||
let issue_count_in_column = per_column_issue_count.get(&id).cloned().unwrap_or_default();
|
||||
let delete_row = if issue_count_in_column == 0 {
|
||||
let on_delete = mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
ev.stop_propagation();
|
||||
Msg::ModalOpened(Box::new(ModalType::DeleteIssueStatusModal(Box::new(
|
||||
DeleteIssueStatusModal::new(id),
|
||||
))))
|
||||
});
|
||||
let delete = StyledButton::build()
|
||||
.primary()
|
||||
.add_class("removeColumn")
|
||||
.icon(Icon::Trash)
|
||||
.on_click(on_delete)
|
||||
.build()
|
||||
.into_node();
|
||||
div![class!["removeColumn"], delete]
|
||||
} else {
|
||||
div![
|
||||
class!["issueCount"],
|
||||
format!("Issues in column: {}", issue_count_in_column)
|
||||
]
|
||||
};
|
||||
|
||||
div![
|
||||
class!["columnPreview"],
|
||||
attrs![At::Style => column_style, At::Draggable => "true", At::DropZone => "true"],
|
||||
div![
|
||||
class!["columnName"],
|
||||
span![is.name.as_str()],
|
||||
on_edit,
|
||||
delete_row
|
||||
],
|
||||
drag_started,
|
||||
drag_stopped,
|
||||
drag_over_handler,
|
||||
drag_out,
|
||||
]
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub fn render(model: &Model) -> Node<Msg> {
|
||||
project_icon,
|
||||
div![
|
||||
class!["projectTexts"],
|
||||
div![class!["projectName"], project.name],
|
||||
div![class!["projectName"], project.name.as_str()],
|
||||
div![class!["projectCategory"], project.category.to_string()]
|
||||
],
|
||||
],
|
||||
|
@ -1,5 +1,5 @@
|
||||
use seed::{prelude::*, *};
|
||||
use wasm_bindgen::JsCast;
|
||||
use std::str::FromStr;
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
@ -33,6 +33,18 @@ pub trait ToChild {
|
||||
fn to_child(&self) -> Self::Builder;
|
||||
}
|
||||
|
||||
pub fn go_to_board() {
|
||||
go_to("/board");
|
||||
}
|
||||
|
||||
pub fn go_to_login() {
|
||||
go_to("/login");
|
||||
}
|
||||
|
||||
pub fn go_to(url: &str) {
|
||||
seed::push_route(Url::from_str(url).unwrap());
|
||||
}
|
||||
|
||||
pub fn find_issue(model: &Model, issue_id: IssueId) -> Option<&Issue> {
|
||||
model.issues.iter().find(|issue| issue.id == issue_id)
|
||||
}
|
||||
@ -107,13 +119,3 @@ pub fn read_auth_token() -> Result<uuid::Uuid, String> {
|
||||
.parse()
|
||||
.map_err(|_| "Bad token format".to_string())
|
||||
}
|
||||
|
||||
pub fn drag_ev<Ms>(
|
||||
trigger: impl Into<Ev>,
|
||||
handler: impl FnOnce(web_sys::DragEvent) -> Ms + 'static + Clone,
|
||||
) -> EventHandler<Ms> {
|
||||
let closure_handler = move |event: web_sys::Event| {
|
||||
(handler.clone())(event.dyn_ref::<web_sys::DragEvent>().unwrap().clone())
|
||||
};
|
||||
EventHandler::new(trigger, closure_handler)
|
||||
}
|
||||
|
@ -102,11 +102,10 @@ impl ToNode for ChildBuilder {
|
||||
} = self;
|
||||
|
||||
let id = field_id.as_ref().map(|f| f.to_string()).unwrap_or_default();
|
||||
let handler = if let Some(field_id) = field_id {
|
||||
mouse_ev(Ev::Click, move |_| Msg::U32InputChanged(field_id, value))
|
||||
} else {
|
||||
ev(Ev::FullScreenError, move |_| Msg::NoOp)
|
||||
};
|
||||
let field_id_clone = field_id.clone();
|
||||
let handler: EventHandler<Msg> = mouse_ev(Ev::Click, move |_| {
|
||||
field_id_clone.map(|field_id| Msg::U32InputChanged(field_id, value))
|
||||
});
|
||||
|
||||
class_list.push("styledCheckboxChild".to_string());
|
||||
class_list.push(if selected { "selected" } else { "" }.to_string());
|
||||
|
@ -222,11 +222,11 @@ pub fn render(values: StyledInput) -> Node<Msg> {
|
||||
}));
|
||||
input_handlers.push(ev(Ev::KeyUp, move |event| {
|
||||
event.stop_propagation();
|
||||
Msg::NoOp
|
||||
None as Option<Msg>
|
||||
}));
|
||||
input_handlers.push(ev(Ev::Click, move |event| {
|
||||
event.stop_propagation();
|
||||
Msg::NoOp
|
||||
None as Option<Msg>
|
||||
}));
|
||||
|
||||
div![
|
||||
|
@ -122,7 +122,7 @@ pub fn render(values: StyledModal) -> Node<Msg> {
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::ModalDropped);
|
||||
let body_handler = mouse_ev(Ev::Click, |ev| {
|
||||
ev.stop_propagation();
|
||||
Msg::NoOp
|
||||
None as Option<Msg>
|
||||
});
|
||||
|
||||
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
||||
|
@ -343,7 +343,7 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
||||
attrs![At::Class => select_class.join(" "), At::Style => dropdown_style.as_str()],
|
||||
keyboard_ev(Ev::KeyUp, |ev| {
|
||||
ev.stop_propagation();
|
||||
Msg::NoOp
|
||||
None as Option<Msg>
|
||||
}),
|
||||
div![
|
||||
attrs![At::Class => format!("valueContainer {}", variant)],
|
||||
|
@ -161,7 +161,7 @@ pub fn render(values: StyledTextarea) -> Node<Msg> {
|
||||
let resize_handler = ev(Ev::KeyUp, move |event| {
|
||||
event.stop_propagation();
|
||||
if handler_disable_auto_resize {
|
||||
return Msg::NoOp;
|
||||
return None as Option<Msg>;
|
||||
}
|
||||
|
||||
let target = event.target().unwrap();
|
||||
@ -172,7 +172,7 @@ pub fn render(values: StyledTextarea) -> Node<Msg> {
|
||||
textarea
|
||||
.style()
|
||||
.set_css_text(format!("height: {min_height}px", min_height = min_height).as_str());
|
||||
Msg::NoOp
|
||||
None as Option<Msg>
|
||||
});
|
||||
|
||||
let handler_disable_auto_resize = disable_auto_resize;
|
||||
|
@ -5,7 +5,6 @@ use uuid::Uuid;
|
||||
|
||||
use jirs_data::WsMsg;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{Page, PageContent, SignInPage};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
@ -15,7 +14,8 @@ use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_link::StyledLink;
|
||||
use crate::shared::{outer_layout, write_auth_token, ToNode};
|
||||
use crate::validations::{is_email, is_token};
|
||||
use crate::{model, FieldId, Msg, SignInFieldId};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{model, FieldId, Msg, SignInFieldId, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
if model.page != Page::SignIn {
|
||||
@ -49,10 +49,10 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
page.token_touched = true;
|
||||
}
|
||||
Msg::SignInRequest => {
|
||||
send_ws_msg(WsMsg::AuthenticateRequest(
|
||||
page.email.clone(),
|
||||
page.username.clone(),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::AuthenticateRequest(page.email.clone(), page.username.clone()),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::BindClientRequest => {
|
||||
let bind_token: uuid::Uuid = match Uuid::from_str(page.token.as_str()) {
|
||||
@ -62,21 +62,24 @@ pub fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>)
|
||||
return;
|
||||
}
|
||||
};
|
||||
send_ws_msg(WsMsg::BindTokenCheck(bind_token));
|
||||
send_ws_msg(WsMsg::BindTokenCheck(bind_token), model.ws.as_ref());
|
||||
}
|
||||
Msg::WsMsg(WsMsg::AuthenticateSuccess) => {
|
||||
page.login_success = true;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::BindTokenOk(access_token)) => {
|
||||
match write_auth_token(Some(access_token)) {
|
||||
Ok(msg) => {
|
||||
orders.skip().send_msg(msg);
|
||||
}
|
||||
Err(e) => {
|
||||
error!(e);
|
||||
Msg::WebSocketChange(change) => match change {
|
||||
WebSocketChanged::WsMsg(WsMsg::AuthenticateSuccess) => {
|
||||
page.login_success = true;
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::BindTokenOk(access_token)) => {
|
||||
match write_auth_token(Some(access_token)) {
|
||||
Ok(msg) => {
|
||||
orders.skip().send_msg(msg);
|
||||
}
|
||||
Err(e) => {
|
||||
error!(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::{SignUpFieldId, WsMsg};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{Page, PageContent, SignUpPage};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
@ -12,7 +11,8 @@ use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_link::StyledLink;
|
||||
use crate::shared::{outer_layout, ToNode};
|
||||
use crate::validations::is_email;
|
||||
use crate::{model, FieldId, Msg};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{model, FieldId, Msg, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>) {
|
||||
if model.page != Page::SignUp {
|
||||
@ -42,17 +42,20 @@ pub fn update(msg: Msg, model: &mut model::Model, _orders: &mut impl Orders<Msg>
|
||||
page.email_touched = true;
|
||||
}
|
||||
Msg::SignUpRequest => {
|
||||
send_ws_msg(WsMsg::SignUpRequest(
|
||||
page.email.clone(),
|
||||
page.username.clone(),
|
||||
));
|
||||
}
|
||||
Msg::WsMsg(WsMsg::SignUpSuccess) => {
|
||||
page.sign_up_success = true;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::SignUpPairTaken) => {
|
||||
page.error = "Pair you give is either taken or is not matching".to_string();
|
||||
send_ws_msg(
|
||||
WsMsg::SignUpRequest(page.email.clone(), page.username.clone()),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::WebSocketChange(change) => match change {
|
||||
WebSocketChanged::WsMsg(WsMsg::SignUpSuccess) => {
|
||||
page.sign_up_success = true;
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::SignUpPairTaken) => {
|
||||
page.error = "Pair you give is either taken or is not matching".to_string();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -126,7 +129,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
let error_row = if page.error.is_empty() {
|
||||
empty![]
|
||||
} else {
|
||||
div![class!["error"], p![page.error]]
|
||||
div![class!["error"], p![page.error.as_str()]]
|
||||
};
|
||||
|
||||
let sign_up_form = StyledForm::build()
|
||||
|
@ -2,7 +2,6 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::{InvitationState, ToVec, UserRole, UsersFieldId, WsMsg};
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::*;
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_field::StyledField;
|
||||
@ -11,7 +10,8 @@ use crate::shared::styled_input::StyledInput;
|
||||
use crate::shared::styled_select::*;
|
||||
use crate::shared::{inner_layout, ToChild, ToNode};
|
||||
use crate::validations::is_email;
|
||||
use crate::{FieldId, Msg, PageChanged, UsersPageChange};
|
||||
use crate::ws::send_ws_msg;
|
||||
use crate::{FieldId, Msg, PageChanged, UsersPageChange, WebSocketChanged};
|
||||
|
||||
pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
if let Msg::ChangePage(Page::Users) = msg {
|
||||
@ -27,18 +27,50 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
page.user_role_state.update(&msg, orders);
|
||||
|
||||
match msg {
|
||||
Msg::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) | Msg::ChangePage(Page::Users)
|
||||
if model.user.is_some() =>
|
||||
{
|
||||
send_ws_msg(WsMsg::InvitationListRequest);
|
||||
send_ws_msg(WsMsg::InvitedUsersRequest);
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
|
||||
page.invited_users = users;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitationListLoaded(invitations)) => {
|
||||
page.invitations = invitations;
|
||||
Msg::ChangePage(Page::Users) if model.user.is_some() => {
|
||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::InvitedUsersRequest, model.ws.as_ref());
|
||||
}
|
||||
Msg::WebSocketChange(change) => match change {
|
||||
WebSocketChanged::WsMsg(WsMsg::AuthorizeLoaded(Ok(_))) if model.user.is_some() => {
|
||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref());
|
||||
send_ws_msg(WsMsg::InvitedUsersRequest, model.ws.as_ref());
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitedUsersLoaded(users)) => {
|
||||
page.invited_users = users;
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitationListLoaded(invitations)) => {
|
||||
page.invitations = invitations;
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitationRevokeSuccess(id)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut page.invitations, &mut old);
|
||||
for mut invitation in old {
|
||||
if id == invitation.id {
|
||||
invitation.state = InvitationState::Revoked;
|
||||
}
|
||||
page.invitations.push(invitation);
|
||||
}
|
||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref());
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitedUserRemoveSuccess(email)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut page.invited_users, &mut old);
|
||||
for user in old {
|
||||
if user.email != email {
|
||||
page.invited_users.push(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitationSendSuccess) => {
|
||||
send_ws_msg(WsMsg::InvitationListRequest, model.ws.as_ref());
|
||||
page.form_state = InvitationFormState::Succeed;
|
||||
}
|
||||
WebSocketChanged::WsMsg(WsMsg::InvitationSendFailure) => {
|
||||
page.form_state = InvitationFormState::Failed;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Msg::PageChanged(PageChanged::Users(UsersPageChange::ResetForm)) => {
|
||||
page.name.clear();
|
||||
page.name_touched = false;
|
||||
@ -64,43 +96,22 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
Msg::InviteRequest => {
|
||||
page.form_state = InvitationFormState::Sent;
|
||||
send_ws_msg(WsMsg::InvitationSendRequest {
|
||||
name: page.name.clone(),
|
||||
email: page.email.clone(),
|
||||
})
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitationRevokeSuccess(id)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut page.invitations, &mut old);
|
||||
for mut invitation in old {
|
||||
if id == invitation.id {
|
||||
invitation.state = InvitationState::Revoked;
|
||||
}
|
||||
page.invitations.push(invitation);
|
||||
}
|
||||
send_ws_msg(WsMsg::InvitationListRequest);
|
||||
send_ws_msg(
|
||||
WsMsg::InvitationSendRequest {
|
||||
name: page.name.clone(),
|
||||
email: page.email.clone(),
|
||||
},
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::InviteRevokeRequest(invitation_id) => {
|
||||
send_ws_msg(WsMsg::InvitationRevokeRequest(invitation_id));
|
||||
send_ws_msg(
|
||||
WsMsg::InvitationRevokeRequest(invitation_id),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
Msg::InvitedUserRemove(email) => {
|
||||
send_ws_msg(WsMsg::InvitedUserRemoveRequest(email));
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitedUserRemoveSuccess(email)) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut page.invited_users, &mut old);
|
||||
for user in old {
|
||||
if user.email != email {
|
||||
page.invited_users.push(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitationSendSuccess) => {
|
||||
send_ws_msg(WsMsg::InvitationListRequest);
|
||||
page.form_state = InvitationFormState::Succeed;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::InvitationSendFailure) => {
|
||||
page.form_state = InvitationFormState::Failed;
|
||||
send_ws_msg(WsMsg::InvitedUserRemoveRequest(email), model.ws.as_ref());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -210,8 +221,8 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.into_node();
|
||||
li![
|
||||
class!["user"],
|
||||
span![user.name],
|
||||
span![user.email],
|
||||
span![user.name.as_str()],
|
||||
span![user.email.as_str()],
|
||||
span![format!("{}", user.user_role)],
|
||||
remove,
|
||||
]
|
||||
@ -238,8 +249,8 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
li![
|
||||
class!["invitation"],
|
||||
attrs![At::Class => format!("{}", invitation.state)],
|
||||
span![invitation.name],
|
||||
span![invitation.email],
|
||||
span![invitation.name.as_str()],
|
||||
span![invitation.email.as_str()],
|
||||
span![format!("{}", invitation.state)],
|
||||
revoke,
|
||||
]
|
||||
|
@ -2,8 +2,8 @@ use seed::*;
|
||||
|
||||
use jirs_data::*;
|
||||
|
||||
use crate::api::send_ws_msg;
|
||||
use crate::model::{Model, PageContent};
|
||||
use crate::ws::send_ws_msg;
|
||||
|
||||
pub fn drag_started(issue_id: IssueId, model: &mut Model) {
|
||||
let project_page = match &mut model.page_content {
|
||||
@ -90,16 +90,22 @@ pub fn sync(model: &mut Model) {
|
||||
continue;
|
||||
}
|
||||
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
issue.id,
|
||||
IssueFieldId::IssueStatusId,
|
||||
PayloadVariant::I32(issue.issue_status_id),
|
||||
));
|
||||
send_ws_msg(WsMsg::IssueUpdateRequest(
|
||||
issue.id,
|
||||
IssueFieldId::ListPosition,
|
||||
PayloadVariant::I32(issue.list_position),
|
||||
));
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
issue.id,
|
||||
IssueFieldId::IssueStatusId,
|
||||
PayloadVariant::I32(issue.issue_status_id),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
send_ws_msg(
|
||||
WsMsg::IssueUpdateRequest(
|
||||
issue.id,
|
||||
IssueFieldId::ListPosition,
|
||||
PayloadVariant::I32(issue.list_position),
|
||||
),
|
||||
model.ws.as_ref(),
|
||||
);
|
||||
}
|
||||
project_page.issue_drag.clear();
|
||||
}
|
||||
|
@ -4,57 +4,75 @@ use jirs_data::WsMsg;
|
||||
|
||||
use crate::model::*;
|
||||
use crate::shared::write_auth_token;
|
||||
use crate::{Msg, APP};
|
||||
use crate::{Msg, WebSocketChanged};
|
||||
|
||||
pub mod issue;
|
||||
|
||||
pub fn handle(msg: WsMsg) {
|
||||
let app = match unsafe { APP.as_mut().unwrap() }.write() {
|
||||
Ok(app) => app,
|
||||
pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>) {
|
||||
let ws = match ws {
|
||||
Some(ws) => ws,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
match msg {
|
||||
WsMsg::Ping | WsMsg::Pong => {}
|
||||
_ => app.update(Msg::WsMsg(msg)),
|
||||
}
|
||||
let binary = bincode::serialize(&msg).unwrap();
|
||||
ws.send_bytes(binary.as_slice())
|
||||
.expect("Failed to send ws msg");
|
||||
}
|
||||
|
||||
pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
if model.host_url.is_empty() {
|
||||
return;
|
||||
}
|
||||
let url = model.ws_url.as_str();
|
||||
|
||||
model.ws = WebSocket::builder(url, orders)
|
||||
.on_message(|msg| Msg::WebSocketChange(WebSocketChanged::WebSocketMessage(msg)))
|
||||
.on_open(|| Msg::WebSocketChange(WebSocketChanged::WebSocketOpened))
|
||||
.on_close(|_| Msg::WebSocketChange(WebSocketChanged::WebSocketClosed))
|
||||
.on_error(|| {})
|
||||
.build_and_open()
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub async fn read_incoming(msg: WebSocketMessage) -> Msg {
|
||||
let bytes = msg.bytes().await.unwrap_or_default();
|
||||
Msg::WebSocketChange(WebSocketChanged::WebSocketMessageLoaded(bytes))
|
||||
}
|
||||
|
||||
pub fn update(msg: &WsMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
// auth
|
||||
Msg::WsMsg(WsMsg::AuthorizeLoaded(Ok(user))) => {
|
||||
WsMsg::AuthorizeLoaded(Ok(user)) => {
|
||||
model.user = Some(user.clone());
|
||||
}
|
||||
Msg::WsMsg(WsMsg::AuthorizeExpired) => {
|
||||
WsMsg::AuthorizeExpired => {
|
||||
if let Ok(msg) = write_auth_token(None) {
|
||||
orders.skip().send_msg(msg);
|
||||
}
|
||||
}
|
||||
// project
|
||||
Msg::WsMsg(WsMsg::ProjectLoaded(project)) => {
|
||||
WsMsg::ProjectLoaded(project) => {
|
||||
model.project = Some(project.clone());
|
||||
}
|
||||
// issues
|
||||
Msg::WsMsg(WsMsg::ProjectIssuesLoaded(v)) => {
|
||||
WsMsg::ProjectIssuesLoaded(v) => {
|
||||
let mut v = v.clone();
|
||||
v.sort_by(|a, b| (a.list_position as i64).cmp(&(b.list_position as i64)));
|
||||
model.issues = v;
|
||||
}
|
||||
// issue statuses
|
||||
Msg::WsMsg(WsMsg::IssueStatusesResponse(v)) => {
|
||||
WsMsg::IssueStatusesResponse(v) => {
|
||||
model.issue_statuses = v.clone();
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueStatusCreated(is)) => {
|
||||
WsMsg::IssueStatusCreated(is) => {
|
||||
model.issue_statuses.push(is.clone());
|
||||
model
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueStatusUpdated(changed)) => {
|
||||
WsMsg::IssueStatusUpdated(changed) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut model.issue_statuses, &mut old);
|
||||
for is in old {
|
||||
@ -68,7 +86,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
.issue_statuses
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
}
|
||||
Msg::WsMsg(WsMsg::IssueDeleted(id)) => {
|
||||
WsMsg::IssueDeleted(id) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut model.issue_statuses, &mut old);
|
||||
for is in old {
|
||||
@ -82,11 +100,11 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
.sort_by(|a, b| a.position.cmp(&b.position));
|
||||
}
|
||||
// users
|
||||
Msg::WsMsg(WsMsg::ProjectUsersLoaded(v)) => {
|
||||
WsMsg::ProjectUsersLoaded(v) => {
|
||||
model.users = v.clone();
|
||||
}
|
||||
// comments
|
||||
Msg::WsMsg(WsMsg::IssueCommentsLoaded(comments)) => {
|
||||
WsMsg::IssueCommentsLoaded(comments) => {
|
||||
let issue_id = match model.modals.get(0) {
|
||||
Some(ModalType::EditIssue(issue_id, _)) => *issue_id,
|
||||
_ => return,
|
||||
@ -98,7 +116,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
v.sort_by(|a, b| a.updated_at.cmp(&b.updated_at));
|
||||
model.comments = v;
|
||||
}
|
||||
Msg::WsMsg(WsMsg::CommentDeleted(comment_id)) => {
|
||||
WsMsg::CommentDeleted(comment_id) => {
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut model.comments, &mut old);
|
||||
for comment in old.into_iter() {
|
||||
@ -107,7 +125,7 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Msg::WsMsg(WsMsg::AvatarUrlChanged(user_id, avatar_url)) => {
|
||||
WsMsg::AvatarUrlChanged(user_id, avatar_url) => {
|
||||
for user in model.users.iter_mut() {
|
||||
if user.id == *user_id {
|
||||
user.avatar_url = Some(avatar_url.clone());
|
||||
|
@ -24,7 +24,7 @@ if (process.env.NODE_ENV === "production") {
|
||||
execSync('cargo build --bin jirs-css', {
|
||||
cwd: jirDir,
|
||||
});
|
||||
const css = spawn('./target/debug/jirs-css', [
|
||||
spawn('./target/debug/jirs-css', [
|
||||
'-W',
|
||||
'-O',
|
||||
'./jirs-client/dev/styles.css'
|
||||
@ -70,7 +70,7 @@ module.exports = {
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, 'dev'),
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
hot: false,
|
||||
port: process.env.JIRS_CLIENT_PORT || 6000,
|
||||
host: process.env.JIRS_CLIENT_BIND || '0.0.0.0',
|
||||
allowedHosts: [
|
||||
|
@ -50,7 +50,6 @@ byteorder = "1.0"
|
||||
chrono = { version = "0.4", features = [ "serde" ] }
|
||||
libc = { version = "0.2.0" }
|
||||
pq-sys = { version = ">=0.3.0, <0.5.0" }
|
||||
quickcheck = { version = "0.4" }
|
||||
serde_json = { version = ">=0.8.0, <2.0" }
|
||||
toml = "0.5.6"
|
||||
bincode = "1.2.1"
|
||||
|
@ -153,53 +153,6 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
|
||||
/// Representation of the `issue_statuses` table.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
issue_statuses (id) {
|
||||
/// The `id` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
id -> Int4,
|
||||
/// The `name` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Varchar`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
name -> Varchar,
|
||||
/// The `position` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
position -> Int4,
|
||||
/// The `project_id` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
project_id -> Int4,
|
||||
/// The `created_at` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
created_at -> Timestamp,
|
||||
/// The `updated_at` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
updated_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
@ -301,6 +254,53 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
|
||||
/// Representation of the `issue_statuses` table.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
issue_statuses (id) {
|
||||
/// The `id` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
id -> Int4,
|
||||
/// The `name` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Varchar`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
name -> Varchar,
|
||||
/// The `position` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
position -> Int4,
|
||||
/// The `project_id` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Int4`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
project_id -> Int4,
|
||||
/// The `created_at` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
created_at -> Timestamp,
|
||||
/// The `updated_at` column of the `issue_statuses` table.
|
||||
///
|
||||
/// Its SQL type is `Timestamp`.
|
||||
///
|
||||
/// (Automatically generated by Diesel.)
|
||||
updated_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use jirs_data::sql::*;
|
||||
@ -489,8 +489,8 @@ allow_tables_to_appear_in_same_query!(
|
||||
comments,
|
||||
invitations,
|
||||
issue_assignees,
|
||||
issue_statuses,
|
||||
issues,
|
||||
issue_statuses,
|
||||
projects,
|
||||
tokens,
|
||||
users,
|
||||
|
Loading…
Reference in New Issue
Block a user