Refactored tables and loading blocks to use the new dedicated widgets for Tables and Loading blocks
This commit is contained in:
Generated
+110
-128
@@ -75,9 +75,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -224,9 +224,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.31"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@@ -234,7 +234,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -274,7 +274,7 @@ checksum = "15d296c475c6ed4093824c28e222420831d27577aaaf0a1163a3b7fc35b248a5"
|
||||
dependencies = [
|
||||
"directories",
|
||||
"serde",
|
||||
"serde_yaml 0.9.30",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -374,9 +374,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
@@ -569,19 +569,13 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap 2.1.0",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
@@ -600,9 +594,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
@@ -699,9 +693,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.59"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
@@ -732,22 +726,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -764,9 +748,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@@ -779,9 +763,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.67"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -794,9 +778,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -809,12 +793,6 @@ dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
@@ -848,9 +826,9 @@ checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
|
||||
|
||||
[[package]]
|
||||
name = "log4rs"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd"
|
||||
checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -861,11 +839,13 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"log-mdc",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde-value",
|
||||
"serde_json",
|
||||
"serde_yaml 0.8.26",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
"thread-id",
|
||||
"typemap-ors",
|
||||
@@ -874,11 +854,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7"
|
||||
checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -905,7 +885,7 @@ dependencies = [
|
||||
"rstest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml 0.9.30",
|
||||
"serde_yaml",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
@@ -927,9 +907,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
@@ -984,10 +964,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -1028,9 +1014,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.62"
|
||||
version = "0.10.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
|
||||
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
@@ -1060,9 +1046,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.98"
|
||||
version = "0.9.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
|
||||
checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1173,9 +1159,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1262,9 +1248,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1274,9 +1260,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1297,9 +1283,9 @@ checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.23"
|
||||
version = "0.11.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
|
||||
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
@@ -1319,9 +1305,11 @@ dependencies = [
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
@@ -1379,9 +1367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.30"
|
||||
version = "0.38.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"errno",
|
||||
@@ -1390,6 +1378,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@@ -1448,9 +1445,9 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -1467,9 +1464,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1478,9 +1475,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1510,23 +1507,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.26"
|
||||
version = "0.9.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
|
||||
checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e"
|
||||
dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"ryu",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -1654,6 +1639,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
@@ -1677,13 +1668,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.9.0"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -1720,12 +1710,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.31"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
|
||||
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
@@ -1755,9 +1746,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.35.1"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -1809,9 +1800,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.8"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -1830,11 +1821,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@@ -1903,9 +1894,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -1989,9 +1980,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1999,9 +1990,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -2014,9 +2005,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.40"
|
||||
version = "0.4.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
|
||||
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -2026,9 +2017,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -2036,9 +2027,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2049,15 +2040,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.67"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
|
||||
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -2236,15 +2227,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
|
||||
+7
-102
@@ -4,15 +4,13 @@ use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::text::{Line, Text};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Row;
|
||||
use ratatui::widgets::Table;
|
||||
use ratatui::widgets::Tabs;
|
||||
use ratatui::widgets::{Block, Wrap};
|
||||
use ratatui::widgets::Wrap;
|
||||
use ratatui::widgets::{Clear, List, ListItem};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::{HorizontallyScrollableText, Route, StatefulList, StatefulTable, TabState};
|
||||
use crate::models::{HorizontallyScrollableText, Route, StatefulList, TabState};
|
||||
use crate::ui::radarr_ui::RadarrUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
@@ -22,6 +20,7 @@ use crate::ui::utils::{
|
||||
use crate::ui::widgets::button::Button;
|
||||
use crate::ui::widgets::checkbox::Checkbox;
|
||||
use crate::ui::widgets::input_box::InputBox;
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
|
||||
mod radarr_ui;
|
||||
mod styles;
|
||||
@@ -275,14 +274,6 @@ fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -
|
||||
content_area
|
||||
}
|
||||
|
||||
pub struct TableProps<'a, T> {
|
||||
pub content: Option<&'a mut StatefulTable<T>>,
|
||||
pub wrapped_content: Option<Option<&'a mut StatefulTable<T>>>,
|
||||
pub table_headers: Vec<&'a str>,
|
||||
pub constraints: Vec<Constraint>,
|
||||
pub help: Option<String>,
|
||||
}
|
||||
|
||||
pub struct ListProps<'a, T> {
|
||||
pub content: &'a mut StatefulList<T>,
|
||||
pub title: &'static str,
|
||||
@@ -291,94 +282,6 @@ pub struct ListProps<'a, T> {
|
||||
pub help: Option<String>,
|
||||
}
|
||||
|
||||
fn draw_table<'a, T, F>(
|
||||
f: &mut Frame<'_>,
|
||||
area: Rect,
|
||||
block: Block<'_>,
|
||||
table_props: TableProps<'a, T>,
|
||||
row_mapper: F,
|
||||
is_loading: bool,
|
||||
highlight: bool,
|
||||
) where
|
||||
F: Fn(&T) -> Row<'a>,
|
||||
{
|
||||
let TableProps {
|
||||
content,
|
||||
wrapped_content,
|
||||
table_headers,
|
||||
constraints,
|
||||
help,
|
||||
} = table_props;
|
||||
|
||||
let content_area = draw_help_footer_and_get_content_area(f, area, help);
|
||||
|
||||
#[allow(clippy::unnecessary_unwrap)]
|
||||
if wrapped_content.is_some() && wrapped_content.as_ref().unwrap().is_some() {
|
||||
draw_table_contents(
|
||||
f,
|
||||
block,
|
||||
row_mapper,
|
||||
highlight,
|
||||
wrapped_content.unwrap().as_mut().unwrap(),
|
||||
table_headers,
|
||||
constraints,
|
||||
content_area,
|
||||
);
|
||||
} else if content.is_some() && !content.as_ref().unwrap().items.is_empty() {
|
||||
draw_table_contents(
|
||||
f,
|
||||
block,
|
||||
row_mapper,
|
||||
highlight,
|
||||
content.unwrap(),
|
||||
table_headers,
|
||||
constraints,
|
||||
content_area,
|
||||
);
|
||||
} else {
|
||||
loading(f, block, content_area, is_loading);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn draw_table_contents<'a, T, F>(
|
||||
f: &mut Frame<'_>,
|
||||
block: Block<'_>,
|
||||
row_mapper: F,
|
||||
highlight: bool,
|
||||
content: &mut StatefulTable<T>,
|
||||
table_headers: Vec<&str>,
|
||||
constraints: Vec<Constraint>,
|
||||
area: Rect,
|
||||
) where
|
||||
F: Fn(&T) -> Row<'a>,
|
||||
{
|
||||
let rows = content.items.iter().map(row_mapper);
|
||||
|
||||
let headers = Row::new(table_headers).default().bold().bottom_margin(0);
|
||||
|
||||
let mut table = Table::new(rows, &constraints).header(headers).block(block);
|
||||
|
||||
if highlight {
|
||||
table = table
|
||||
.highlight_style(Style::new().highlight())
|
||||
.highlight_symbol(HIGHLIGHT_SYMBOL);
|
||||
}
|
||||
|
||||
f.render_stateful_widget(table, area, &mut content.state);
|
||||
}
|
||||
|
||||
pub fn loading(f: &mut Frame<'_>, block: Block<'_>, area: Rect, is_loading: bool) {
|
||||
if is_loading {
|
||||
let paragraph = Paragraph::new(Text::from("\n\n Loading ...\n\n"))
|
||||
.system_function()
|
||||
.block(block);
|
||||
f.render_widget(paragraph, area);
|
||||
} else {
|
||||
f.render_widget(block, area)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_prompt_box(
|
||||
f: &mut Frame<'_>,
|
||||
area: Rect,
|
||||
@@ -532,7 +435,7 @@ pub fn draw_list_box<'a, T>(
|
||||
|
||||
f.render_stateful_widget(list, content_area, &mut content.state);
|
||||
} else {
|
||||
loading(f, block, content_area, is_loading);
|
||||
f.render_widget(LoadingBlock::new(is_loading, block), content_area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +446,9 @@ fn draw_help_footer_and_get_content_area(
|
||||
) -> Rect {
|
||||
if let Some(help_string) = help {
|
||||
let [content_area, help_footer_area] =
|
||||
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)]).areas(area);
|
||||
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
|
||||
.margin(1)
|
||||
.areas(area);
|
||||
|
||||
let help_paragraph = Paragraph::new(Text::from(format!(" {help_string}").help()))
|
||||
.block(layout_block_top_border())
|
||||
|
||||
@@ -18,7 +18,8 @@ use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_top_border_with_title, title_block,
|
||||
title_style,
|
||||
};
|
||||
use crate::ui::{draw_large_popup_over, draw_small_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{draw_large_popup_over, draw_small_popup_over, DrawUi};
|
||||
use crate::utils::convert_runtime;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -67,13 +68,10 @@ impl DrawUi for CollectionDetailsUi {
|
||||
}
|
||||
|
||||
pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let [description_area, table_area, help_footer_area] = Layout::vertical([
|
||||
Constraint::Percentage(25),
|
||||
Constraint::Percentage(70),
|
||||
Constraint::Percentage(5),
|
||||
])
|
||||
.margin(1)
|
||||
.areas(area);
|
||||
let [description_area, table_area] =
|
||||
Layout::vertical([Constraint::Percentage(25), Constraint::Fill(0)])
|
||||
.margin(1)
|
||||
.areas(area);
|
||||
let collection_selection =
|
||||
if let Some(filtered_collections) = app.data.radarr_data.filtered_collections.as_ref() {
|
||||
filtered_collections.current_selection()
|
||||
@@ -97,13 +95,63 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
.current_selection()
|
||||
.clone()
|
||||
};
|
||||
let help_text = Text::from(
|
||||
format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
)
|
||||
.help(),
|
||||
);
|
||||
let movie_row_mapper = |movie: &CollectionMovie| {
|
||||
let in_library = if app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.items
|
||||
.iter()
|
||||
.any(|mov| mov.tmdb_id == movie.tmdb_id)
|
||||
{
|
||||
"✔"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(table_area, 20),
|
||||
current_selection == *movie,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let imdb_rating = movie
|
||||
.ratings
|
||||
.imdb
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
let rotten_tomatoes_rating = movie
|
||||
.ratings
|
||||
.rotten_tomatoes
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let imdb_rating = if imdb_rating == 0.0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{imdb_rating:.1}")
|
||||
};
|
||||
let rotten_tomatoes_rating = if rotten_tomatoes_rating == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{rotten_tomatoes_rating}%")
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(in_library),
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(imdb_rating),
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let monitored = if collection_selection.monitored {
|
||||
"Yes"
|
||||
} else {
|
||||
@@ -115,10 +163,14 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
"No"
|
||||
};
|
||||
let minimum_availability = collection_selection.minimum_availability.to_display_str();
|
||||
let help_footer = format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
);
|
||||
|
||||
let collection_description = Text::from(vec![
|
||||
Line::from(vec![
|
||||
"Overview ".primary().bold(),
|
||||
"Overview: ".primary().bold(),
|
||||
collection_selection
|
||||
.overview
|
||||
.clone()
|
||||
@@ -151,102 +203,36 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
let description_paragraph = Paragraph::new(collection_description)
|
||||
.block(borderless_block())
|
||||
.wrap(Wrap { trim: false });
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
let movies_table = ManagarrTable::new(
|
||||
Some(&mut app.data.radarr_data.collection_movies),
|
||||
movie_row_mapper,
|
||||
)
|
||||
.block(layout_block_top_border_with_title(title_style("Movies")))
|
||||
.loading(app.is_loading)
|
||||
.footer_alignment(Alignment::Center)
|
||||
.footer(Some(help_footer))
|
||||
.headers([
|
||||
"✔",
|
||||
"Title",
|
||||
"Year",
|
||||
"Runtime",
|
||||
"IMDB Rating",
|
||||
"Rotten Tomatoes Rating",
|
||||
"Genres",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(2),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Percentage(28),
|
||||
]);
|
||||
|
||||
f.render_widget(title_block(&collection_selection.title.text), area);
|
||||
|
||||
f.render_widget(description_paragraph, description_area);
|
||||
f.render_widget(help_paragraph, help_footer_area);
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
table_area,
|
||||
layout_block_top_border_with_title(title_style("Movies")),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.collection_movies),
|
||||
wrapped_content: None,
|
||||
table_headers: vec![
|
||||
"✔",
|
||||
"Title",
|
||||
"Year",
|
||||
"Runtime",
|
||||
"IMDB Rating",
|
||||
"Rotten Tomatoes Rating",
|
||||
"Genres",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(2),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Percentage(28),
|
||||
],
|
||||
help: None,
|
||||
},
|
||||
|movie| {
|
||||
let in_library = if app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.items
|
||||
.iter()
|
||||
.any(|mov| mov.tmdb_id == movie.tmdb_id)
|
||||
{
|
||||
"✔"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(table_area, 20),
|
||||
current_selection == *movie,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let imdb_rating = movie
|
||||
.ratings
|
||||
.imdb
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
let rotten_tomatoes_rating = movie
|
||||
.ratings
|
||||
.rotten_tomatoes
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let imdb_rating = if imdb_rating == 0.0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{imdb_rating:.1}")
|
||||
};
|
||||
let rotten_tomatoes_rating = if rotten_tomatoes_rating == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{rotten_tomatoes_rating}%")
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(in_library),
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(imdb_rating),
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
f.render_widget(movies_table, table_area);
|
||||
}
|
||||
|
||||
fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -12,9 +12,10 @@ use crate::ui::radarr_ui::collections::collection_details_ui::CollectionDetailsU
|
||||
use crate::ui::radarr_ui::collections::edit_collection_ui::EditCollectionUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_error_message_popup, draw_input_box_popup, draw_popup_over, draw_prompt_box,
|
||||
draw_prompt_popup_over, draw_table, DrawUi, TableProps,
|
||||
draw_prompt_popup_over, DrawUi,
|
||||
};
|
||||
|
||||
mod collection_details_ui;
|
||||
@@ -113,67 +114,62 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
Some(filtered_collections) if !app.data.radarr_data.is_filtering => Some(filtered_collections),
|
||||
_ => Some(&mut app.data.radarr_data.collections),
|
||||
};
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content,
|
||||
wrapped_content: None,
|
||||
table_headers: vec![
|
||||
"Collection",
|
||||
"Number of Movies",
|
||||
"Root Folder Path",
|
||||
"Quality Profile",
|
||||
"Search on Add",
|
||||
"Monitored",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(25),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|collection| {
|
||||
let number_of_movies = collection.movies.clone().unwrap_or_default().len();
|
||||
collection.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 25),
|
||||
*collection == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let monitored = if collection.monitored { "🏷" } else { "" };
|
||||
let search_on_add = if collection.search_on_add {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
};
|
||||
let collections_table_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let collection_row_mapping = |collection: &Collection| {
|
||||
let number_of_movies = collection.movies.clone().unwrap_or_default().len();
|
||||
collection.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 25),
|
||||
*collection == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let monitored = if collection.monitored { "🏷" } else { "" };
|
||||
let search_on_add = if collection.search_on_add {
|
||||
"Yes"
|
||||
} else {
|
||||
"No"
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(collection.title.to_string()),
|
||||
Cell::from(number_of_movies.to_string()),
|
||||
Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
|
||||
Cell::from(
|
||||
quality_profile_map
|
||||
.get_by_left(&collection.quality_profile_id)
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
),
|
||||
Cell::from(search_on_add),
|
||||
Cell::from(monitored),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(collection.title.to_string()),
|
||||
Cell::from(number_of_movies.to_string()),
|
||||
Cell::from(collection.root_folder_path.clone().unwrap_or_default()),
|
||||
Cell::from(
|
||||
quality_profile_map
|
||||
.get_by_left(&collection.quality_profile_id)
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
),
|
||||
Cell::from(search_on_add),
|
||||
Cell::from(monitored),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let collections_table = ManagarrTable::new(content, collection_row_mapping)
|
||||
.loading(app.is_loading)
|
||||
.footer(collections_table_footer)
|
||||
.block(layout_block_top_border())
|
||||
.headers([
|
||||
"Collection",
|
||||
"Number of Movies",
|
||||
"Root Folder Path",
|
||||
"Quality Profile",
|
||||
"Search on Add",
|
||||
"Monitored",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(25),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(15),
|
||||
]);
|
||||
|
||||
f.render_widget(collections_table, area);
|
||||
}
|
||||
|
||||
fn draw_update_all_collections_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLO
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, DrawUi};
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -48,76 +49,74 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
} else {
|
||||
app.data.radarr_data.downloads.current_selection().clone()
|
||||
};
|
||||
let downloads_table_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.downloads),
|
||||
wrapped_content: None,
|
||||
table_headers: vec![
|
||||
"Title",
|
||||
"Percent Complete",
|
||||
"Size",
|
||||
"Output Path",
|
||||
"Indexer",
|
||||
"Download Client",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(13),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|download_record| {
|
||||
let DownloadRecord {
|
||||
title,
|
||||
size,
|
||||
sizeleft,
|
||||
download_client,
|
||||
indexer,
|
||||
output_path,
|
||||
..
|
||||
} = download_record;
|
||||
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
||||
let DownloadRecord {
|
||||
title,
|
||||
size,
|
||||
sizeleft,
|
||||
download_client,
|
||||
indexer,
|
||||
output_path,
|
||||
..
|
||||
} = download_record;
|
||||
|
||||
if output_path.is_some() {
|
||||
output_path.as_ref().unwrap().scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 18),
|
||||
current_selection == *download_record,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
}
|
||||
if output_path.is_some() {
|
||||
output_path.as_ref().unwrap().scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 18),
|
||||
current_selection == *download_record,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
}
|
||||
|
||||
let percent = 1f64 - (*sizeleft as f64 / *size as f64);
|
||||
let file_size: f64 = convert_to_gb(*size);
|
||||
let percent = 1f64 - (*sizeleft as f64 / *size as f64);
|
||||
let file_size: f64 = convert_to_gb(*size);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(title.to_owned()),
|
||||
Cell::from(format!("{:.0}%", percent * 100.0)),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(
|
||||
output_path
|
||||
.as_ref()
|
||||
.unwrap_or(&HorizontallyScrollableText::default())
|
||||
.to_string(),
|
||||
),
|
||||
Cell::from(indexer.to_owned()),
|
||||
Cell::from(download_client.to_owned()),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(title.to_owned()),
|
||||
Cell::from(format!("{:.0}%", percent * 100.0)),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(
|
||||
output_path
|
||||
.as_ref()
|
||||
.unwrap_or(&HorizontallyScrollableText::default())
|
||||
.to_string(),
|
||||
),
|
||||
Cell::from(indexer.to_owned()),
|
||||
Cell::from(download_client.to_owned()),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let downloads_table = ManagarrTable::new(
|
||||
Some(&mut app.data.radarr_data.downloads),
|
||||
downloads_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.footer(downloads_table_footer)
|
||||
.headers([
|
||||
"Title",
|
||||
"Percent Complete",
|
||||
"Size",
|
||||
"Output Path",
|
||||
"Indexer",
|
||||
"Download Client",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(13),
|
||||
]);
|
||||
|
||||
f.render_widget(downloads_table, area);
|
||||
}
|
||||
|
||||
fn draw_delete_download_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::ui::utils::title_block_centered;
|
||||
use crate::ui::widgets::button::Button;
|
||||
use crate::ui::widgets::checkbox::Checkbox;
|
||||
use crate::ui::widgets::input_box::InputBox;
|
||||
use crate::ui::{draw_popup_over, loading, DrawUi};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::{draw_popup_over, DrawUi};
|
||||
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||
use ratatui::Frame;
|
||||
|
||||
@@ -160,6 +161,6 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
f.render_widget(cancel_button, cancel_area);
|
||||
}
|
||||
} else {
|
||||
loading(f, block, area, app.is_loading);
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ use crate::ui::utils::title_block_centered;
|
||||
use crate::ui::widgets::button::Button;
|
||||
use crate::ui::widgets::checkbox::Checkbox;
|
||||
use crate::ui::widgets::input_box::InputBox;
|
||||
use crate::ui::{draw_popup_over, loading, DrawUi};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::{draw_popup_over, DrawUi};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "indexer_settings_ui_tests.rs"]
|
||||
@@ -155,6 +156,6 @@ fn draw_edit_indexer_settings_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
||||
f.render_widget(save_button, save_area);
|
||||
f.render_widget(cancel_button, cancel_area);
|
||||
} else {
|
||||
loading(f, block, area, app.is_loading);
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||
use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, DrawUi};
|
||||
|
||||
mod edit_indexer_ui;
|
||||
mod indexer_settings_ui;
|
||||
@@ -59,83 +60,81 @@ impl DrawUi for IndexersUi {
|
||||
}
|
||||
|
||||
fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.indexers),
|
||||
wrapped_content: None,
|
||||
table_headers: vec![
|
||||
"Indexer",
|
||||
"RSS",
|
||||
"Automatic Search",
|
||||
"Interactive Search",
|
||||
"Priority",
|
||||
"Tags",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(25),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(23),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|indexer: &'_ Indexer| {
|
||||
let Indexer {
|
||||
name,
|
||||
enable_rss,
|
||||
enable_automatic_search,
|
||||
enable_interactive_search,
|
||||
priority,
|
||||
tags,
|
||||
..
|
||||
} = indexer;
|
||||
let bool_to_text = |flag: bool| {
|
||||
if flag {
|
||||
return Text::from("Enabled").success();
|
||||
}
|
||||
let indexers_row_mapping = |indexer: &'_ Indexer| {
|
||||
let Indexer {
|
||||
name,
|
||||
enable_rss,
|
||||
enable_automatic_search,
|
||||
enable_interactive_search,
|
||||
priority,
|
||||
tags,
|
||||
..
|
||||
} = indexer;
|
||||
let bool_to_text = |flag: bool| {
|
||||
if flag {
|
||||
return Text::from("Enabled").success();
|
||||
}
|
||||
|
||||
Text::from("Disabled").failure()
|
||||
};
|
||||
Text::from("Disabled").failure()
|
||||
};
|
||||
|
||||
let rss = bool_to_text(*enable_rss);
|
||||
let automatic_search = bool_to_text(*enable_automatic_search);
|
||||
let interactive_search = bool_to_text(*enable_interactive_search);
|
||||
let tags: String = tags
|
||||
.iter()
|
||||
.map(|tag_id| {
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.tags_map
|
||||
.get_by_left(&tag_id.as_i64().unwrap())
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
let rss = bool_to_text(*enable_rss);
|
||||
let automatic_search = bool_to_text(*enable_automatic_search);
|
||||
let interactive_search = bool_to_text(*enable_interactive_search);
|
||||
let tags: String = tags
|
||||
.iter()
|
||||
.map(|tag_id| {
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.tags_map
|
||||
.get_by_left(&tag_id.as_i64().unwrap())
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(name.clone().unwrap_or_default()),
|
||||
Cell::from(rss),
|
||||
Cell::from(automatic_search),
|
||||
Cell::from(interactive_search),
|
||||
Cell::from(priority.to_string()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
Row::new(vec![
|
||||
Cell::from(name.clone().unwrap_or_default()),
|
||||
Cell::from(rss),
|
||||
Cell::from(automatic_search),
|
||||
Cell::from(interactive_search),
|
||||
Cell::from(priority.to_string()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let indexers_table_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let indexers_table = ManagarrTable::new(
|
||||
Some(&mut app.data.radarr_data.indexers),
|
||||
indexers_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.footer(indexers_table_footer)
|
||||
.loading(app.is_loading)
|
||||
.headers([
|
||||
"Indexer",
|
||||
"RSS",
|
||||
"Automatic Search",
|
||||
"Interactive Search",
|
||||
"Priority",
|
||||
"Tags",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(25),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(23),
|
||||
]);
|
||||
|
||||
f.render_widget(indexers_table, area);
|
||||
}
|
||||
|
||||
fn draw_delete_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -6,10 +6,9 @@ use crate::models::Route;
|
||||
use crate::ui::radarr_ui::indexers::draw_indexers;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, get_width_from_percentage, title_block};
|
||||
use crate::ui::{
|
||||
draw_help_footer_and_get_content_area, draw_large_popup_over, draw_table, DrawUi, TableProps,
|
||||
};
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{draw_large_popup_over, DrawUi};
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
use ratatui::Frame;
|
||||
|
||||
@@ -47,47 +46,45 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
||||
IndexerTestResultModalItem::default()
|
||||
};
|
||||
f.render_widget(title_block("Test All Indexers"), area);
|
||||
let help = Some(format!(
|
||||
let help_footer = format!(
|
||||
"<↑↓> scroll | {}",
|
||||
build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES)
|
||||
));
|
||||
let content_area = draw_help_footer_and_get_content_area(f, area, help);
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
content_area,
|
||||
borderless_block(),
|
||||
TableProps {
|
||||
content: app.data.radarr_data.indexer_test_all_results.as_mut(),
|
||||
wrapped_content: None,
|
||||
table_headers: vec!["Indexer", "Pass/Fail", "Failure Messages"],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(70),
|
||||
],
|
||||
help: None,
|
||||
},
|
||||
|result| {
|
||||
result.validation_failures.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 86),
|
||||
*result == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||
let row = Row::new(vec![
|
||||
Cell::from(result.name.to_owned()),
|
||||
Cell::from(pass_fail.to_owned()),
|
||||
Cell::from(result.validation_failures.to_string()),
|
||||
]);
|
||||
|
||||
if result.is_valid {
|
||||
row.success()
|
||||
} else {
|
||||
row.failure()
|
||||
}
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
let test_results_row_mapping = |result: &IndexerTestResultModalItem| {
|
||||
result.validation_failures.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 86),
|
||||
*result == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||
let row = Row::new(vec![
|
||||
Cell::from(result.name.to_owned()),
|
||||
Cell::from(pass_fail.to_owned()),
|
||||
Cell::from(result.validation_failures.to_string()),
|
||||
]);
|
||||
|
||||
if result.is_valid {
|
||||
row.success()
|
||||
} else {
|
||||
row.failure()
|
||||
}
|
||||
};
|
||||
|
||||
let indexers_test_results_table = ManagarrTable::new(
|
||||
app.data.radarr_data.indexer_test_all_results.as_mut(),
|
||||
test_results_row_mapping,
|
||||
)
|
||||
.block(borderless_block())
|
||||
.loading(app.is_loading)
|
||||
.footer(Some(help_footer))
|
||||
.footer_alignment(Alignment::Center)
|
||||
.margin(1)
|
||||
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||
.constraints([
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(70),
|
||||
]);
|
||||
|
||||
f.render_widget(indexers_test_results_table, area);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ use crate::ui::utils::{
|
||||
};
|
||||
use crate::ui::widgets::button::Button;
|
||||
use crate::ui::widgets::input_box::InputBox;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_drop_down_popup, draw_error_popup, draw_error_popup_over, draw_large_popup_over,
|
||||
draw_medium_popup_over, draw_selectable_list, draw_table, DrawUi, TableProps,
|
||||
draw_medium_popup_over, draw_selectable_list, DrawUi,
|
||||
};
|
||||
use crate::utils::convert_runtime;
|
||||
use crate::{render_selectable_input_box, App};
|
||||
@@ -116,6 +117,64 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.unwrap()
|
||||
.offset
|
||||
.borrow();
|
||||
let search_results_row_mapping = |movie: &AddMovieSearchResult| {
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let imdb_rating = movie
|
||||
.ratings
|
||||
.imdb
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
let rotten_tomatoes_rating = movie
|
||||
.ratings
|
||||
.rotten_tomatoes
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let imdb_rating = if imdb_rating == 0.0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{imdb_rating:.1}")
|
||||
};
|
||||
let rotten_tomatoes_rating = if rotten_tomatoes_rating == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{rotten_tomatoes_rating}%")
|
||||
};
|
||||
let in_library = if app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.items
|
||||
.iter()
|
||||
.any(|mov| mov.tmdb_id == movie.tmdb_id)
|
||||
{
|
||||
"✔"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 27),
|
||||
*movie == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(in_library),
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(imdb_rating),
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
|
||||
if let Route::Radarr(active_radarr_block, _) = *app.get_current_route() {
|
||||
match active_radarr_block {
|
||||
@@ -134,8 +193,14 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
f.render_widget(help_paragraph, help_area);
|
||||
}
|
||||
ActiveRadarrBlock::AddMovieEmptySearchResults => {
|
||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
f.render_widget(layout_block(), results_area);
|
||||
draw_error_popup(f, "No movies found matching your query!");
|
||||
f.render_widget(help_paragraph, help_area);
|
||||
}
|
||||
ActiveRadarrBlock::AddMovieSearchResults
|
||||
| ActiveRadarrBlock::AddMoviePrompt
|
||||
@@ -150,96 +215,33 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
let search_results_table = ManagarrTable::new(
|
||||
app.data.radarr_data.add_searched_movies.as_mut(),
|
||||
search_results_row_mapping,
|
||||
)
|
||||
.loading(is_loading)
|
||||
.block(layout_block())
|
||||
.headers([
|
||||
"✔",
|
||||
"Title",
|
||||
"Year",
|
||||
"Runtime",
|
||||
"IMDB",
|
||||
"Rotten Tomatoes",
|
||||
"Genres",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(2),
|
||||
Constraint::Percentage(27),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(28),
|
||||
]);
|
||||
|
||||
f.render_widget(search_results_table, results_area);
|
||||
f.render_widget(help_paragraph, help_area);
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
results_area,
|
||||
layout_block(),
|
||||
TableProps {
|
||||
content: None,
|
||||
wrapped_content: Some(app.data.radarr_data.add_searched_movies.as_mut()),
|
||||
table_headers: vec![
|
||||
"✔",
|
||||
"Title",
|
||||
"Year",
|
||||
"Runtime",
|
||||
"IMDB",
|
||||
"Rotten Tomatoes",
|
||||
"Genres",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(2),
|
||||
Constraint::Percentage(27),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(8),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(28),
|
||||
],
|
||||
help: None,
|
||||
},
|
||||
|movie| {
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let imdb_rating = movie
|
||||
.ratings
|
||||
.imdb
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
let rotten_tomatoes_rating = movie
|
||||
.ratings
|
||||
.rotten_tomatoes
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.value
|
||||
.as_u64()
|
||||
.unwrap();
|
||||
let imdb_rating = if imdb_rating == 0.0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{imdb_rating:.1}")
|
||||
};
|
||||
let rotten_tomatoes_rating = if rotten_tomatoes_rating == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{rotten_tomatoes_rating}%")
|
||||
};
|
||||
let in_library = if app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.items
|
||||
.iter()
|
||||
.any(|mov| mov.tmdb_id == movie.tmdb_id)
|
||||
{
|
||||
"✔"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 27),
|
||||
*movie == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(in_library),
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(imdb_rating),
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
is_loading,
|
||||
true,
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ use crate::ui::radarr_ui::library::delete_movie_ui::DeleteMovieUi;
|
||||
use crate::ui::radarr_ui::library::edit_movie_ui::EditMovieUi;
|
||||
use crate::ui::radarr_ui::library::movie_details_ui::MovieDetailsUi;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_error_message_popup, draw_input_box_popup, draw_popup_over, draw_prompt_box,
|
||||
draw_prompt_popup_over, draw_table, DrawUi, TableProps,
|
||||
draw_prompt_popup_over, DrawUi,
|
||||
};
|
||||
use crate::utils::{convert_runtime, convert_to_gb};
|
||||
|
||||
@@ -106,90 +107,85 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Some(filtered_movies) if !app.data.radarr_data.is_filtering => Some(filtered_movies),
|
||||
_ => Some(&mut app.data.radarr_data.movies),
|
||||
};
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content,
|
||||
wrapped_content: None,
|
||||
table_headers: vec![
|
||||
"Title",
|
||||
"Year",
|
||||
"Studio",
|
||||
"Runtime",
|
||||
"Rating",
|
||||
"Language",
|
||||
"Size",
|
||||
"Quality Profile",
|
||||
"Monitored",
|
||||
"Tags",
|
||||
],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(27),
|
||||
Constraint::Percentage(4),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(12),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|movie| {
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 27),
|
||||
*movie == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let monitored = if movie.monitored { "🏷" } else { "" };
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let file_size: f64 = convert_to_gb(movie.size_on_disk);
|
||||
let certification = movie.certification.clone().unwrap_or_default();
|
||||
let quality_profile = quality_profile_map
|
||||
.get_by_left(&movie.quality_profile_id)
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let tags = movie
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag_id| {
|
||||
tags_map
|
||||
.get_by_left(&tag_id.as_i64().unwrap())
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
let library_table_row_mapping = |movie: &Movie| {
|
||||
movie.title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 27),
|
||||
*movie == current_selection,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let monitored = if movie.monitored { "🏷" } else { "" };
|
||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||
let file_size: f64 = convert_to_gb(movie.size_on_disk);
|
||||
let certification = movie.certification.clone().unwrap_or_default();
|
||||
let quality_profile = quality_profile_map
|
||||
.get_by_left(&movie.quality_profile_id)
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let tags = movie
|
||||
.tags
|
||||
.iter()
|
||||
.map(|tag_id| {
|
||||
tags_map
|
||||
.get_by_left(&tag_id.as_i64().unwrap())
|
||||
.unwrap()
|
||||
.clone()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
decorate_with_row_style(
|
||||
downloads_vec,
|
||||
movie,
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
]),
|
||||
)
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
decorate_with_row_style(
|
||||
downloads_vec,
|
||||
movie,
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
]),
|
||||
)
|
||||
};
|
||||
let library_table = ManagarrTable::new(content, library_table_row_mapping)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.footer(help_footer)
|
||||
.headers([
|
||||
"Title",
|
||||
"Year",
|
||||
"Studio",
|
||||
"Runtime",
|
||||
"Rating",
|
||||
"Language",
|
||||
"Size",
|
||||
"Quality Profile",
|
||||
"Monitored",
|
||||
"Tags",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(27),
|
||||
Constraint::Percentage(4),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(6),
|
||||
Constraint::Percentage(12),
|
||||
]);
|
||||
|
||||
f.render_widget(library_table, area);
|
||||
}
|
||||
|
||||
fn draw_update_all_movies_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -15,10 +15,11 @@ use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border,
|
||||
};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_drop_down_popup, draw_large_popup_over, draw_prompt_box, draw_prompt_box_with_content,
|
||||
draw_prompt_popup_over, draw_selectable_list, draw_small_popup_over, draw_table, draw_tabs,
|
||||
loading, DrawUi, TableProps,
|
||||
draw_prompt_popup_over, draw_selectable_list, draw_small_popup_over, draw_tabs, DrawUi,
|
||||
};
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
@@ -189,7 +190,10 @@ fn draw_file_info(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
f.render_widget(video_details_title_paragraph, video_details_title_area);
|
||||
f.render_widget(video_details_paragraph, video_details_area);
|
||||
}
|
||||
_ => loading(f, layout_block_top_border(), area, app.is_loading),
|
||||
_ => f.render_widget(
|
||||
LoadingBlock::new(app.is_loading, layout_block_top_border()),
|
||||
area,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,11 +234,12 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
|
||||
f.render_widget(paragraph, area);
|
||||
}
|
||||
_ => loading(
|
||||
f,
|
||||
block,
|
||||
_ => f.render_widget(
|
||||
LoadingBlock::new(
|
||||
app.is_loading || app.data.radarr_data.movie_details_modal.is_none(),
|
||||
block,
|
||||
),
|
||||
area,
|
||||
app.is_loading || app.data.radarr_data.movie_details_modal.is_none(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -249,148 +254,137 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.current_selection()
|
||||
.clone()
|
||||
};
|
||||
let history_row_mapping = |movie_history_item: &MovieHistoryItem| {
|
||||
let MovieHistoryItem {
|
||||
source_title,
|
||||
quality,
|
||||
languages,
|
||||
date,
|
||||
event_type,
|
||||
} = movie_history_item;
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(&mut movie_details_modal.movie_history),
|
||||
wrapped_content: None,
|
||||
table_headers: vec!["Source Title", "Event Type", "Languages", "Quality", "Date"],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(34),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(21),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|movie_history_item| {
|
||||
let MovieHistoryItem {
|
||||
source_title,
|
||||
quality,
|
||||
languages,
|
||||
date,
|
||||
event_type,
|
||||
} = movie_history_item;
|
||||
movie_history_item.source_title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 34),
|
||||
current_selection == *movie_history_item,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
|
||||
movie_history_item.source_title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 34),
|
||||
current_selection == *movie_history_item,
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(source_title.to_string()),
|
||||
Cell::from(event_type.to_owned()),
|
||||
Cell::from(
|
||||
languages
|
||||
.iter()
|
||||
.map(|language| language.name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
),
|
||||
Cell::from(quality.quality.name.to_owned()),
|
||||
Cell::from(date.to_string()),
|
||||
])
|
||||
.success()
|
||||
};
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let history_table = ManagarrTable::new(
|
||||
Some(&mut movie_details_modal.movie_history),
|
||||
history_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.footer(help_footer)
|
||||
.headers(["Source Title", "Event Type", "Languages", "Quality", "Date"])
|
||||
.constraints([
|
||||
Constraint::Percentage(34),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(21),
|
||||
]);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(source_title.to_string()),
|
||||
Cell::from(event_type.to_owned()),
|
||||
Cell::from(
|
||||
languages
|
||||
.iter()
|
||||
.map(|language| language.name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
),
|
||||
Cell::from(quality.quality.name.to_owned()),
|
||||
Cell::from(date.to_string()),
|
||||
])
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
f.render_widget(history_table, area);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_movie_cast(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_cast,
|
||||
),
|
||||
wrapped_content: None,
|
||||
constraints: iter::repeat(Constraint::Ratio(1, 2)).take(2).collect(),
|
||||
table_headers: vec!["Cast Member", "Character"],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|cast_member| {
|
||||
let Credit {
|
||||
person_name,
|
||||
character,
|
||||
..
|
||||
} = cast_member;
|
||||
let cast_row_mapping = |cast_member: &Credit| {
|
||||
let Credit {
|
||||
person_name,
|
||||
character,
|
||||
..
|
||||
} = cast_member;
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(character.clone().unwrap_or_default()),
|
||||
])
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
Row::new(vec![
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(character.clone().unwrap_or_default()),
|
||||
])
|
||||
.success()
|
||||
};
|
||||
let content = Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_cast,
|
||||
);
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let cast_table = ManagarrTable::new(content, cast_row_mapping)
|
||||
.block(layout_block_top_border())
|
||||
.footer(help_footer)
|
||||
.loading(app.is_loading)
|
||||
.headers(["Cast Member", "Character"])
|
||||
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
||||
|
||||
f.render_widget(cast_table, area);
|
||||
}
|
||||
|
||||
fn draw_movie_crew(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_crew,
|
||||
),
|
||||
wrapped_content: None,
|
||||
constraints: iter::repeat(Constraint::Ratio(1, 3)).take(3).collect(),
|
||||
table_headers: vec!["Crew Member", "Job", "Department"],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|crew_member| {
|
||||
let Credit {
|
||||
person_name,
|
||||
job,
|
||||
department,
|
||||
..
|
||||
} = crew_member;
|
||||
let crew_row_mapping = |crew_member: &Credit| {
|
||||
let Credit {
|
||||
person_name,
|
||||
job,
|
||||
department,
|
||||
..
|
||||
} = crew_member;
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(job.clone().unwrap_or_default()),
|
||||
Cell::from(department.clone().unwrap_or_default()),
|
||||
])
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
Row::new(vec![
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(job.clone().unwrap_or_default()),
|
||||
Cell::from(department.clone().unwrap_or_default()),
|
||||
])
|
||||
.success()
|
||||
};
|
||||
let content = Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_crew,
|
||||
);
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let crew_table = ManagarrTable::new(content, crew_row_mapping)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.headers(["Crew Member", "Job", "Department"])
|
||||
.constraints(iter::repeat(Constraint::Ratio(1, 3)).take(3))
|
||||
.footer(help_footer);
|
||||
|
||||
f.render_widget(crew_table, area);
|
||||
}
|
||||
|
||||
fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
@@ -407,6 +401,11 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
_ => (Release::default(), true, None),
|
||||
};
|
||||
let current_route = *app.get_current_route();
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let mut table_headers_vec = vec![
|
||||
"Source".to_owned(),
|
||||
"Age".to_owned(),
|
||||
@@ -442,99 +441,89 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
ReleaseField::Quality => table_headers_vec[8].push_str(direction),
|
||||
}
|
||||
}
|
||||
let content = Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_releases,
|
||||
);
|
||||
let releases_row_mapping = |release: &Release| {
|
||||
let Release {
|
||||
protocol,
|
||||
age,
|
||||
title,
|
||||
indexer,
|
||||
size,
|
||||
rejected,
|
||||
seeders,
|
||||
leechers,
|
||||
languages,
|
||||
quality,
|
||||
..
|
||||
} = release;
|
||||
let age = format!("{age} days");
|
||||
title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 30),
|
||||
current_selection == *release
|
||||
&& current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(),
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let size = convert_to_gb(*size);
|
||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||
let peers = if seeders.is_none() || leechers.is_none() {
|
||||
Text::from("")
|
||||
} else {
|
||||
let seeders = seeders.clone().unwrap().as_u64().unwrap();
|
||||
let leechers = leechers.clone().unwrap().as_u64().unwrap();
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(
|
||||
&mut app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_details_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.movie_releases,
|
||||
),
|
||||
wrapped_content: None,
|
||||
constraints: vec![
|
||||
Constraint::Length(9),
|
||||
Constraint::Length(10),
|
||||
Constraint::Length(5),
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Length(12),
|
||||
Constraint::Length(12),
|
||||
Constraint::Percentage(7),
|
||||
Constraint::Percentage(10),
|
||||
],
|
||||
table_headers: table_headers_vec.iter().map(|s| &**s).collect(),
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.movie_info_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|release| {
|
||||
let Release {
|
||||
protocol,
|
||||
age,
|
||||
title,
|
||||
indexer,
|
||||
size,
|
||||
rejected,
|
||||
decorate_peer_style(
|
||||
seeders,
|
||||
leechers,
|
||||
languages,
|
||||
quality,
|
||||
..
|
||||
} = release;
|
||||
let age = format!("{age} days");
|
||||
title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 30),
|
||||
current_selection == *release
|
||||
&& current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(),
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let size = convert_to_gb(*size);
|
||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||
let peers = if seeders.is_none() || leechers.is_none() {
|
||||
Text::from("")
|
||||
} else {
|
||||
let seeders = seeders.clone().unwrap().as_u64().unwrap();
|
||||
let leechers = leechers.clone().unwrap().as_u64().unwrap();
|
||||
Text::from(format!("{seeders} / {leechers}")),
|
||||
)
|
||||
};
|
||||
|
||||
decorate_peer_style(
|
||||
seeders,
|
||||
leechers,
|
||||
Text::from(format!("{seeders} / {leechers}")),
|
||||
)
|
||||
};
|
||||
let language = if languages.is_some() {
|
||||
languages.clone().unwrap()[0].name.clone()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let quality = quality.quality.name.clone();
|
||||
|
||||
let language = if languages.is_some() {
|
||||
languages.clone().unwrap()[0].name.clone()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let quality = quality.quality.name.clone();
|
||||
Row::new(vec![
|
||||
Cell::from(protocol.clone()),
|
||||
Cell::from(age),
|
||||
Cell::from(rejected_str),
|
||||
Cell::from(title.to_string()),
|
||||
Cell::from(indexer.clone()),
|
||||
Cell::from(format!("{size:.1} GB")),
|
||||
Cell::from(peers),
|
||||
Cell::from(language),
|
||||
Cell::from(quality),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let releases_table = ManagarrTable::new(content, releases_row_mapping)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading || is_empty)
|
||||
.footer(help_footer)
|
||||
.headers(table_headers_vec.iter().map(|s| &**s))
|
||||
.constraints([
|
||||
Constraint::Length(9),
|
||||
Constraint::Length(10),
|
||||
Constraint::Length(5),
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Length(12),
|
||||
Constraint::Length(12),
|
||||
Constraint::Percentage(7),
|
||||
Constraint::Percentage(10),
|
||||
]);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(protocol.clone()),
|
||||
Cell::from(age),
|
||||
Cell::from(rejected_str),
|
||||
Cell::from(title.to_string()),
|
||||
Cell::from(indexer.clone()),
|
||||
Cell::from(format!("{size:.1} GB")),
|
||||
Cell::from(peers),
|
||||
Cell::from(language),
|
||||
Cell::from(quality),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading || is_empty,
|
||||
true,
|
||||
);
|
||||
f.render_widget(releases_table, area);
|
||||
}
|
||||
|
||||
fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::models::radarr_models::{DiskSpace, DownloadRecord, Movie, RootFolder}
|
||||
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
|
||||
use crate::models::Route;
|
||||
use crate::ui::draw_tabs;
|
||||
use crate::ui::loading;
|
||||
use crate::ui::radarr_ui::collections::CollectionsUi;
|
||||
use crate::ui::radarr_ui::downloads::DownloadsUi;
|
||||
use crate::ui::radarr_ui::indexers::IndexersUi;
|
||||
@@ -24,6 +23,7 @@ use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, layout_block, line_gauge_with_label, line_gauge_with_title, title_block,
|
||||
};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::DrawUi;
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
@@ -163,7 +163,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
)
|
||||
}
|
||||
} else {
|
||||
loading(f, block, area, app.is_loading);
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ fn draw_downloads_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
f.render_widget(download_gauge, download_item_areas[i]);
|
||||
}
|
||||
} else {
|
||||
loading(f, block, area, app.is_loading);
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_F
|
||||
use crate::models::Route;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_input_box_popup, draw_popup_over, draw_prompt_box, draw_prompt_popup_over, draw_table,
|
||||
DrawUi, TableProps,
|
||||
draw_input_box_popup, draw_popup_over, draw_prompt_box, draw_prompt_popup_over, DrawUi,
|
||||
};
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
@@ -56,51 +56,50 @@ impl DrawUi for RootFoldersUi {
|
||||
}
|
||||
|
||||
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
layout_block_top_border(),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.root_folders),
|
||||
wrapped_content: None,
|
||||
table_headers: vec!["Path", "Free Space", "Unmapped Folders"],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(60),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Percentage(20),
|
||||
],
|
||||
help: app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help(),
|
||||
},
|
||||
|root_folders| {
|
||||
let RootFolder {
|
||||
path,
|
||||
free_space,
|
||||
unmapped_folders,
|
||||
..
|
||||
} = root_folders;
|
||||
let help_footer = app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help();
|
||||
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
||||
let RootFolder {
|
||||
path,
|
||||
free_space,
|
||||
unmapped_folders,
|
||||
..
|
||||
} = root_folders;
|
||||
|
||||
let space: f64 = convert_to_gb(*free_space);
|
||||
let space: f64 = convert_to_gb(*free_space);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(path.to_owned()),
|
||||
Cell::from(format!("{space:.2} GB")),
|
||||
Cell::from(
|
||||
unmapped_folders
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::new())
|
||||
.len()
|
||||
.to_string(),
|
||||
),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(path.to_owned()),
|
||||
Cell::from(format!("{space:.2} GB")),
|
||||
Cell::from(
|
||||
unmapped_folders
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::new())
|
||||
.len()
|
||||
.to_string(),
|
||||
),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
|
||||
let root_folders_table = ManagarrTable::new(
|
||||
Some(&mut app.data.radarr_data.root_folders),
|
||||
root_folders_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.footer(help_footer)
|
||||
.headers(["Path", "Free Space", "Unmapped Folders"])
|
||||
.constraints([
|
||||
Constraint::Ratio(3, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
]);
|
||||
|
||||
f.render_widget(root_folders_table, area);
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_prompt_box(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -11,13 +11,14 @@ use ratatui::{
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::Task;
|
||||
use crate::models::radarr_models::{QueueEvent, Task};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||
use crate::ui::radarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_table, ListProps, TableProps};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::ListProps;
|
||||
use crate::{
|
||||
models::Route,
|
||||
ui::{draw_list_box, utils::title_block, DrawUi},
|
||||
@@ -87,92 +88,83 @@ pub(super) fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area:
|
||||
}
|
||||
|
||||
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
title_block("Tasks"),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.tasks),
|
||||
wrapped_content: None,
|
||||
table_headers: TASK_TABLE_HEADERS.to_vec(),
|
||||
constraints: TASK_TABLE_CONSTRAINTS.to_vec(),
|
||||
help: None,
|
||||
},
|
||||
|task| {
|
||||
let task_props = extract_task_props(task);
|
||||
let tasks_row_mapping = |task: &Task| {
|
||||
let task_props = extract_task_props(task);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
||||
.block(title_block("Tasks"))
|
||||
.loading(app.is_loading)
|
||||
.highlight_rows(false)
|
||||
.headers(TASK_TABLE_HEADERS)
|
||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||
|
||||
f.render_widget(tasks_table, area);
|
||||
}
|
||||
|
||||
pub(super) fn draw_queued_events(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
draw_table(
|
||||
f,
|
||||
area,
|
||||
title_block("Queued Events"),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.queued_events),
|
||||
wrapped_content: None,
|
||||
table_headers: vec!["Trigger", "Status", "Name", "Queued", "Started", "Duration"],
|
||||
constraints: vec![
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(16),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(14),
|
||||
],
|
||||
help: None,
|
||||
},
|
||||
|event| {
|
||||
let queued = convert_to_minutes_hours_days(Utc::now().sub(event.queued).num_minutes());
|
||||
let queued_string = if queued != "now" {
|
||||
format!("{queued} ago")
|
||||
} else {
|
||||
queued
|
||||
};
|
||||
let started_string = if event.started.is_some() {
|
||||
let started =
|
||||
convert_to_minutes_hours_days(Utc::now().sub(event.started.unwrap()).num_minutes());
|
||||
let events_row_mapping = |event: &QueueEvent| {
|
||||
let queued = convert_to_minutes_hours_days(Utc::now().sub(event.queued).num_minutes());
|
||||
let queued_string = if queued != "now" {
|
||||
format!("{queued} ago")
|
||||
} else {
|
||||
queued
|
||||
};
|
||||
let started_string = if event.started.is_some() {
|
||||
let started =
|
||||
convert_to_minutes_hours_days(Utc::now().sub(event.started.unwrap()).num_minutes());
|
||||
|
||||
if started != "now" {
|
||||
format!("{started} ago")
|
||||
} else {
|
||||
started
|
||||
}
|
||||
if started != "now" {
|
||||
format!("{started} ago")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
started
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let duration = if event.duration.is_some() {
|
||||
&event.duration.as_ref().unwrap()[..8]
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let duration = if event.duration.is_some() {
|
||||
&event.duration.as_ref().unwrap()[..8]
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(event.trigger.clone()),
|
||||
Cell::from(event.status.clone()),
|
||||
Cell::from(event.command_name.clone()),
|
||||
Cell::from(queued_string),
|
||||
Cell::from(started_string),
|
||||
Cell::from(duration.to_owned()),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
);
|
||||
Row::new(vec![
|
||||
Cell::from(event.trigger.clone()),
|
||||
Cell::from(event.status.clone()),
|
||||
Cell::from(event.command_name.clone()),
|
||||
Cell::from(queued_string),
|
||||
Cell::from(started_string),
|
||||
Cell::from(duration.to_owned()),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let events_table = ManagarrTable::new(
|
||||
Some(&mut app.data.radarr_data.queued_events),
|
||||
events_row_mapping,
|
||||
)
|
||||
.block(title_block("Queued Events"))
|
||||
.loading(app.is_loading)
|
||||
.highlight_rows(false)
|
||||
.headers(["Trigger", "Status", "Name", "Queued", "Started", "Duration"])
|
||||
.constraints([
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(16),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(14),
|
||||
]);
|
||||
|
||||
f.render_widget(events_table, area);
|
||||
}
|
||||
|
||||
fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::layout::{Alignment, Rect};
|
||||
use ratatui::text::{Span, Text};
|
||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||
use ratatui::Frame;
|
||||
@@ -6,6 +6,7 @@ use ratatui::Frame;
|
||||
use crate::app::context_clues::{build_context_clue_string, BARE_POPUP_CONTEXT_CLUES};
|
||||
use crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES;
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::Task;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::style_log_list_item;
|
||||
@@ -15,10 +16,11 @@ use crate::ui::radarr_ui::system::{
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, title_block};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::{
|
||||
draw_help_footer_and_get_content_area, draw_large_popup_over, draw_list_box,
|
||||
draw_medium_popup_over, draw_prompt_box, draw_prompt_popup_over, draw_table, loading, DrawUi,
|
||||
ListProps, TableProps,
|
||||
draw_medium_popup_over, draw_prompt_box, draw_prompt_popup_over, DrawUi, ListProps,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -82,40 +84,35 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let tasks_popup_table = |f: &mut Frame<'_>, app: &mut App<'_>, area: Rect| {
|
||||
let help_footer = Some(build_context_clue_string(&SYSTEM_TASKS_CONTEXT_CLUES));
|
||||
// let context_area = draw_help_footer_and_get_content_area(
|
||||
// f,
|
||||
// area,
|
||||
// help_footer,
|
||||
// );
|
||||
let tasks_row_mapping = |task: &Task| {
|
||||
let task_props = extract_task_props(task);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
||||
.block(borderless_block())
|
||||
.loading(app.is_loading)
|
||||
.margin(1)
|
||||
.footer(help_footer)
|
||||
.footer_alignment(Alignment::Center)
|
||||
.headers(TASK_TABLE_HEADERS)
|
||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||
|
||||
f.render_widget(title_block("Tasks"), area);
|
||||
|
||||
let context_area = draw_help_footer_and_get_content_area(
|
||||
f,
|
||||
area,
|
||||
Some(build_context_clue_string(&SYSTEM_TASKS_CONTEXT_CLUES)),
|
||||
);
|
||||
|
||||
draw_table(
|
||||
f,
|
||||
context_area,
|
||||
borderless_block(),
|
||||
TableProps {
|
||||
content: Some(&mut app.data.radarr_data.tasks),
|
||||
wrapped_content: None,
|
||||
table_headers: TASK_TABLE_HEADERS.to_vec(),
|
||||
constraints: TASK_TABLE_CONSTRAINTS.to_vec(),
|
||||
help: None,
|
||||
},
|
||||
|task| {
|
||||
let task_props = extract_task_props(task);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
)
|
||||
f.render_widget(tasks_table, area);
|
||||
};
|
||||
|
||||
if matches!(
|
||||
@@ -163,6 +160,6 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
|
||||
f.render_widget(updates_paragraph, content_area);
|
||||
} else {
|
||||
loading(f, block, content_area, app.is_loading);
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), content_area);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::prelude::Text;
|
||||
use ratatui::widgets::{Block, Paragraph, Widget};
|
||||
|
||||
pub struct LoadingBlock<'a> {
|
||||
is_loading: bool,
|
||||
block: Block<'a>,
|
||||
}
|
||||
|
||||
impl<'a> LoadingBlock<'a> {
|
||||
pub fn new(is_loading: bool, block: Block<'a>) -> Self {
|
||||
Self { is_loading, block }
|
||||
}
|
||||
|
||||
fn render_loading_block(&self, area: Rect, buf: &mut Buffer) {
|
||||
if self.is_loading {
|
||||
Paragraph::new(Text::from("\n\n Loading ...\n\n"))
|
||||
.system_function()
|
||||
.block(self.block.clone())
|
||||
.render(area, buf);
|
||||
} else {
|
||||
self.block.clone().render(area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for LoadingBlock<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_loading_block(area, buf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
use crate::models::StatefulTable;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::HIGHLIGHT_SYMBOL;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::prelude::{Style, Stylize, Text};
|
||||
use ratatui::widgets::{Block, Paragraph, Row, StatefulWidget, Table, Widget};
|
||||
|
||||
pub struct ManagarrTable<'a, T, F>
|
||||
where
|
||||
F: Fn(&T) -> Row<'a>,
|
||||
{
|
||||
content: Option<&'a mut StatefulTable<T>>,
|
||||
table_headers: Vec<Text<'a>>,
|
||||
constraints: Vec<Constraint>,
|
||||
row_mapper: F,
|
||||
footer: Option<String>,
|
||||
footer_alignment: Alignment,
|
||||
block: Block<'a>,
|
||||
margin: u16,
|
||||
is_loading: bool,
|
||||
highlight_rows: bool,
|
||||
}
|
||||
|
||||
impl<'a, T, F> ManagarrTable<'a, T, F>
|
||||
where
|
||||
F: Fn(&T) -> Row<'a>,
|
||||
{
|
||||
pub fn new(content: Option<&'a mut StatefulTable<T>>, row_mapper: F) -> Self {
|
||||
Self {
|
||||
content,
|
||||
table_headers: Vec::new(),
|
||||
constraints: Vec::new(),
|
||||
row_mapper,
|
||||
footer: None,
|
||||
footer_alignment: Alignment::Left,
|
||||
block: Block::new(),
|
||||
margin: 0,
|
||||
is_loading: false,
|
||||
highlight_rows: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn headers<I>(mut self, headers: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<Text<'a>>,
|
||||
{
|
||||
self.table_headers = headers.into_iter().map(Into::into).collect();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn constraints<I>(mut self, constraints: I) -> Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<Constraint>,
|
||||
{
|
||||
self.constraints = constraints.into_iter().map(Into::into).collect();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer(mut self, footer: Option<String>) -> Self {
|
||||
self.footer = footer;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer_alignment(mut self, alignment: Alignment) -> Self {
|
||||
self.footer_alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn block(mut self, block: Block<'a>) -> Self {
|
||||
self.block = block;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn margin(mut self, margin: u16) -> Self {
|
||||
self.margin = margin;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn loading(mut self, is_loading: bool) -> Self {
|
||||
self.is_loading = is_loading;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn highlight_rows(mut self, hightlight_rows: bool) -> Self {
|
||||
self.highlight_rows = hightlight_rows;
|
||||
self
|
||||
}
|
||||
|
||||
fn render_table(&mut self, area: Rect, buf: &mut Buffer) {
|
||||
let table_area = if let Some(ref footer) = self.footer {
|
||||
let [content_area, footer_area] =
|
||||
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
|
||||
.margin(self.margin)
|
||||
.areas(area);
|
||||
|
||||
Paragraph::new(Text::from(format!(" {footer}").help()))
|
||||
.block(layout_block_top_border())
|
||||
.alignment(self.footer_alignment)
|
||||
.render(footer_area, buf);
|
||||
|
||||
content_area
|
||||
} else {
|
||||
area
|
||||
};
|
||||
let loading_block = LoadingBlock::new(self.is_loading, self.block.clone());
|
||||
|
||||
if let Some(ref mut content) = self.content {
|
||||
if !content.items.is_empty() {
|
||||
let rows = content.items.iter().map(&self.row_mapper);
|
||||
|
||||
let headers = Row::new(self.table_headers.clone())
|
||||
.default()
|
||||
.bold()
|
||||
.bottom_margin(0);
|
||||
|
||||
let mut table = Table::new(rows, &self.constraints)
|
||||
.header(headers)
|
||||
.block(self.block.clone());
|
||||
|
||||
if self.highlight_rows {
|
||||
table = table
|
||||
.highlight_style(Style::new().highlight())
|
||||
.highlight_symbol(HIGHLIGHT_SYMBOL);
|
||||
}
|
||||
|
||||
StatefulWidget::render(table, table_area, buf, &mut content.state);
|
||||
} else {
|
||||
loading_block.render(table_area, buf);
|
||||
}
|
||||
} else {
|
||||
loading_block.render(table_area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, F> Widget for ManagarrTable<'a, T, F>
|
||||
where
|
||||
F: Fn(&T) -> Row<'a>,
|
||||
{
|
||||
fn render(mut self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_table(area, buf);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub(super) mod button;
|
||||
pub(super) mod input_box;
|
||||
pub(super) mod checkbox;
|
||||
pub(super) mod input_box;
|
||||
pub(super) mod loading_block;
|
||||
pub(super) mod managarr_table;
|
||||
|
||||
Reference in New Issue
Block a user