chore: merge release-v0.1.6 into main
This commit is contained in:
+10
-11
@@ -1,21 +1,20 @@
|
|||||||
### 2022-10-07
|
### 2022-10-16
|
||||||
|
|
||||||
|
|
||||||
### Chores
|
### Chores
|
||||||
+ Update clap to v4, [15597dbe6942ec053541398ce0e9dedc10a4d3ea]
|
+ Cargo update, [c3e72ae7369a25d903f39e55a4349cb005671dd4]
|
||||||
|
+ create_release.sh v0.1.0, [3c8d59c666bd4cda9ca54989b2f1b48bba17bc57]
|
||||||
|
+ uuid updated to version 1.2, [438ad770f4a5ecb5f4bbc308066ad9e808f66514]
|
||||||
|
|
||||||
### Docs
|
### Fixes
|
||||||
+ readme.md updated, [a05bf561cc6d96237f683ab0b3c782d6841974d9]
|
+ loading icon shifting error fix, also make icon white, closes #15, [59797685dffa29752a48c98e6cf465884d6d9df6]
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
+ use newtype construct for container id, [41cbb84f2896f8be2c37eba87e390d998aff7382]
|
+ Show container name in log panel title, closes #16, [9cb0c414afc284947fc2b8494504387e4e7edd87]
|
||||||
|
+ use gui_state HashSet to keep track of application gui state, [9e9d51559a13944622abf4fcbd3bd63766d11467]
|
||||||
|
+ terminal.clear() after run_app finished, [67c49575682cb271fac0998ff377a6504cd0bc86]
|
||||||
|
|
||||||
### Refactors
|
### Refactors
|
||||||
+ Impl Copy where able to, [e76878f424d72b943713ef84e95e25fada77d79e]
|
+ CpuStats & MemStats use tuple struct, [a060d032586a0707ac91cb13d922aae0850449c5]
|
||||||
+ replace async fn with just fn, [17dc604befac75cb9dc0311a0e43f9927fe0ca30]
|
|
||||||
+ remove pointless clone()'s & variable declarations, [6731002ee42c9460042c2c38aff5101b1bcebbe6]
|
|
||||||
+ replace String::from("") with String::new(), [62fb22478697cc9a7ab9fb562a724965b437233a]
|
|
||||||
+ replace map_or_else with map_or, [3e26f292c7dc5e13af4580952767ebe821aa5183], [5660b34d5149dce27706ff6daa90b854e6f84e14]
|
|
||||||
|
|
||||||
|
|
||||||
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
|
||||||
|
|||||||
+23
-5
@@ -1,7 +1,25 @@
|
|||||||
|
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.1.6'>v0.1.6</a>
|
||||||
|
### 2022-10-16
|
||||||
|
|
||||||
|
### Chores
|
||||||
|
+ Cargo update, [c3e72ae7](https://github.com/mrjackwills/oxker/commit/c3e72ae7369a25d903f39e55a4349cb005671dd4),
|
||||||
|
+ create_release.sh v0.1.0, [3c8d59c6](https://github.com/mrjackwills/oxker/commit/3c8d59c666bd4cda9ca54989b2f1b48bba17bc57),
|
||||||
|
+ uuid updated to version 1.2, [438ad770](https://github.com/mrjackwills/oxker/commit/438ad770f4a5ecb5f4bbc308066ad9e808f66514),
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
+ loading icon shifting error fix, also make icon white, closes [#15](https://github.com/mrjackwills/oxker/issues/15), [59797685](https://github.com/mrjackwills/oxker/commit/59797685dffa29752a48c98e6cf465884d6d9df6),
|
||||||
|
|
||||||
|
### Features
|
||||||
|
+ Show container name in log panel title, closes [#16](https://github.com/mrjackwills/oxker/issues/16), [9cb0c414](https://github.com/mrjackwills/oxker/commit/9cb0c414afc284947fc2b8494504387e4e7edd87),
|
||||||
|
+ use gui_state HashSet to keep track of application gui state, [9e9d5155](https://github.com/mrjackwills/oxker/commit/9e9d51559a13944622abf4fcbd3bd63766d11467),
|
||||||
|
+ terminal.clear() after run_app finished, [67c49575](https://github.com/mrjackwills/oxker/commit/67c49575682cb271fac0998ff377a6504cd0bc86),
|
||||||
|
|
||||||
|
### Refactors
|
||||||
|
+ CpuStats & MemStats use tuple struct, [a060d032](https://github.com/mrjackwills/oxker/commit/a060d032586a0707ac91cb13d922aae0850449c5),
|
||||||
|
|
||||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.1.5'>v0.1.5</a>
|
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.1.5'>v0.1.5</a>
|
||||||
### 2022-10-07
|
### 2022-10-07
|
||||||
|
|
||||||
|
|
||||||
### Chores
|
### Chores
|
||||||
+ Update clap to v4, [15597dbe](https://github.com/mrjackwills/oxker/commit/15597dbe6942ec053541398ce0e9dedc10a4d3ea),
|
+ Update clap to v4, [15597dbe](https://github.com/mrjackwills/oxker/commit/15597dbe6942ec053541398ce0e9dedc10a4d3ea),
|
||||||
|
|
||||||
@@ -16,13 +34,13 @@
|
|||||||
+ replace async fn with just fn, [17dc604b](https://github.com/mrjackwills/oxker/commit/17dc604befac75cb9dc0311a0e43f9927fe0ca30),
|
+ replace async fn with just fn, [17dc604b](https://github.com/mrjackwills/oxker/commit/17dc604befac75cb9dc0311a0e43f9927fe0ca30),
|
||||||
+ remove pointless clone()'s & variable declarations, [6731002e](https://github.com/mrjackwills/oxker/commit/6731002ee42c9460042c2c38aff5101b1bcebbe6),
|
+ remove pointless clone()'s & variable declarations, [6731002e](https://github.com/mrjackwills/oxker/commit/6731002ee42c9460042c2c38aff5101b1bcebbe6),
|
||||||
+ replace String::from("") with String::new(), [62fb2247](https://github.com/mrjackwills/oxker/commit/62fb22478697cc9a7ab9fb562a724965b437233a),
|
+ replace String::from("") with String::new(), [62fb2247](https://github.com/mrjackwills/oxker/commit/62fb22478697cc9a7ab9fb562a724965b437233a),
|
||||||
+ replace map_or_else with map_or, [3e26f292](https://github.com/mrjackwills/oxker/commit/3e26f292c7dc5e13af4580952767ebe821aa5183),, [5660b34d](https://github.com/mrjackwills/oxker/commit/5660b34d5149dce27706ff6daa90b854e6f84e14),
|
+ replace map_or_else with map_or, [3e26f292](https://github.com/mrjackwills/oxker/commit/3e26f292c7dc5e13af4580952767ebe821aa5183), [5660b34d](https://github.com/mrjackwills/oxker/commit/5660b34d5149dce27706ff6daa90b854e6f84e14),
|
||||||
|
|
||||||
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.1.4'>v0.1.4</a>
|
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.1.4'>v0.1.4</a>
|
||||||
### 2022-09-07
|
### 2022-09-07
|
||||||
|
|
||||||
### Chores
|
### Chores
|
||||||
+ dependencies updated, [a3168daa](https://github.com/mrjackwills/oxker/commit/a3168daa3f769a6747dfbe61103073a7e80a1485),, [78e59160](https://github.com/mrjackwills/oxker/commit/78e59160bb6a978ee80e3a99eb72f051fb64e737),
|
+ dependencies updated, [a3168daa](https://github.com/mrjackwills/oxker/commit/a3168daa3f769a6747dfbe61103073a7e80a1485),[78e59160](https://github.com/mrjackwills/oxker/commit/78e59160bb6a978ee80e3a99eb72f051fb64e737),
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
+ containerize self, github action to build and push to [Docker Hub](https://hub.docker.com/r/mrjackwills/oxker), [07f97202](https://github.com/mrjackwills/oxker/commit/07f972022a69f22bac57925e6ad84234381f7890),
|
+ containerize self, github action to build and push to [Docker Hub](https://hub.docker.com/r/mrjackwills/oxker), [07f97202](https://github.com/mrjackwills/oxker/commit/07f972022a69f22bac57925e6ad84234381f7890),
|
||||||
@@ -138,9 +156,9 @@
|
|||||||
### 2022-04-29
|
### 2022-04-29
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
+ allow toggling of mouse caputre, to select & copy text with mouse, closes #2, [aec184ea](https://github.com/mrjackwills/oxker/commit/aec184ea22b289e91942a4c3e6a415685884bc47),
|
+ allow toggling of mouse caputre, to select & copy text with mouse, closes [#2](https://github.com/mrjackwills/oxker/issues/2), [aec184ea](https://github.com/mrjackwills/oxker/commit/aec184ea22b289e91942a4c3e6a415685884bc47),
|
||||||
+ show id column, [b10f9274](https://github.com/mrjackwills/oxker/commit/b10f927481c9e38a48c1d4b94e744ec48e8b6ba6),
|
+ show id column, [b10f9274](https://github.com/mrjackwills/oxker/commit/b10f927481c9e38a48c1d4b94e744ec48e8b6ba6),
|
||||||
+ draw_popup, using enum to draw in one of 9 areas, closes #6, [1017850a](https://github.com/mrjackwills/oxker/commit/1017850a6cc91328abc1127bdb117495f5e909d8),
|
+ draw_popup, using enum to draw in one of 9 areas, closes [#6](https://github.com/mrjackwills/oxker/issues/6), [1017850a](https://github.com/mrjackwills/oxker/commit/1017850a6cc91328abc1127bdb117495f5e909d8),
|
||||||
+ use a message rx/sx for all docker commands, remove update loop, wait for update message from gui instead, [9b70fdfa](https://github.com/mrjackwills/oxker/commit/9b70fdfad7b38361ebee301bdc2545d3f0dfcf9e),
|
+ use a message rx/sx for all docker commands, remove update loop, wait for update message from gui instead, [9b70fdfa](https://github.com/mrjackwills/oxker/commit/9b70fdfad7b38361ebee301bdc2545d3f0dfcf9e),
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
Generated
+15
-15
@@ -101,9 +101,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.0.10"
|
version = "4.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b1a0a4208c6c483b952ad35c6eed505fc13b46f08f631b81e828084a9318d74"
|
checksum = "6bf8832993da70a4c6d13c581f4463c2bdda27b9bf1c5498dc4365543abe6d6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@@ -118,9 +118,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.0.10"
|
version = "4.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db342ce9fda24fb191e2ed4e102055a4d381c1086a06630174cd8da8d5d917ce"
|
checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
@@ -433,9 +433,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.134"
|
version = "0.2.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@@ -514,7 +514,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxker"
|
name = "oxker"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bollard",
|
"bollard",
|
||||||
@@ -623,9 +623,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.46"
|
version = "1.0.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -712,9 +712,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.85"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@@ -1034,9 +1034,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
@@ -1072,9 +1072,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.1.2"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"rand",
|
"rand",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "oxker"
|
name = "oxker"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Jack Wills <email@mrjackwills.com>"]
|
authors = ["Jack Wills <email@mrjackwills.com>"]
|
||||||
description = "A simple tui to view & control docker containers"
|
description = "A simple tui to view & control docker containers"
|
||||||
@@ -23,7 +23,7 @@ tokio = {version = "1.21", features=["full"]}
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
tui = "0.19"
|
tui = "0.19"
|
||||||
uuid = {version = "1.1", features = ["v4", "fast-rng"]}
|
uuid = {version = "1.2", features = ["v4", "fast-rng"]}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
case $TARGETARCH in
|
case $TARGETARCH in
|
||||||
|
|
||||||
"amd64")
|
"amd64")
|
||||||
echo "x86_64-unknown-linux-musl" > /.platform
|
echo "x86_64-unknown-linux-musl" > /.platform
|
||||||
echo "" > /.compiler
|
echo "" > /.compiler
|
||||||
;;
|
;;
|
||||||
"arm64")
|
"arm64")
|
||||||
echo "aarch64-unknown-linux-musl" > /.platform
|
echo "aarch64-unknown-linux-musl" > /.platform
|
||||||
echo "gcc-aarch64-linux-gnu" > /.compiler
|
echo "gcc-aarch64-linux-gnu" > /.compiler
|
||||||
;;
|
;;
|
||||||
"arm")
|
"arm")
|
||||||
echo "arm-unknown-linux-musleabihf" > /.platform
|
echo "arm-unknown-linux-musleabihf" > /.platform
|
||||||
echo "gcc-arm-linux-gnueabihf" > /.compiler
|
echo "gcc-arm-linux-gnueabihf" > /.compiler
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
+42
-16
@@ -1,9 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# rust create_release
|
# rust create_release
|
||||||
# v0.0.15
|
# v0.1.2
|
||||||
|
|
||||||
PACKAGE_NAME='oxker'
|
|
||||||
STAR_LINE='****************************************'
|
STAR_LINE='****************************************'
|
||||||
CWD=$(pwd)
|
CWD=$(pwd)
|
||||||
|
|
||||||
@@ -20,11 +19,6 @@ error_close() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -z "$PACKAGE_NAME" ]
|
|
||||||
then
|
|
||||||
error_close "No package name"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# $1 string - question to ask
|
# $1 string - question to ask
|
||||||
ask_yn () {
|
ask_yn () {
|
||||||
printf "%b%s? [y/N]:%b " "${GREEN}" "$1" "${RESET}"
|
printf "%b%s? [y/N]:%b " "${GREEN}" "$1" "${RESET}"
|
||||||
@@ -114,8 +108,8 @@ update_release_body_and_changelog () {
|
|||||||
sed -i -E "s=(\s)\[([0-9a-f]{8})([0-9a-f]{32})\]= [\2](${GIT_REPO_URL}/commit/\2\3),=g" ./CHANGELOG.md
|
sed -i -E "s=(\s)\[([0-9a-f]{8})([0-9a-f]{32})\]= [\2](${GIT_REPO_URL}/commit/\2\3),=g" ./CHANGELOG.md
|
||||||
|
|
||||||
# Update changelog to add links to closed issues - comma included!
|
# Update changelog to add links to closed issues - comma included!
|
||||||
# "closes [#1]," -> "closes [#1](https:/www.../issues/1),""
|
# "closes #1," -> "closes [#1](https:/www.../issues/1),""
|
||||||
sed -i -r -E "s=closes \[#([0-9]+)\],=closes [#\1](${GIT_REPO_URL}/issues/\1),=g" ./CHANGELOG.md
|
sed -i -r -E "s=closes \#([0-9]+)\,=closes [#\1](${GIT_REPO_URL}/issues/\1),=g" ./CHANGELOG.md
|
||||||
}
|
}
|
||||||
|
|
||||||
# update version in cargo.toml, to match selected current version
|
# update version in cargo.toml, to match selected current version
|
||||||
@@ -193,34 +187,66 @@ cargo_build () {
|
|||||||
ask_continue
|
ask_continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# $1 text to colourise
|
||||||
|
release_continue () {
|
||||||
|
echo -e "\n${PURPLE}$1${RESET}"
|
||||||
|
ask_continue
|
||||||
|
|
||||||
|
}
|
||||||
# Full flow to create a new release
|
# Full flow to create a new release
|
||||||
release_flow() {
|
release_flow() {
|
||||||
check_git
|
check_git
|
||||||
get_git_remote_url
|
get_git_remote_url
|
||||||
cargo_test
|
cargo_test
|
||||||
cargo_build
|
cargo_build
|
||||||
|
|
||||||
cd "${CWD}" || error_close "Can't find ${CWD}"
|
cd "${CWD}" || error_close "Can't find ${CWD}"
|
||||||
check_tag
|
check_tag
|
||||||
|
|
||||||
NEW_TAG_WITH_V="v${MAJOR}.${MINOR}.${PATCH}"
|
NEW_TAG_WITH_V="v${MAJOR}.${MINOR}.${PATCH}"
|
||||||
printf "\nnew tag chosen: %s\n\n" "${NEW_TAG_WITH_V}"
|
printf "\nnew tag chosen: %s\n\n" "${NEW_TAG_WITH_V}"
|
||||||
|
|
||||||
RELEASE_BRANCH=release-$NEW_TAG_WITH_V
|
RELEASE_BRANCH=release-$NEW_TAG_WITH_V
|
||||||
echo -e
|
echo -e
|
||||||
ask_changelog_update
|
ask_changelog_update
|
||||||
git checkout -b "$RELEASE_BRANCH"
|
|
||||||
update_version_number_in_files
|
|
||||||
cargo fmt
|
|
||||||
git add .
|
|
||||||
git commit -m "chore: release $NEW_TAG_WITH_V"
|
|
||||||
|
|
||||||
|
release_continue "checkout ${RELEASE_BRANCH}"
|
||||||
|
git checkout -b "$RELEASE_BRANCH"
|
||||||
|
|
||||||
|
release_continue "update_version_number_in_files"
|
||||||
|
update_version_number_in_files
|
||||||
|
|
||||||
|
echo -e "\ncargo fmt"
|
||||||
|
cargo fmt
|
||||||
|
|
||||||
|
release_continue "git add ."
|
||||||
|
git add .
|
||||||
|
|
||||||
|
release_continue "git commit -m \"chore: release \"${NEW_TAG_WITH_V}\""
|
||||||
|
git commit -m "chore: release ${NEW_TAG_WITH_V}"
|
||||||
|
|
||||||
|
release_continue "git checkout main"
|
||||||
git checkout main
|
git checkout main
|
||||||
|
|
||||||
|
release_continue "git merge --no-ff \"${RELEASE_BRANCH}\" -m \"chore: merge ${RELEASE_BRANCH} into main\""
|
||||||
git merge --no-ff "$RELEASE_BRANCH" -m "chore: merge ${RELEASE_BRANCH} into main"
|
git merge --no-ff "$RELEASE_BRANCH" -m "chore: merge ${RELEASE_BRANCH} into main"
|
||||||
|
|
||||||
|
release_continue "git tag -am \"${RELEASE_BRANCH}\" \"$NEW_TAG_WITH_V\""
|
||||||
git tag -am "${RELEASE_BRANCH}" "$NEW_TAG_WITH_V"
|
git tag -am "${RELEASE_BRANCH}" "$NEW_TAG_WITH_V"
|
||||||
echo "git tag -am \"${RELEASE_BRANCH}\" \"$NEW_TAG_WITH_V\""
|
|
||||||
|
release_continue "git push --atomic origin main \"$NEW_TAG_WITH_V\""
|
||||||
git push --atomic origin main "$NEW_TAG_WITH_V"
|
git push --atomic origin main "$NEW_TAG_WITH_V"
|
||||||
|
|
||||||
|
release_continue "git checkout dev"
|
||||||
git checkout dev
|
git checkout dev
|
||||||
git merge --no-ff main -m 'chore: merge main into dev'
|
|
||||||
|
release_continue "git merge --no-ff main -m \"chore: merge main into dev\""
|
||||||
|
git merge --no-ff main -m "chore: merge main into dev"
|
||||||
|
|
||||||
|
release_continue "git push origin dev"
|
||||||
git push origin dev
|
git push origin dev
|
||||||
|
|
||||||
|
release_continue "git branch -d \"$RELEASE_BRANCH\""
|
||||||
git branch -d "$RELEASE_BRANCH"
|
git branch -d "$RELEASE_BRANCH"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ impl<T> StatefulList<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the current status of the select list, e.g. 2/5,
|
||||||
pub fn get_state_title(&self) -> String {
|
pub fn get_state_title(&self) -> String {
|
||||||
if self.items.is_empty() {
|
if self.items.is_empty() {
|
||||||
String::new()
|
String::new()
|
||||||
@@ -254,13 +255,11 @@ pub trait Stats {
|
|||||||
/// So can use custom display formatter
|
/// So can use custom display formatter
|
||||||
/// Use trait Stats for use as generic in draw_chart function
|
/// Use trait Stats for use as generic in draw_chart function
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct CpuStats {
|
pub struct CpuStats(f64);
|
||||||
value: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuStats {
|
impl CpuStats {
|
||||||
pub const fn new(value: f64) -> Self {
|
pub const fn new(value: f64) -> Self {
|
||||||
Self { value }
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,21 +267,21 @@ impl Eq for CpuStats {}
|
|||||||
|
|
||||||
impl PartialEq for CpuStats {
|
impl PartialEq for CpuStats {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.value == other.value
|
self.0 == other.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for CpuStats {
|
impl PartialOrd for CpuStats {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
self.value.partial_cmp(&other.value)
|
self.0.partial_cmp(&other.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for CpuStats {
|
impl Ord for CpuStats {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
if self.value > other.value {
|
if self.0 > other.0 {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
} else if (self.value - other.value).abs() < 0.01 {
|
} else if (self.0 - other.0).abs() < 0.01 {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else {
|
} else {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
@@ -292,13 +291,13 @@ impl Ord for CpuStats {
|
|||||||
|
|
||||||
impl Stats for CpuStats {
|
impl Stats for CpuStats {
|
||||||
fn get_value(&self) -> f64 {
|
fn get_value(&self) -> f64 {
|
||||||
self.value
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CpuStats {
|
impl fmt::Display for CpuStats {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let disp = format!("{:05.2}%", self.value);
|
let disp = format!("{:05.2}%", self.0);
|
||||||
write!(f, "{:>x$}", disp, x = f.width().unwrap_or(1))
|
write!(f, "{:>x$}", disp, x = f.width().unwrap_or(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,41 +306,39 @@ impl fmt::Display for CpuStats {
|
|||||||
/// So can use custom display formatter
|
/// So can use custom display formatter
|
||||||
/// Use trait Stats for use as generic in draw_chart function
|
/// Use trait Stats for use as generic in draw_chart function
|
||||||
#[derive(Debug, Default, Clone, Copy, Eq)]
|
#[derive(Debug, Default, Clone, Copy, Eq)]
|
||||||
pub struct ByteStats {
|
pub struct ByteStats(u64);
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for ByteStats {
|
impl PartialEq for ByteStats {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.value == other.value
|
self.0 == other.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for ByteStats {
|
impl PartialOrd for ByteStats {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
self.value.partial_cmp(&other.value)
|
self.0.partial_cmp(&other.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for ByteStats {
|
impl Ord for ByteStats {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.value.cmp(&other.value)
|
self.0.cmp(&other.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ByteStats {
|
impl ByteStats {
|
||||||
pub const fn new(value: u64) -> Self {
|
pub const fn new(value: u64) -> Self {
|
||||||
Self { value }
|
Self(value)
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, value: u64) {
|
pub fn update(&mut self, value: u64) {
|
||||||
self.value = value;
|
self.0 = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
impl Stats for ByteStats {
|
impl Stats for ByteStats {
|
||||||
fn get_value(&self) -> f64 {
|
fn get_value(&self) -> f64 {
|
||||||
self.value as f64
|
self.0 as f64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,7 +350,7 @@ impl fmt::Display for ByteStats {
|
|||||||
x if x >= ONE_GB => format!("{y:.2} GB", y = as_f64 / ONE_GB),
|
x if x >= ONE_GB => format!("{y:.2} GB", y = as_f64 / ONE_GB),
|
||||||
x if x >= ONE_MB => format!("{y:.2} MB", y = as_f64 / ONE_MB),
|
x if x >= ONE_MB => format!("{y:.2} MB", y = as_f64 / ONE_MB),
|
||||||
x if x >= ONE_KB => format!("{y:.2} kB", y = as_f64 / ONE_KB),
|
x if x >= ONE_KB => format!("{y:.2} kB", y = as_f64 / ONE_KB),
|
||||||
_ => format!("{} B", self.value),
|
_ => format!("{} B", self.0),
|
||||||
};
|
};
|
||||||
write!(f, "{:>x$}", p, x = f.width().unwrap_or(1))
|
write!(f, "{:>x$}", p, x = f.width().unwrap_or(1))
|
||||||
}
|
}
|
||||||
@@ -426,7 +423,7 @@ impl ContainerItem {
|
|||||||
self.cpu_stats
|
self.cpu_stats
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|i| (i.0 as f64, i.1.value as f64))
|
.map(|i| (i.0 as f64, i.1 .0 as f64))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +433,7 @@ impl ContainerItem {
|
|||||||
self.mem_stats
|
self.mem_stats
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|i| (i.0 as f64, i.1.value as f64))
|
.map(|i| (i.0 as f64, i.1 .0 as f64))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-7
@@ -14,10 +14,8 @@ pub struct AppData {
|
|||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
error: Option<AppError>,
|
error: Option<AppError>,
|
||||||
logs_parsed: bool,
|
logs_parsed: bool,
|
||||||
pub containers: StatefulList<ContainerItem>,
|
|
||||||
pub init: bool,
|
|
||||||
pub show_error: bool,
|
|
||||||
sorted_by: Option<(Header, SortedOrder)>,
|
sorted_by: Option<(Header, SortedOrder)>,
|
||||||
|
pub containers: StatefulList<ContainerItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
@@ -79,9 +77,7 @@ impl AppData {
|
|||||||
args,
|
args,
|
||||||
containers: StatefulList::new(vec![]),
|
containers: StatefulList::new(vec![]),
|
||||||
error: None,
|
error: None,
|
||||||
init: false,
|
|
||||||
logs_parsed: false,
|
logs_parsed: false,
|
||||||
show_error: false,
|
|
||||||
sorted_by: None,
|
sorted_by: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,11 +253,20 @@ impl AppData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the title for log panel for selected container
|
/// Get the title for log panel for selected container
|
||||||
/// will be "logs x/x"
|
/// will be either
|
||||||
|
/// 1) "logs x/x - container_name" where container_name is 32 chars max
|
||||||
|
/// 2) "logs - container_name" when no logs found, again 32 chars max
|
||||||
pub fn get_log_title(&self) -> String {
|
pub fn get_log_title(&self) -> String {
|
||||||
self.get_selected_log_index()
|
self.get_selected_log_index()
|
||||||
.map_or("".to_owned(), |index| {
|
.map_or("".to_owned(), |index| {
|
||||||
self.containers.items[index].logs.get_state_title()
|
let logs_len = self.containers.items[index].logs.get_state_title();
|
||||||
|
let mut name = self.containers.items[index].name.clone();
|
||||||
|
name.truncate(32);
|
||||||
|
if logs_len.is_empty() {
|
||||||
|
format!("- {} ", name)
|
||||||
|
} else {
|
||||||
|
format!("{} - {}", logs_len, name)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-45
@@ -3,7 +3,7 @@ use bollard::{
|
|||||||
service::ContainerSummary,
|
service::ContainerSummary,
|
||||||
Docker,
|
Docker,
|
||||||
};
|
};
|
||||||
use futures_util::StreamExt;
|
use futures_util::{Future, StreamExt};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
app_data::{AppData, ContainerId, DockerControls},
|
app_data::{AppData, ContainerId, DockerControls},
|
||||||
app_error::AppError,
|
app_error::AppError,
|
||||||
parse_args::CliArgs,
|
parse_args::CliArgs,
|
||||||
ui::GuiState,
|
ui::{GuiState, Status},
|
||||||
};
|
};
|
||||||
mod message;
|
mod message;
|
||||||
pub use message::DockerMessage;
|
pub use message::DockerMessage;
|
||||||
@@ -316,8 +316,9 @@ impl DockerData {
|
|||||||
self.gui_state.lock().remove_loading(loading_uuid);
|
self.gui_state.lock().remove_loading(loading_uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize docker container data, before any messages are received
|
/// Initialize docker container data, before any messages are received
|
||||||
async fn initialise_container_data(&mut self) {
|
async fn initialise_container_data(&mut self) {
|
||||||
|
self.gui_state.lock().status_push(Status::Init);
|
||||||
let loading_uuid = Uuid::new_v4();
|
let loading_uuid = Uuid::new_v4();
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
let loading_spin = self.loading_spin(loading_uuid).await;
|
||||||
|
|
||||||
@@ -336,65 +337,62 @@ impl DockerData {
|
|||||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||||
self.initialised = self.app_data.lock().initialised(&all_ids);
|
self.initialised = self.app_data.lock().initialised(&all_ids);
|
||||||
}
|
}
|
||||||
self.app_data.lock().init = true;
|
self.gui_state.lock().status_del(Status::Init);
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
self.stop_loading_spin(&loading_spin, loading_uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the global error as the docker error, and set gui_state to error
|
||||||
|
fn set_error(&mut self, error: DockerControls) {
|
||||||
|
self.app_data
|
||||||
|
.lock()
|
||||||
|
.set_error(AppError::DockerCommand(error));
|
||||||
|
self.gui_state.lock().status_push(Status::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a docker command, will start and stop the loading spinner, and set correct error
|
||||||
|
async fn exec_docker(
|
||||||
|
&mut self,
|
||||||
|
docker_fn: impl Future<Output = Result<(), bollard::errors::Error>> + Send,
|
||||||
|
control: DockerControls,
|
||||||
|
) {
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
let loading_spin = self.loading_spin(uuid).await;
|
||||||
|
if docker_fn.await.is_err() {
|
||||||
|
self.set_error(control);
|
||||||
|
};
|
||||||
|
self.stop_loading_spin(&loading_spin, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle incoming messages, container controls & all container information update
|
/// Handle incoming messages, container controls & all container information update
|
||||||
async fn message_handler(&mut self) {
|
async fn message_handler(&mut self) {
|
||||||
while let Some(message) = self.receiver.recv().await {
|
while let Some(message) = self.receiver.recv().await {
|
||||||
let docker = Arc::clone(&self.docker);
|
let docker = Arc::clone(&self.docker);
|
||||||
let app_data = Arc::clone(&self.app_data);
|
|
||||||
let loading_uuid = Uuid::new_v4();
|
|
||||||
match message {
|
match message {
|
||||||
DockerMessage::Pause(id) => {
|
DockerMessage::Pause(id) => {
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
self.exec_docker(docker.pause_container(id.get()), DockerControls::Pause)
|
||||||
if docker.pause_container(id.get()).await.is_err() {
|
.await;
|
||||||
app_data
|
|
||||||
.lock()
|
|
||||||
.set_error(AppError::DockerCommand(DockerControls::Pause));
|
|
||||||
};
|
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
|
||||||
}
|
}
|
||||||
DockerMessage::Restart(id) => {
|
DockerMessage::Restart(id) => {
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
self.exec_docker(
|
||||||
if docker.restart_container(id.get(), None).await.is_err() {
|
docker.restart_container(id.get(), None),
|
||||||
app_data
|
DockerControls::Restart,
|
||||||
.lock()
|
)
|
||||||
.set_error(AppError::DockerCommand(DockerControls::Restart));
|
.await;
|
||||||
};
|
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
|
||||||
}
|
}
|
||||||
DockerMessage::Start(id) => {
|
DockerMessage::Start(id) => {
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
self.exec_docker(
|
||||||
if docker
|
docker.start_container(id.get(), None::<StartContainerOptions<String>>),
|
||||||
.start_container(id.get(), None::<StartContainerOptions<String>>)
|
DockerControls::Start,
|
||||||
.await
|
)
|
||||||
.is_err()
|
.await;
|
||||||
{
|
|
||||||
app_data
|
|
||||||
.lock()
|
|
||||||
.set_error(AppError::DockerCommand(DockerControls::Start));
|
|
||||||
};
|
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
|
||||||
}
|
}
|
||||||
DockerMessage::Stop(id) => {
|
DockerMessage::Stop(id) => {
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
self.exec_docker(docker.stop_container(id.get(), None), DockerControls::Stop)
|
||||||
if docker.stop_container(id.get(), None).await.is_err() {
|
.await;
|
||||||
app_data
|
|
||||||
.lock()
|
|
||||||
.set_error(AppError::DockerCommand(DockerControls::Stop));
|
|
||||||
};
|
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
|
||||||
}
|
}
|
||||||
DockerMessage::Unpause(id) => {
|
DockerMessage::Unpause(id) => {
|
||||||
let loading_spin = self.loading_spin(loading_uuid).await;
|
self.exec_docker(docker.unpause_container(id.get()), DockerControls::Unpause)
|
||||||
if docker.unpause_container(id.get()).await.is_err() {
|
.await;
|
||||||
app_data
|
|
||||||
.lock()
|
|
||||||
.set_error(AppError::DockerCommand(DockerControls::Unpause));
|
|
||||||
};
|
|
||||||
self.stop_loading_spin(&loading_spin, loading_uuid);
|
|
||||||
self.update_everything().await;
|
self.update_everything().await;
|
||||||
}
|
}
|
||||||
DockerMessage::Update => self.update_everything().await,
|
DockerMessage::Update => self.update_everything().await,
|
||||||
|
|||||||
+30
-20
@@ -21,7 +21,7 @@ use crate::{
|
|||||||
app_data::{AppData, DockerControls, Header, SortedOrder},
|
app_data::{AppData, DockerControls, Header, SortedOrder},
|
||||||
app_error::AppError,
|
app_error::AppError,
|
||||||
docker_data::DockerMessage,
|
docker_data::DockerMessage,
|
||||||
ui::{GuiState, SelectablePanel},
|
ui::{GuiState, SelectablePanel, Status},
|
||||||
};
|
};
|
||||||
pub use message::InputMessages;
|
pub use message::InputMessages;
|
||||||
|
|
||||||
@@ -64,9 +64,11 @@ impl InputHandler {
|
|||||||
match message {
|
match message {
|
||||||
InputMessages::ButtonPress(key_code) => self.button_press(key_code).await,
|
InputMessages::ButtonPress(key_code) => self.button_press(key_code).await,
|
||||||
InputMessages::MouseEvent(mouse_event) => {
|
InputMessages::MouseEvent(mouse_event) => {
|
||||||
let show_error = self.app_data.lock().show_error;
|
let error_or_help = self
|
||||||
let show_info = self.gui_state.lock().show_help;
|
.gui_state
|
||||||
if !show_error && !show_info {
|
.lock()
|
||||||
|
.status_contains(&[Status::Error, Status::Help]);
|
||||||
|
if !error_or_help {
|
||||||
self.mouse_press(mouse_event);
|
self.mouse_press(mouse_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,10 +87,11 @@ impl InputHandler {
|
|||||||
.gui_state
|
.gui_state
|
||||||
.lock()
|
.lock()
|
||||||
.set_info_box("✖ mouse capture disabled".to_owned()),
|
.set_info_box("✖ mouse capture disabled".to_owned()),
|
||||||
Err(_) => self
|
Err(_) => {
|
||||||
.app_data
|
self.app_data
|
||||||
.lock()
|
.lock()
|
||||||
.set_error(AppError::MouseCapture(false)),
|
.set_error(AppError::MouseCapture(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match execute!(std::io::stdout(), EnableMouseCapture) {
|
match execute!(std::io::stdout(), EnableMouseCapture) {
|
||||||
@@ -96,11 +99,13 @@ impl InputHandler {
|
|||||||
.gui_state
|
.gui_state
|
||||||
.lock()
|
.lock()
|
||||||
.set_info_box("✓ mouse capture enabled".to_owned()),
|
.set_info_box("✓ mouse capture enabled".to_owned()),
|
||||||
Err(_) => self.app_data.lock().set_error(AppError::MouseCapture(true)),
|
Err(_) => {
|
||||||
|
self.app_data.lock().set_error(AppError::MouseCapture(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the info box sleep handle is currently being executed, as in m is pressed twice within a 4000ms window
|
// If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window
|
||||||
// then cancel the first handle, as a new handle will be invoked
|
// then cancel the first handle, as a new handle will be invoked
|
||||||
if let Some(info_sleep_timer) = self.info_sleep.as_ref() {
|
if let Some(info_sleep_timer) = self.info_sleep.as_ref() {
|
||||||
info_sleep_timer.abort();
|
info_sleep_timer.abort();
|
||||||
@@ -129,32 +134,37 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send a quit message to docker, to abort all spawns, if an error is return, set is_running to false here instead
|
/// Send a quit message to docker, to abort all spawns, if an error is return, set is_running to false here instead
|
||||||
|
/// If gui_status is Error or Init, then just set the is_running to false immediately, for a quicker exit
|
||||||
async fn quit(&self) {
|
async fn quit(&self) {
|
||||||
match self.docker_sender.send(DockerMessage::Quit).await {
|
let error_init = self
|
||||||
Ok(_) => (),
|
.gui_state
|
||||||
Err(_) => self.is_running.store(false, Ordering::SeqCst),
|
.lock()
|
||||||
|
.status_contains(&[Status::Error, Status::Init]);
|
||||||
|
if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() {
|
||||||
|
self.is_running.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle any keyboard button events
|
/// Handle any keyboard button events
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
async fn button_press(&mut self, key_code: KeyCode) {
|
async fn button_press(&mut self, key_code: KeyCode) {
|
||||||
let show_error = self.app_data.lock().show_error;
|
// TODO - refactor this to a single call, maybe return Error, Help or Normal
|
||||||
let show_info = self.gui_state.lock().show_help;
|
let contains_error = self.gui_state.lock().status_contains(&[Status::Error]);
|
||||||
|
let contains_help = self.gui_state.lock().status_contains(&[Status::Help]);
|
||||||
|
|
||||||
if show_error {
|
if contains_error {
|
||||||
match key_code {
|
match key_code {
|
||||||
KeyCode::Char('q' | 'Q') => self.quit().await,
|
KeyCode::Char('q' | 'Q') => self.quit().await,
|
||||||
KeyCode::Char('c' | 'C') => {
|
KeyCode::Char('c' | 'C') => {
|
||||||
self.app_data.lock().show_error = false;
|
|
||||||
self.app_data.lock().remove_error();
|
self.app_data.lock().remove_error();
|
||||||
|
self.gui_state.lock().status_del(Status::Error);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
} else if show_info {
|
} else if contains_help {
|
||||||
match key_code {
|
match key_code {
|
||||||
KeyCode::Char('q' | 'Q') => self.quit().await,
|
KeyCode::Char('q' | 'Q') => self.quit().await,
|
||||||
KeyCode::Char('h' | 'H') => self.gui_state.lock().show_help = false,
|
KeyCode::Char('h' | 'H') => self.gui_state.lock().status_del(Status::Help),
|
||||||
KeyCode::Char('m' | 'M') => self.m_key(),
|
KeyCode::Char('m' | 'M') => self.m_key(),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -171,7 +181,7 @@ impl InputHandler {
|
|||||||
KeyCode::Char('8') => self.sort(Header::Rx),
|
KeyCode::Char('8') => self.sort(Header::Rx),
|
||||||
KeyCode::Char('9') => self.sort(Header::Tx),
|
KeyCode::Char('9') => self.sort(Header::Tx),
|
||||||
KeyCode::Char('q' | 'Q') => self.quit().await,
|
KeyCode::Char('q' | 'Q') => self.quit().await,
|
||||||
KeyCode::Char('h' | 'H') => self.gui_state.lock().show_help = true,
|
KeyCode::Char('h' | 'H') => self.gui_state.lock().status_push(Status::Help),
|
||||||
KeyCode::Char('m' | 'M') => self.m_key(),
|
KeyCode::Char('m' | 'M') => self.m_key(),
|
||||||
KeyCode::Tab => {
|
KeyCode::Tab => {
|
||||||
// Skip control panel if no containers, could be refactored
|
// Skip control panel if no containers, could be refactored
|
||||||
|
|||||||
+20
-18
@@ -24,7 +24,7 @@ mod input_handler;
|
|||||||
mod parse_args;
|
mod parse_args;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use ui::{create_ui, GuiState};
|
use ui::{create_ui, GuiState, Status};
|
||||||
|
|
||||||
fn setup_tracing() {
|
fn setup_tracing() {
|
||||||
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
||||||
@@ -45,23 +45,25 @@ async fn main() {
|
|||||||
let (docker_sx, docker_rx) = tokio::sync::mpsc::channel(16);
|
let (docker_sx, docker_rx) = tokio::sync::mpsc::channel(16);
|
||||||
|
|
||||||
// Create docker daemon handler, and only spawn up the docker data handler if ping returns non-error
|
// Create docker daemon handler, and only spawn up the docker data handler if ping returns non-error
|
||||||
match Docker::connect_with_socket_defaults() {
|
if let Ok(docker) = Docker::connect_with_socket_defaults() {
|
||||||
Ok(docker) => match docker.ping().await {
|
if docker.ping().await.is_ok() {
|
||||||
Ok(_) => {
|
let docker = Arc::new(docker);
|
||||||
let docker = Arc::new(docker);
|
let is_running = Arc::clone(&is_running);
|
||||||
let is_running = Arc::clone(&is_running);
|
tokio::spawn(DockerData::init(
|
||||||
tokio::spawn(DockerData::init(
|
args,
|
||||||
args,
|
docker_app_data,
|
||||||
docker_app_data,
|
docker,
|
||||||
docker,
|
docker_gui_state,
|
||||||
docker_gui_state,
|
docker_rx,
|
||||||
docker_rx,
|
is_running,
|
||||||
is_running,
|
));
|
||||||
));
|
} else {
|
||||||
}
|
app_data.lock().set_error(AppError::DockerConnect);
|
||||||
Err(_) => app_data.lock().set_error(AppError::DockerConnect),
|
docker_gui_state.lock().status_push(Status::DockerConnect);
|
||||||
},
|
}
|
||||||
Err(_) => app_data.lock().set_error(AppError::DockerConnect),
|
} else {
|
||||||
|
app_data.lock().set_error(AppError::DockerConnect);
|
||||||
|
docker_gui_state.lock().status_push(Status::DockerConnect);
|
||||||
}
|
}
|
||||||
let input_app_data = Arc::clone(&app_data);
|
let input_app_data = Arc::clone(&app_data);
|
||||||
|
|
||||||
|
|||||||
+38
-20
@@ -15,6 +15,7 @@ use tui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::app_data::{Header, SortedOrder};
|
use crate::app_data::{Header, SortedOrder};
|
||||||
|
use crate::ui::Status;
|
||||||
use crate::{
|
use crate::{
|
||||||
app_data::{AppData, ByteStats, Columns, CpuStats, State, Stats},
|
app_data::{AppData, ByteStats, Columns, CpuStats, State, Stats},
|
||||||
app_error::AppError,
|
app_error::AppError,
|
||||||
@@ -50,7 +51,9 @@ fn generate_block<'a>(
|
|||||||
gui_state: &Arc<Mutex<GuiState>>,
|
gui_state: &Arc<Mutex<GuiState>>,
|
||||||
panel: SelectablePanel,
|
panel: SelectablePanel,
|
||||||
) -> Block<'a> {
|
) -> Block<'a> {
|
||||||
gui_state.lock().update_map(Region::Panel(panel), area);
|
gui_state
|
||||||
|
.lock()
|
||||||
|
.update_heading_map(Region::Panel(panel), area);
|
||||||
let current_selected_panel = gui_state.lock().selected_panel;
|
let current_selected_panel = gui_state.lock().selected_panel;
|
||||||
let title = match panel {
|
let title = match panel {
|
||||||
SelectablePanel::Containers => {
|
SelectablePanel::Containers => {
|
||||||
@@ -215,9 +218,8 @@ pub fn logs<B: Backend>(
|
|||||||
loading_icon: &str,
|
loading_icon: &str,
|
||||||
) {
|
) {
|
||||||
let block = generate_block(app_data, area, gui_state, SelectablePanel::Logs);
|
let block = generate_block(app_data, area, gui_state, SelectablePanel::Logs);
|
||||||
|
let contains_init = gui_state.lock().status_contains(&[Status::Init]);
|
||||||
let init = app_data.lock().init;
|
if contains_init {
|
||||||
if !init {
|
|
||||||
let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon))
|
let paragraph = Paragraph::new(format!("parsing logs {}", loading_icon))
|
||||||
.style(Style::default())
|
.style(Style::default())
|
||||||
.block(block)
|
.block(block)
|
||||||
@@ -337,6 +339,7 @@ fn make_chart<'a, T: Stats + Display>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Draw heading bar at top of program, always visible
|
/// Draw heading bar at top of program, always visible
|
||||||
|
/// TODO Should seperate into loading icon/headers/help functions
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn heading_bar<B: Backend>(
|
pub fn heading_bar<B: Backend>(
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@@ -347,10 +350,10 @@ pub fn heading_bar<B: Backend>(
|
|||||||
sorted_by: Option<(Header, SortedOrder)>,
|
sorted_by: Option<(Header, SortedOrder)>,
|
||||||
gui_state: &Arc<Mutex<GuiState>>,
|
gui_state: &Arc<Mutex<GuiState>>,
|
||||||
) {
|
) {
|
||||||
let block = || Block::default().style(Style::default().bg(Color::Magenta).fg(Color::Black));
|
let block = |fg: Color| Block::default().style(Style::default().bg(Color::Magenta).fg(fg));
|
||||||
let info_visible = gui_state.lock().show_help;
|
let help_visible = gui_state.lock().status_contains(&[Status::Help]);
|
||||||
|
|
||||||
f.render_widget(block(), area);
|
f.render_widget(block(Color::Black), area);
|
||||||
|
|
||||||
// Generate a bloack for the header, if the header is currently being used to sort a column, then highlight it white
|
// Generate a bloack for the header, if the header is currently being used to sort a column, then highlight it white
|
||||||
let header_block = |x: &Header| {
|
let header_block = |x: &Header| {
|
||||||
@@ -380,8 +383,7 @@ pub fn heading_bar<B: Backend>(
|
|||||||
let block = header_block(header);
|
let block = header_block(header);
|
||||||
let text = match header {
|
let text = match header {
|
||||||
Header::State => format!(
|
Header::State => format!(
|
||||||
" {}{:>width$}{ic}",
|
"{:>width$}{ic}",
|
||||||
loading_icon,
|
|
||||||
header,
|
header,
|
||||||
ic = block.1,
|
ic = block.1,
|
||||||
width = width - block.2,
|
width = width - block.2,
|
||||||
@@ -393,7 +395,6 @@ pub fn heading_bar<B: Backend>(
|
|||||||
ic = block.1,
|
ic = block.1,
|
||||||
width = width - block.2
|
width = width - block.2
|
||||||
),
|
),
|
||||||
|
|
||||||
_ => format!(
|
_ => format!(
|
||||||
"{}{:>width$}{ic}",
|
"{}{:>width$}{ic}",
|
||||||
MARGIN,
|
MARGIN,
|
||||||
@@ -430,14 +431,15 @@ pub fn heading_bar<B: Backend>(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let suffix = if info_visible { "exit" } else { "show" };
|
let suffix = if help_visible { "exit" } else { "show" };
|
||||||
let info_text = format!("( h ) {} help {}", suffix, MARGIN);
|
let info_text = format!("( h ) {} help {}", suffix, MARGIN,);
|
||||||
let info_width = info_text.chars().count();
|
let info_width = info_text.chars().count();
|
||||||
|
|
||||||
let column_width = usize::from(area.width) - info_width;
|
let column_width = usize::from(area.width) - info_width;
|
||||||
let column_width = if column_width > 0 { column_width } else { 1 };
|
let column_width = if column_width > 0 { column_width } else { 1 };
|
||||||
let splits = if has_containers {
|
let splits = if has_containers {
|
||||||
vec![
|
vec![
|
||||||
|
Constraint::Min(2),
|
||||||
Constraint::Min(column_width.try_into().unwrap_or_default()),
|
Constraint::Min(column_width.try_into().unwrap_or_default()),
|
||||||
Constraint::Min(info_width.try_into().unwrap_or_default()),
|
Constraint::Min(info_width.try_into().unwrap_or_default()),
|
||||||
]
|
]
|
||||||
@@ -450,28 +452,44 @@ pub fn heading_bar<B: Backend>(
|
|||||||
.constraints(splits.as_ref())
|
.constraints(splits.as_ref())
|
||||||
.split(area);
|
.split(area);
|
||||||
if has_containers {
|
if has_containers {
|
||||||
let container_splits = header_data.iter().map(|i| i.2).collect::<Vec<_>>();
|
// Draw loading icon, or not, and a prefix with a single space
|
||||||
|
let loading_icon = format!("{:>2}", loading_icon);
|
||||||
|
let loading_paragraph = Paragraph::new(loading_icon)
|
||||||
|
.block(block(Color::White))
|
||||||
|
.alignment(Alignment::Center);
|
||||||
|
f.render_widget(loading_paragraph, split_bar[0]);
|
||||||
|
|
||||||
|
let container_splits = header_data.iter().map(|i| i.2).collect::<Vec<_>>();
|
||||||
let headers_section = Layout::default()
|
let headers_section = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints(container_splits.as_ref())
|
.constraints(container_splits.as_ref())
|
||||||
.split(split_bar[0]);
|
.split(split_bar[1]);
|
||||||
|
|
||||||
// draw the actual header blocks
|
// draw the actual header blocks
|
||||||
for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() {
|
for (index, (paragraph, header, _)) in header_data.into_iter().enumerate() {
|
||||||
let rect = headers_section[index];
|
let rect = headers_section[index];
|
||||||
gui_state.lock().update_map(Region::Header(header), rect);
|
gui_state
|
||||||
|
.lock()
|
||||||
|
.update_heading_map(Region::Header(header), rect);
|
||||||
f.render_widget(paragraph, rect);
|
f.render_widget(paragraph, rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let paragraph = Paragraph::new(info_text)
|
// show/hide help
|
||||||
.block(block())
|
let color = if help_visible {
|
||||||
|
Color::Black
|
||||||
|
} else {
|
||||||
|
Color::White
|
||||||
|
};
|
||||||
|
let help_paragraph = Paragraph::new(info_text)
|
||||||
|
.block(block(color))
|
||||||
.alignment(Alignment::Right);
|
.alignment(Alignment::Right);
|
||||||
|
|
||||||
// If no containers, don't display the headers, could maybe do this first?
|
// If no containers, don't display the headers, could maybe do this first?
|
||||||
let index = if has_containers { 1 } else { 0 };
|
let help_index = if has_containers { 2 } else { 0 };
|
||||||
f.render_widget(paragraph, split_bar[index]);
|
// render help info
|
||||||
|
|
||||||
|
f.render_widget(help_paragraph, split_bar[help_index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// From a given &str, return the maximum number of chars on a single line
|
/// From a given &str, return the maximum number of chars on a single line
|
||||||
@@ -487,7 +505,7 @@ fn max_line_width(text: &str) -> usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the help box in the centre of the screen
|
/// Draw the help box in the centre of the screen
|
||||||
/// TODO this is message, should make every line it's own renderable span
|
/// TODO should make every line it's own renderable span
|
||||||
pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
|
||||||
let title = format!(" {} ", VERSION);
|
let title = format!(" {} ", VERSION);
|
||||||
|
|
||||||
|
|||||||
+30
-4
@@ -59,6 +59,7 @@ pub enum BoxLocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BoxLocation {
|
impl BoxLocation {
|
||||||
|
/// Screen is divided into 3x3 sections
|
||||||
pub const fn get_indexes(self) -> (usize, usize) {
|
pub const fn get_indexes(self) -> (usize, usize) {
|
||||||
match self {
|
match self {
|
||||||
Self::TopLeft => (0, 0),
|
Self::TopLeft => (0, 0),
|
||||||
@@ -172,6 +173,16 @@ impl fmt::Display for Loading {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The application gui state can be in multiple of these four states at the same time
|
||||||
|
/// Various functions (e.g input handler), operate differently depending upon current Status
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub enum Status {
|
||||||
|
Init,
|
||||||
|
Help,
|
||||||
|
DockerConnect,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
/// Global gui_state, stored in an Arc<Mutex>
|
/// Global gui_state, stored in an Arc<Mutex>
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GuiState {
|
pub struct GuiState {
|
||||||
@@ -179,8 +190,8 @@ pub struct GuiState {
|
|||||||
heading_map: HashMap<Header, Rect>,
|
heading_map: HashMap<Header, Rect>,
|
||||||
loading_icon: Loading,
|
loading_icon: Loading,
|
||||||
is_loading: HashSet<Uuid>,
|
is_loading: HashSet<Uuid>,
|
||||||
|
status: HashSet<Status>,
|
||||||
pub selected_panel: SelectablePanel,
|
pub selected_panel: SelectablePanel,
|
||||||
pub show_help: bool,
|
|
||||||
pub info_box_text: Option<String>,
|
pub info_box_text: Option<String>,
|
||||||
}
|
}
|
||||||
impl GuiState {
|
impl GuiState {
|
||||||
@@ -191,9 +202,9 @@ impl GuiState {
|
|||||||
heading_map: HashMap::new(),
|
heading_map: HashMap::new(),
|
||||||
loading_icon: Loading::One,
|
loading_icon: Loading::One,
|
||||||
selected_panel: SelectablePanel::Containers,
|
selected_panel: SelectablePanel::Containers,
|
||||||
show_help: false,
|
|
||||||
is_loading: HashSet::new(),
|
is_loading: HashSet::new(),
|
||||||
info_box_text: None,
|
info_box_text: None,
|
||||||
|
status: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +237,7 @@ impl GuiState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert, or updates header area panel into heading_map
|
/// Insert, or updates header area panel into heading_map
|
||||||
pub fn update_map(&mut self, region: Region, area: Rect) {
|
pub fn update_heading_map(&mut self, region: Region, area: Rect) {
|
||||||
match region {
|
match region {
|
||||||
Region::Header(header) => self
|
Region::Header(header) => self
|
||||||
.heading_map
|
.heading_map
|
||||||
@@ -241,6 +252,21 @@ impl GuiState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the current gui_status contains any of the given status'
|
||||||
|
pub fn status_contains(&self, status: &[Status]) -> bool {
|
||||||
|
status.iter().any(|i| self.status.contains(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a gui_status into the current gui_status hashset
|
||||||
|
pub fn status_del(&mut self, status: Status) {
|
||||||
|
self.status.remove(&status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a gui_status into the current gui_status hashset
|
||||||
|
pub fn status_push(&mut self, status: Status) {
|
||||||
|
self.status.insert(status);
|
||||||
|
}
|
||||||
|
|
||||||
/// Change to next selectable panel
|
/// Change to next selectable panel
|
||||||
pub fn next_panel(&mut self) {
|
pub fn next_panel(&mut self) {
|
||||||
self.selected_panel = self.selected_panel.next();
|
self.selected_panel = self.selected_panel.next();
|
||||||
@@ -260,7 +286,7 @@ impl GuiState {
|
|||||||
/// If is_loading has any entries, return the current loading_icon, else an emtpy string
|
/// If is_loading has any entries, return the current loading_icon, else an emtpy string
|
||||||
pub fn get_loading(&mut self) -> String {
|
pub fn get_loading(&mut self) -> String {
|
||||||
if self.is_loading.is_empty() {
|
if self.is_loading.is_empty() {
|
||||||
String::new()
|
String::from(" ")
|
||||||
} else {
|
} else {
|
||||||
self.loading_icon.to_string()
|
self.loading_icon.to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-7
@@ -25,7 +25,7 @@ mod draw_blocks;
|
|||||||
mod gui_state;
|
mod gui_state;
|
||||||
|
|
||||||
pub use self::color_match::*;
|
pub use self::color_match::*;
|
||||||
pub use self::gui_state::{GuiState, SelectablePanel};
|
pub use self::gui_state::{GuiState, SelectablePanel, Status};
|
||||||
use crate::{
|
use crate::{
|
||||||
app_data::AppData, app_error::AppError, docker_data::DockerMessage,
|
app_data::AppData, app_error::AppError, docker_data::DockerMessage,
|
||||||
input_handler::InputMessages,
|
input_handler::InputMessages,
|
||||||
@@ -56,6 +56,7 @@ pub async fn create_ui(
|
|||||||
update_duration,
|
update_duration,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
terminal.clear()?;
|
||||||
|
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
execute!(
|
execute!(
|
||||||
@@ -82,10 +83,8 @@ async fn run_app<B: Backend + Send>(
|
|||||||
update_duration: Duration,
|
update_duration: Duration,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
let input_poll_rate = std::time::Duration::from_millis(75);
|
let input_poll_rate = std::time::Duration::from_millis(75);
|
||||||
|
let status_dockerconnect = gui_state.lock().status_contains(&[Status::DockerConnect]);
|
||||||
// Check for docker connect errors before attempting to draw the gui
|
if status_dockerconnect {
|
||||||
let e = app_data.lock().get_error();
|
|
||||||
if let Some(AppError::DockerConnect) = e {
|
|
||||||
let mut seconds = 5;
|
let mut seconds = 5;
|
||||||
loop {
|
loop {
|
||||||
if seconds < 1 {
|
if seconds < 1 {
|
||||||
@@ -156,7 +155,7 @@ fn ui<B: Backend>(
|
|||||||
let log_index = app_data.lock().get_selected_log_index();
|
let log_index = app_data.lock().get_selected_log_index();
|
||||||
let sorted_by = app_data.lock().get_sorted();
|
let sorted_by = app_data.lock().get_sorted();
|
||||||
|
|
||||||
let show_help = gui_state.lock().show_help;
|
let show_help = gui_state.lock().status_contains(&[Status::Help]);
|
||||||
let info_text = gui_state.lock().info_box_text.clone();
|
let info_text = gui_state.lock().info_box_text.clone();
|
||||||
let loading_icon = gui_state.lock().get_loading();
|
let loading_icon = gui_state.lock().get_loading();
|
||||||
|
|
||||||
@@ -240,7 +239,6 @@ fn ui<B: Backend>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(error) = has_error {
|
if let Some(error) = has_error {
|
||||||
app_data.lock().show_error = true;
|
|
||||||
draw_blocks::error(f, error, None);
|
draw_blocks::error(f, error, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user