Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9599ac28ca | ||
|
|
e71a699ed8 | ||
| ff4eb8ca98 | |||
| b69973b9af | |||
| 3e133fa147 | |||
| ae506789ab | |||
| c4e8d64710 | |||
| ca4319001c | |||
| ebc58b831d | |||
|
|
b1572c903c | ||
|
|
5b73924e2a | ||
| 97ce258a8d | |||
| fedec79a95 | |||
| 8e74709b9c |
@@ -8,9 +8,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
bump_type:
|
bump_type:
|
||||||
description: "Specify the type of version bump"
|
description: 'Specify the type of version bump'
|
||||||
required: true
|
required: true
|
||||||
default: "patch"
|
default: 'patch'
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- patch
|
- patch
|
||||||
@@ -46,12 +46,12 @@ jobs:
|
|||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: '3.10'
|
||||||
|
|
||||||
- name: Install Commitizen
|
- name: Install Commitizen
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip install commitizen
|
pip3 install commitizen==4.8.3
|
||||||
npm install -g conventional-changelog-cli
|
npm install -g conventional-changelog-cli
|
||||||
|
|
||||||
- name: Configure Git user
|
- name: Configure Git user
|
||||||
@@ -126,12 +126,90 @@ jobs:
|
|||||||
echo "No changes to commit (already at $VERSION)"
|
echo "No changes to commit (already at $VERSION)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Bump validate_theme_derive/Cargo.toml version
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/proc_macros/validate_theme_derive
|
||||||
|
env:
|
||||||
|
VERSION: ${{ env.version }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
: "${VERSION:?env.version is empty}"
|
||||||
|
|
||||||
|
# Ignore Act's local artifact dir noise
|
||||||
|
echo artifacts/ >> .git/info/exclude || true
|
||||||
|
|
||||||
|
# Edit the version line right after name="managarr"
|
||||||
|
sed -E -i '
|
||||||
|
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"managarr"[[:space:]]*$/ {
|
||||||
|
n
|
||||||
|
s|^[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"|version = "'"$VERSION"'"|
|
||||||
|
}
|
||||||
|
' Cargo.toml
|
||||||
|
|
||||||
|
cargo update || true
|
||||||
|
|
||||||
|
# Git config that helps in containers (Act)
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
|
|
||||||
|
# Debug: show what changed
|
||||||
|
git status --porcelain
|
||||||
|
git diff --name-only -- Cargo.toml || true
|
||||||
|
|
||||||
|
# Only commit if one of these files actually changed
|
||||||
|
if ! git diff --quiet -- Cargo.toml; then
|
||||||
|
# Stage only modifications of already tracked files (won't pick up artifacts/)
|
||||||
|
git add -u -- Cargo.toml
|
||||||
|
git commit -m "chore: bump validate_theme_derive Cargo.toml to $VERSION"
|
||||||
|
else
|
||||||
|
echo "No changes to commit (already at $VERSION)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Bump enum_display_style_derive/Cargo.toml version
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/proc_macros/enum_display_style_derive
|
||||||
|
env:
|
||||||
|
VERSION: ${{ env.version }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
: "${VERSION:?env.version is empty}"
|
||||||
|
|
||||||
|
# Ignore Act's local artifact dir noise
|
||||||
|
echo artifacts/ >> .git/info/exclude || true
|
||||||
|
|
||||||
|
# Edit the version line right after name="managarr"
|
||||||
|
sed -E -i '
|
||||||
|
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"managarr"[[:space:]]*$/ {
|
||||||
|
n
|
||||||
|
s|^[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"|version = "'"$VERSION"'"|
|
||||||
|
}
|
||||||
|
' Cargo.toml
|
||||||
|
|
||||||
|
cargo update || true
|
||||||
|
|
||||||
|
# Git config that helps in containers (Act)
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
|
|
||||||
|
# Debug: show what changed
|
||||||
|
git status --porcelain
|
||||||
|
git diff --name-only -- Cargo.toml || true
|
||||||
|
|
||||||
|
# Only commit if one of these files actually changed
|
||||||
|
if ! git diff --quiet -- Cargo.toml; then
|
||||||
|
# Stage only modifications of already tracked files (won't pick up artifacts/)
|
||||||
|
git add -u -- Cargo.toml
|
||||||
|
git commit -m "chore: bump enum_display_style_derive Cargo.toml to $VERSION"
|
||||||
|
else
|
||||||
|
echo "No changes to commit (already at $VERSION)"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Generate changelog for the version bump
|
- name: Generate changelog for the version bump
|
||||||
id: changelog
|
id: changelog
|
||||||
run: |
|
run: |
|
||||||
changelog=$(conventional-changelog -p angular -i CHANGELOG.md -s --from ${{ env.prev_version }} --to ${{ env.version }})
|
conventional-changelog -p angular -i CHANGELOG.md --from ${{ env.prev_version }} --to v${{ env.version }} > artifacts/changelog.md
|
||||||
echo "$changelog" > artifacts/changelog.md
|
|
||||||
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Push changes
|
- name: Push changes
|
||||||
if: env.ACT != 'true'
|
if: env.ACT != 'true'
|
||||||
@@ -153,6 +231,8 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
Cargo.toml
|
Cargo.toml
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
proc_macros/validate_theme_derive/Cargo.toml
|
||||||
|
proc_macros/enum_display_style_derive/Cargo.toml
|
||||||
|
|
||||||
build-release-artifacts:
|
build-release-artifacts:
|
||||||
name: build-release
|
name: build-release
|
||||||
@@ -333,13 +413,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
release_version="$(cat ./artifacts/release-version)"
|
release_version="$(cat ./artifacts/release-version)"
|
||||||
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
||||||
changelog_body="$(cat ./artifacts/changelog.md)"
|
|
||||||
echo "changelog_body=$(cat artifacts/changelog.md)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Validate release environment variables
|
- name: Validate release environment variables
|
||||||
run: |
|
run: |
|
||||||
echo "Release version: ${{ env.RELEASE_VERSION }}"
|
echo "Release version: ${{ env.RELEASE_VERSION }}"
|
||||||
echo "Changelog body: ${{ env.changelog_body }}"
|
echo "Changelog body: $(cat artifacts/changelog.md)"
|
||||||
|
|
||||||
- name: Create a GitHub Release
|
- name: Create a GitHub Release
|
||||||
if: env.ACT != 'true'
|
if: env.ACT != 'true'
|
||||||
@@ -373,8 +451,8 @@ jobs:
|
|||||||
artifacts/managarr-armv7-musl.tar.gz
|
artifacts/managarr-armv7-musl.tar.gz
|
||||||
artifacts/managarr-armv7-musl.sha256
|
artifacts/managarr-armv7-musl.sha256
|
||||||
tag_name: v${{ env.RELEASE_VERSION }}
|
tag_name: v${{ env.RELEASE_VERSION }}
|
||||||
name: "v${{ env.RELEASE_VERSION }}"
|
name: 'v${{ env.RELEASE_VERSION }}'
|
||||||
body: ${{ env.changelog_body }}
|
body_path: artifacts/changelog.md
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## v0.6.2 (2025-12-12)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- Fixed breaking Sonarr Episode file API calls after recent Sonarr API update
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
|
||||||
|
- Replaced all modulo usages of tick_until_poll with is_multiple_of
|
||||||
|
|
||||||
|
## v0.6.1 (2025-09-02)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- Fixed UI bugs introduced as part of the hotkey refactor
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
|
||||||
|
- Updated crate to publish properly with the procedural macros
|
||||||
|
|
||||||
## v0.6.0 (2025-08-29)
|
## v0.6.0 (2025-08-29)
|
||||||
|
|
||||||
### Feat
|
### Feat
|
||||||
|
|||||||
Generated
+673
-484
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "managarr"
|
name = "managarr"
|
||||||
version = "0.6.0"
|
version = "0.6.2"
|
||||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
description = "A TUI and CLI to manage your Servarrs"
|
description = "A TUI and CLI to manage your Servarrs"
|
||||||
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
|
keywords = ["managarr", "ratatui", "dashboard", "servarr", "tui"]
|
||||||
@@ -66,8 +66,8 @@ deunicode = "1.6.0"
|
|||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
openssl = { version = "0.10.70", features = ["vendored"] }
|
openssl = { version = "0.10.70", features = ["vendored"] }
|
||||||
veil = "0.2.0"
|
veil = "0.2.0"
|
||||||
validate_theme_derive = { path = "proc_macros/validate_theme_derive" }
|
validate_theme_derive = "0.1.0"
|
||||||
enum_display_style_derive = { path = "proc_macros/enum_display_style_derive" }
|
enum_display_style_derive = "0.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2.0.16"
|
assert_cmd = "2.0.16"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Documentation: https://docs.brew.sh/Formula-Cookbook
|
# Documentation: https://docs.brew.sh/Formula-Cookbook
|
||||||
# https://rubydoc.brew.sh/Formula
|
# https://rubydoc.brew.sh/Formula
|
||||||
class Managarr < Formula
|
class Managarr < Formula
|
||||||
desc "A fast and simple dashboard for Kubernetes written in Rust"
|
desc "Managarr is a TUI and CLI to help you manage your HTPC (Home Theater PC)"
|
||||||
homepage "https://github.com/Dark-Alex-17/managarr"
|
homepage "https://github.com/Dark-Alex-17/managarr"
|
||||||
if OS.mac? and Hardware::CPU.arm?
|
if OS.mac? and Hardware::CPU.arm?
|
||||||
url "https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-macos-arm64.tar.gz"
|
url "https://github.com/Dark-Alex-17/managarr/releases/download/v$version/managarr-macos-arm64.tar.gz"
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "enum_display_style_derive"
|
name = "enum_display_style_derive"
|
||||||
version = "0.1.0"
|
version = "0.6.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
|
description = "A proc-macro to derive a `Display` and `FromStr` implementation for enums with a `style` attribute."
|
||||||
|
license = "MIT"
|
||||||
|
documentation = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
repository = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
homepage = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "validate_theme_derive"
|
name = "validate_theme_derive"
|
||||||
version = "0.1.0"
|
version = "0.6.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
|
description = "A proc-macro to validate a theme."
|
||||||
|
license = "MIT"
|
||||||
|
documentation = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
repository = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
homepage = "https://github.com/Dark-Alex-17/managarr"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|||||||
+4
-1
@@ -160,7 +160,10 @@ impl App<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_tick(&mut self) {
|
pub async fn on_tick(&mut self) {
|
||||||
if self.tick_count % self.tick_until_poll == 0 || self.is_routing || self.should_refresh {
|
if self.tick_count.is_multiple_of(self.tick_until_poll)
|
||||||
|
|| self.is_routing
|
||||||
|
|| self.should_refresh
|
||||||
|
{
|
||||||
match self.get_current_route() {
|
match self.get_current_route() {
|
||||||
Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await,
|
Route::Radarr(active_radarr_block, _) => self.radarr_on_tick(active_radarr_block).await,
|
||||||
Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await,
|
Route::Sonarr(active_sonarr_block, _) => self.sonarr_on_tick(active_sonarr_block).await,
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ impl App<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tick_count % self.tick_until_poll == 0 {
|
if self.tick_count.is_multiple_of(self.tick_until_poll) {
|
||||||
self.refresh_radarr_metadata().await;
|
self.refresh_radarr_metadata().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ impl App<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tick_count % self.tick_until_poll == 0 {
|
if self.tick_count.is_multiple_of(self.tick_until_poll) {
|
||||||
self.refresh_sonarr_metadata().await;
|
self.refresh_sonarr_metadata().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ pub(in crate::handlers::sonarr_handlers) mod utils {
|
|||||||
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
|
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
|
||||||
size: 3543348019,
|
size: 3543348019,
|
||||||
quality: quality_wrapper(),
|
quality: quality_wrapper(),
|
||||||
languages: vec![language()],
|
languages: vec![Some(language())],
|
||||||
date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
||||||
media_info: Some(media_info()),
|
media_info: Some(media_info()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ pub struct EpisodeFile {
|
|||||||
pub path: String,
|
pub path: String,
|
||||||
#[serde(deserialize_with = "super::from_i64")]
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
pub size: i64,
|
pub size: i64,
|
||||||
pub languages: Vec<Language>,
|
pub languages: Vec<Option<Language>>,
|
||||||
pub quality: QualityWrapper,
|
pub quality: QualityWrapper,
|
||||||
pub date_added: DateTime<Utc>,
|
pub date_added: DateTime<Utc>,
|
||||||
pub media_info: Option<MediaInfo>,
|
pub media_info: Option<MediaInfo>,
|
||||||
|
|||||||
@@ -297,7 +297,13 @@ impl Network<'_, '_> {
|
|||||||
Date Added: {}",
|
Date Added: {}",
|
||||||
file.relative_path,
|
file.relative_path,
|
||||||
file.path,
|
file.path,
|
||||||
file.languages.first().unwrap_or(&Language::default()).name,
|
file
|
||||||
|
.languages
|
||||||
|
.first()
|
||||||
|
.unwrap_or(&Some(Language::default()))
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&Language::default())
|
||||||
|
.name,
|
||||||
file.date_added,
|
file.date_added,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ pub(in crate::network::sonarr_network) mod test_utils {
|
|||||||
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
|
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
|
||||||
size: 3543348019,
|
size: 3543348019,
|
||||||
quality: quality_wrapper(),
|
quality: quality_wrapper(),
|
||||||
languages: vec![language()],
|
languages: vec![Some(language())],
|
||||||
date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
||||||
media_info: Some(media_info()),
|
media_info: Some(media_info()),
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@ fn draw_error(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
app.error.scroll_left_or_reset(
|
app.error.scroll_left_or_reset(
|
||||||
area.width as usize,
|
area.width as usize,
|
||||||
true,
|
true,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
let paragraph = Paragraph::new(Text::from(app.error.to_string().failure()))
|
let paragraph = Paragraph::new(Text::from(app.error.to_string().failure()))
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ fn draw_blocklist_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 20),
|
get_width_from_percentage(area, 20),
|
||||||
current_selection == *blocklist_item,
|
current_selection == *blocklist_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
let languages_string = languages
|
let languages_string = languages
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect};
|
use ratatui::layout::{Constraint, Flex, Layout, Rect};
|
||||||
use ratatui::style::Stylize;
|
use ratatui::style::Stylize;
|
||||||
use ratatui::text::{Line, Text};
|
use ratatui::text::{Line, Text};
|
||||||
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
@@ -90,7 +90,7 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(table_area, 20),
|
get_width_from_percentage(table_area, 20),
|
||||||
current_selection == *movie,
|
current_selection == *movie,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let (hours, minutes) = convert_runtime(movie.runtime);
|
let (hours, minutes) = convert_runtime(movie.runtime);
|
||||||
let imdb_rating = movie
|
let imdb_rating = movie
|
||||||
@@ -184,7 +184,6 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
)
|
)
|
||||||
.block(layout_block_top_border_with_title(title_style("Movies")))
|
.block(layout_block_top_border_with_title(title_style("Movies")))
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.footer_alignment(Alignment::Center)
|
|
||||||
.headers([
|
.headers([
|
||||||
"✔",
|
"✔",
|
||||||
"Title",
|
"Title",
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
collection.title.scroll_left_or_reset(
|
collection.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 25),
|
get_width_from_percentage(area, 25),
|
||||||
*collection == current_selection,
|
*collection == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let monitored = if collection.monitored { "🏷" } else { "" };
|
let monitored = if collection.monitored { "🏷" } else { "" };
|
||||||
let search_on_add = if collection.search_on_add {
|
let search_on_add = if collection.search_on_add {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
output_path.as_ref().unwrap().scroll_left_or_reset(
|
output_path.as_ref().unwrap().scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 18),
|
get_width_from_percentage(area, 18),
|
||||||
current_selection == *download_record,
|
current_selection == *download_record,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::ui::utils::{get_width_from_percentage, title_block};
|
|||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use crate::ui::widgets::popup::Size;
|
use crate::ui::widgets::popup::Size;
|
||||||
use crate::ui::{draw_popup, DrawUi};
|
use crate::ui::{draw_popup, DrawUi};
|
||||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
use ratatui::layout::{Constraint, Rect};
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
result.validation_failures.scroll_left_or_reset(
|
result.validation_failures.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 86),
|
get_width_from_percentage(area, 86),
|
||||||
*result == current_selection,
|
*result == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||||
let row = Row::new(vec![
|
let row = Row::new(vec![
|
||||||
@@ -67,7 +67,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
test_results_row_mapping,
|
test_results_row_mapping,
|
||||||
)
|
)
|
||||||
.loading(is_loading)
|
.loading(is_loading)
|
||||||
.footer_alignment(Alignment::Center)
|
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||||
.constraints([
|
.constraints([
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 27),
|
get_width_from_percentage(area, 27),
|
||||||
*movie == current_selection,
|
*movie == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
movie.title.scroll_left_or_reset(
|
movie.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 27),
|
get_width_from_percentage(area, 27),
|
||||||
*movie == current_selection,
|
*movie == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let monitored = if movie.monitored { "🏷" } else { "" };
|
let monitored = if movie.monitored { "🏷" } else { "" };
|
||||||
let studio = movie.studio.clone().unwrap_or_default();
|
let studio = movie.studio.clone().unwrap_or_default();
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
movie_history_item.source_title.scroll_left_or_reset(
|
movie_history_item.source_title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 34),
|
get_width_from_percentage(area, 34),
|
||||||
current_selection == *movie_history_item,
|
current_selection == *movie_history_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
@@ -393,7 +393,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
get_width_from_percentage(area, 30),
|
get_width_from_percentage(area, 30),
|
||||||
current_selection == *release
|
current_selection == *release
|
||||||
&& current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(),
|
&& current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(),
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let size = convert_to_gb(*size);
|
let size = convert_to_gb(*size);
|
||||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ratatui::layout::{Alignment, Rect};
|
use ratatui::layout::Rect;
|
||||||
use ratatui::text::{Span, Text};
|
use ratatui::text::{Span, Text};
|
||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
@@ -60,7 +60,7 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
|
|
||||||
if app.data.radarr_data.log_details.items.is_empty() {
|
if app.data.radarr_data.log_details.items.is_empty() {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading).size(Size::Large).block(block);
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
return;
|
return;
|
||||||
@@ -73,7 +73,10 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
})
|
})
|
||||||
.block(borderless_block());
|
.block(borderless_block());
|
||||||
let popup = Popup::new(logs_list).size(Size::Large).block(block);
|
let popup = Popup::new(logs_list)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
@@ -94,7 +97,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.footer_alignment(Alignment::Center)
|
|
||||||
.headers(TASK_TABLE_HEADERS)
|
.headers(TASK_TABLE_HEADERS)
|
||||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
|
|
||||||
@@ -129,12 +131,15 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.scroll((app.data.radarr_data.updates.offset, 0));
|
.scroll((app.data.radarr_data.updates.offset, 0));
|
||||||
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block);
|
let popup = Popup::new(updates_paragraph)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
} else {
|
} else {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading).size(Size::Large).block(block);
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
output_path.as_ref().unwrap().scroll_left_or_reset(
|
output_path.as_ref().unwrap().scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 18),
|
get_width_from_percentage(area, 18),
|
||||||
current_selection == *download_record,
|
current_selection == *download_record,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ fn draw_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
source_title.scroll_left_or_reset(
|
source_title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 40),
|
get_width_from_percentage(area, 40),
|
||||||
current_selection == *history_item,
|
current_selection == *history_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::ui::utils::{get_width_from_percentage, title_block};
|
|||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use crate::ui::widgets::popup::Size;
|
use crate::ui::widgets::popup::Size;
|
||||||
use crate::ui::{draw_popup, DrawUi};
|
use crate::ui::{draw_popup, DrawUi};
|
||||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
use ratatui::layout::{Constraint, Rect};
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
result.validation_failures.scroll_left_or_reset(
|
result.validation_failures.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 86),
|
get_width_from_percentage(area, 86),
|
||||||
*result == current_selection,
|
*result == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||||
let row = Row::new(vec![
|
let row = Row::new(vec![
|
||||||
@@ -65,7 +65,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
|||||||
test_results_row_mapping,
|
test_results_row_mapping,
|
||||||
)
|
)
|
||||||
.loading(is_loading)
|
.loading(is_loading)
|
||||||
.footer_alignment(Alignment::Center)
|
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
.headers(["Indexer", "Pass/Fail", "Failure Messages"])
|
||||||
.constraints([
|
.constraints([
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ fn draw_add_series_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
series.title.scroll_left_or_reset(
|
series.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 27),
|
get_width_from_percentage(area, 27),
|
||||||
*series == current_selection,
|
*series == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ fn draw_episode_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
|||||||
source_title.scroll_left_or_reset(
|
source_title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 40),
|
get_width_from_percentage(area, 40),
|
||||||
current_selection == *history_item,
|
current_selection == *history_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
@@ -415,7 +415,7 @@ fn draw_episode_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
get_width_from_percentage(area, 30),
|
get_width_from_percentage(area, 30),
|
||||||
current_selection == *release
|
current_selection == *release
|
||||||
&& active_sonarr_block != ActiveSonarrBlock::ManualEpisodeSearchConfirmPrompt,
|
&& active_sonarr_block != ActiveSonarrBlock::ManualEpisodeSearchConfirmPrompt,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let size = convert_to_gb(*size);
|
let size = convert_to_gb(*size);
|
||||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
series.title.scroll_left_or_reset(
|
series.title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 23),
|
get_width_from_percentage(area, 23),
|
||||||
*series == current_selection,
|
*series == current_selection,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let monitored = if series.monitored { "🏷" } else { "" };
|
let monitored = if series.monitored { "🏷" } else { "" };
|
||||||
let certification = series.certification.clone().unwrap_or_default();
|
let certification = series.certification.clone().unwrap_or_default();
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ fn draw_season_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
source_title.scroll_left_or_reset(
|
source_title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 40),
|
get_width_from_percentage(area, 40),
|
||||||
current_selection == *history_item,
|
current_selection == *history_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
@@ -372,7 +372,7 @@ fn draw_season_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
get_width_from_percentage(area, 30),
|
get_width_from_percentage(area, 30),
|
||||||
current_selection == *release
|
current_selection == *release
|
||||||
&& active_sonarr_block != ActiveSonarrBlock::ManualSeasonSearchConfirmPrompt,
|
&& active_sonarr_block != ActiveSonarrBlock::ManualSeasonSearchConfirmPrompt,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
let size = convert_to_gb(*size);
|
let size = convert_to_gb(*size);
|
||||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
source_title.scroll_left_or_reset(
|
source_title.scroll_left_or_reset(
|
||||||
get_width_from_percentage(area, 40),
|
get_width_from_percentage(area, 40),
|
||||||
current_selection == *history_item,
|
current_selection == *history_item,
|
||||||
app.tick_count % app.ticks_until_scroll == 0,
|
app.tick_count.is_multiple_of(app.ticks_until_scroll),
|
||||||
);
|
);
|
||||||
|
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use ratatui::layout::{Alignment, Rect};
|
use ratatui::layout::Rect;
|
||||||
use ratatui::text::{Span, Text};
|
use ratatui::text::{Span, Text};
|
||||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
@@ -60,7 +60,7 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
|
|
||||||
if app.data.sonarr_data.log_details.items.is_empty() {
|
if app.data.sonarr_data.log_details.items.is_empty() {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading).size(Size::Large).block(block);
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
return;
|
return;
|
||||||
@@ -73,7 +73,10 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
})
|
})
|
||||||
.block(borderless_block());
|
.block(borderless_block());
|
||||||
let popup = Popup::new(logs_list).size(Size::Large).block(block);
|
let popup = Popup::new(logs_list)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
@@ -93,7 +96,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
|||||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping)
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping)
|
||||||
.loading(app.is_loading)
|
.loading(app.is_loading)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.footer_alignment(Alignment::Center)
|
|
||||||
.headers(TASK_TABLE_HEADERS)
|
.headers(TASK_TABLE_HEADERS)
|
||||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
|
|
||||||
@@ -128,12 +130,15 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
|||||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||||
.block(borderless_block())
|
.block(borderless_block())
|
||||||
.scroll((app.data.sonarr_data.updates.offset, 0));
|
.scroll((app.data.sonarr_data.updates.offset, 0));
|
||||||
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block);
|
let popup = Popup::new(updates_paragraph)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
} else {
|
} else {
|
||||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
let popup = Popup::new(loading).size(Size::Large).block(block);
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
f.render_widget(popup, f.area());
|
f.render_widget(popup, f.area());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,16 @@ use super::message::Message;
|
|||||||
use super::popup::Size;
|
use super::popup::Size;
|
||||||
use crate::models::stateful_table::StatefulTable;
|
use crate::models::stateful_table::StatefulTable;
|
||||||
use crate::ui::styles::ManagarrStyle;
|
use crate::ui::styles::ManagarrStyle;
|
||||||
use crate::ui::utils::{
|
use crate::ui::utils::{borderless_block, centered_rect, title_block_centered};
|
||||||
borderless_block, centered_rect, layout_block_top_border, title_block_centered,
|
|
||||||
};
|
|
||||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
use crate::ui::widgets::popup::Popup;
|
use crate::ui::widgets::popup::Popup;
|
||||||
use crate::ui::widgets::selectable_list::SelectableList;
|
use crate::ui::widgets::selectable_list::SelectableList;
|
||||||
use crate::ui::HIGHLIGHT_SYMBOL;
|
use crate::ui::HIGHLIGHT_SYMBOL;
|
||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::{Alignment, Constraint, Layout, Position, Rect};
|
use ratatui::layout::{Constraint, Layout, Position, Rect};
|
||||||
use ratatui::prelude::{Style, Stylize, Text};
|
use ratatui::prelude::{Style, Stylize, Text};
|
||||||
use ratatui::widgets::{Block, ListItem, Paragraph, Row, StatefulWidget, Table, Widget, WidgetRef};
|
use ratatui::widgets::{Block, ListItem, Row, StatefulWidget, Table, Widget, WidgetRef};
|
||||||
use ratatui::Frame;
|
use ratatui::Frame;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
@@ -36,8 +34,6 @@ where
|
|||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
constraints: Vec<Constraint>,
|
constraints: Vec<Constraint>,
|
||||||
row_mapper: F,
|
row_mapper: F,
|
||||||
footer: Option<String>,
|
|
||||||
footer_alignment: Alignment,
|
|
||||||
block: Block<'a>,
|
block: Block<'a>,
|
||||||
margin: u16,
|
margin: u16,
|
||||||
#[setters(rename = "loading")]
|
#[setters(rename = "loading")]
|
||||||
@@ -68,8 +64,6 @@ where
|
|||||||
table_headers: Vec::new(),
|
table_headers: Vec::new(),
|
||||||
constraints: Vec::new(),
|
constraints: Vec::new(),
|
||||||
row_mapper,
|
row_mapper,
|
||||||
footer: None,
|
|
||||||
footer_alignment: Alignment::Left,
|
|
||||||
block: borderless_block(),
|
block: borderless_block(),
|
||||||
margin: 0,
|
margin: 0,
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
@@ -119,20 +113,12 @@ where
|
|||||||
|
|
||||||
fn render_table(self, area: Rect, buf: &mut Buffer) {
|
fn render_table(self, area: Rect, buf: &mut Buffer) {
|
||||||
let table_headers = self.parse_headers();
|
let table_headers = self.parse_headers();
|
||||||
let table_area = if let Some(ref footer) = self.footer {
|
let table_area = {
|
||||||
let [content_area, footer_area] =
|
let [content_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
|
||||||
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
|
|
||||||
.margin(self.margin)
|
.margin(self.margin)
|
||||||
.areas(area);
|
.areas(area);
|
||||||
|
|
||||||
Paragraph::new(Text::from(format!(" {footer}").help()))
|
|
||||||
.block(layout_block_top_border())
|
|
||||||
.alignment(self.footer_alignment)
|
|
||||||
.render(footer_area, buf);
|
|
||||||
|
|
||||||
content_area
|
content_area
|
||||||
} else {
|
|
||||||
area
|
|
||||||
};
|
};
|
||||||
let loading_block = LoadingBlock::new(self.is_loading, self.block.clone());
|
let loading_block = LoadingBlock::new(self.is_loading, self.block.clone());
|
||||||
|
|
||||||
@@ -230,19 +216,11 @@ where
|
|||||||
|
|
||||||
pub fn show_cursor(&self, f: &mut Frame<'_>, area: Rect) {
|
pub fn show_cursor(&self, f: &mut Frame<'_>, area: Rect) {
|
||||||
let mut draw_cursor = |length: usize, offset: usize| {
|
let mut draw_cursor = |length: usize, offset: usize| {
|
||||||
let table_area = if self.footer.is_some() {
|
|
||||||
let [content_area, _] = Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
|
|
||||||
.margin(self.margin)
|
|
||||||
.areas(area);
|
|
||||||
content_area
|
|
||||||
} else {
|
|
||||||
area
|
|
||||||
};
|
|
||||||
let popup_area = Rect {
|
let popup_area = Rect {
|
||||||
height: 7,
|
height: 7,
|
||||||
..centered_rect(30, 20, table_area)
|
..centered_rect(30, 20, area)
|
||||||
};
|
};
|
||||||
let [text_box_area, _] = Layout::vertical([Constraint::Length(3), Constraint::Length(1)])
|
let [text_box_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.areas(popup_area);
|
.areas(popup_area);
|
||||||
f.set_cursor_position(Position {
|
f.set_cursor_position(Position {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ mod tests {
|
|||||||
use crate::ui::utils::borderless_block;
|
use crate::ui::utils::borderless_block;
|
||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use ratatui::layout::{Alignment, Constraint};
|
use ratatui::layout::Constraint;
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
@@ -25,8 +25,6 @@ mod tests {
|
|||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
assert_eq!(managarr_table.constraints, Vec::new());
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, borderless_block());
|
assert_eq!(managarr_table.block, borderless_block());
|
||||||
assert_eq!(managarr_table.margin, 0);
|
assert_eq!(managarr_table.margin, 0);
|
||||||
assert!(!managarr_table.is_loading);
|
assert!(!managarr_table.is_loading);
|
||||||
@@ -61,8 +59,6 @@ mod tests {
|
|||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
assert_eq!(managarr_table.constraints, Vec::new());
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, borderless_block());
|
assert_eq!(managarr_table.block, borderless_block());
|
||||||
assert_eq!(managarr_table.margin, 0);
|
assert_eq!(managarr_table.margin, 0);
|
||||||
assert!(!managarr_table.is_loading);
|
assert!(!managarr_table.is_loading);
|
||||||
@@ -97,8 +93,6 @@ mod tests {
|
|||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
assert_eq!(managarr_table.constraints, Vec::new());
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, borderless_block());
|
assert_eq!(managarr_table.block, borderless_block());
|
||||||
assert_eq!(managarr_table.margin, 0);
|
assert_eq!(managarr_table.margin, 0);
|
||||||
assert!(!managarr_table.is_loading);
|
assert!(!managarr_table.is_loading);
|
||||||
@@ -130,8 +124,6 @@ mod tests {
|
|||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
assert_eq!(managarr_table.content.unwrap().items, items);
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
||||||
assert_eq!(managarr_table.constraints, Vec::new());
|
assert_eq!(managarr_table.constraints, Vec::new());
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, borderless_block());
|
assert_eq!(managarr_table.block, borderless_block());
|
||||||
assert_eq!(managarr_table.margin, 0);
|
assert_eq!(managarr_table.margin, 0);
|
||||||
assert!(!managarr_table.is_loading);
|
assert!(!managarr_table.is_loading);
|
||||||
@@ -163,8 +155,6 @@ mod tests {
|
|||||||
assert_eq!(managarr_table.content.unwrap().items, items);
|
assert_eq!(managarr_table.content.unwrap().items, items);
|
||||||
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
|
||||||
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
assert_eq!(managarr_table.table_headers, Vec::<String>::new());
|
||||||
assert_eq!(managarr_table.footer, None);
|
|
||||||
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
|
|
||||||
assert_eq!(managarr_table.block, borderless_block());
|
assert_eq!(managarr_table.block, borderless_block());
|
||||||
assert_eq!(managarr_table.margin, 0);
|
assert_eq!(managarr_table.margin, 0);
|
||||||
assert!(!managarr_table.is_loading);
|
assert!(!managarr_table.is_loading);
|
||||||
|
|||||||
+13
-2
@@ -1,6 +1,6 @@
|
|||||||
use crate::ui::utils::{background_block, centered_rect};
|
use crate::ui::utils::{background_block, centered_rect};
|
||||||
use ratatui::buffer::Buffer;
|
use ratatui::buffer::Buffer;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::{Constraint, Layout, Rect};
|
||||||
use ratatui::widgets::{Block, Clear, Widget};
|
use ratatui::widgets::{Block, Clear, Widget};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -51,6 +51,7 @@ impl Size {
|
|||||||
|
|
||||||
pub struct Popup<'a, T: Widget> {
|
pub struct Popup<'a, T: Widget> {
|
||||||
widget: T,
|
widget: T,
|
||||||
|
margin: u16,
|
||||||
percent_x: u16,
|
percent_x: u16,
|
||||||
percent_y: u16,
|
percent_y: u16,
|
||||||
block: Option<Block<'a>>,
|
block: Option<Block<'a>>,
|
||||||
@@ -62,6 +63,7 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
widget,
|
widget,
|
||||||
percent_x: 0,
|
percent_x: 0,
|
||||||
percent_y: 0,
|
percent_y: 0,
|
||||||
|
margin: 0,
|
||||||
block: None,
|
block: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,6 +86,11 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn margin(mut self, margin: u16) -> Self {
|
||||||
|
self.margin = margin;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn render_popup(self, area: Rect, buf: &mut Buffer) {
|
fn render_popup(self, area: Rect, buf: &mut Buffer) {
|
||||||
let mut popup_area = centered_rect(self.percent_x, self.percent_y, area);
|
let mut popup_area = centered_rect(self.percent_x, self.percent_y, area);
|
||||||
let height = if popup_area.height < 3 {
|
let height = if popup_area.height < 3 {
|
||||||
@@ -102,7 +109,11 @@ impl<'a, T: Widget> Popup<'a, T> {
|
|||||||
block.render(popup_area, buf);
|
block.render(popup_area, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.widget.render(popup_area, buf);
|
let [content_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
|
||||||
|
.margin(self.margin)
|
||||||
|
.areas(popup_area);
|
||||||
|
|
||||||
|
self.widget.render(content_area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_x, 0);
|
assert_eq!(popup.percent_x, 0);
|
||||||
assert_eq!(popup.percent_y, 0);
|
assert_eq!(popup.percent_y, 0);
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
|
assert_eq!(popup.margin, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -42,6 +43,7 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_y, 40);
|
assert_eq!(popup.percent_y, 40);
|
||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
|
assert_eq!(popup.margin, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -52,6 +54,7 @@ mod tests {
|
|||||||
assert_eq!(popup.percent_y, 50);
|
assert_eq!(popup.percent_y, 50);
|
||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.block, None);
|
assert_eq!(popup.block, None);
|
||||||
|
assert_eq!(popup.margin, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -62,5 +65,17 @@ mod tests {
|
|||||||
assert_eq!(popup.widget, Block::new());
|
assert_eq!(popup.widget, Block::new());
|
||||||
assert_eq!(popup.percent_x, 0);
|
assert_eq!(popup.percent_x, 0);
|
||||||
assert_eq!(popup.percent_y, 0);
|
assert_eq!(popup.percent_y, 0);
|
||||||
|
assert_eq!(popup.margin, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_popup_margin() {
|
||||||
|
let popup = Popup::new(Block::new()).margin(5);
|
||||||
|
|
||||||
|
assert_eq!(popup.margin, 5);
|
||||||
|
assert_eq!(popup.widget, Block::new());
|
||||||
|
assert_eq!(popup.percent_x, 0);
|
||||||
|
assert_eq!(popup.percent_y, 0);
|
||||||
|
assert_eq!(popup.block, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user