feat: separate albums/tracks into bordered panes, fix click handling
- Replace single Detail pane with separate Albums and Tracks panes, each with its own border that highlights yellow when focused - Store rendered Rect areas on LibraryState instead of hardcoding layout offsets, fixing album click selection - Split render functions into public components for isolated testing - Restructure snapshot tests by component (artists, albums, tracks, header, detail) — 13 tests expanded to 26
This commit is contained in:
+29
-32
@@ -111,7 +111,7 @@ impl App {
|
||||
|
||||
match self.tab {
|
||||
Tab::Library => {
|
||||
self.handle_library_click(x, rel_y);
|
||||
self.handle_library_click(x, y);
|
||||
}
|
||||
Tab::Wanted => {
|
||||
self.select_list_item(&mut self.wanted_state.clone(), self.wanted.len(), rel_y);
|
||||
@@ -133,42 +133,39 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_library_click(&mut self, x: u16, rel_y: usize) {
|
||||
const ARTISTS_PANE_WIDTH: u16 = 32;
|
||||
const BORDER_TOP: usize = 1;
|
||||
const HEADER_HEIGHT: usize = 6;
|
||||
const DIVIDER_HEIGHT: usize = 1;
|
||||
const ALBUMS_START_ROW: usize = BORDER_TOP + HEADER_HEIGHT + DIVIDER_HEIGHT;
|
||||
fn handle_library_click(&mut self, x: u16, y: u16) {
|
||||
let artists = self.library.artists_inner_area;
|
||||
let albums = self.library.albums_inner_area;
|
||||
let tracks = self.library.tracks_inner_area;
|
||||
|
||||
if x < ARTISTS_PANE_WIDTH {
|
||||
if rel_y > 0 && rel_y <= self.library.artists.len() {
|
||||
self.library.artist_state.select(Some(rel_y - 1));
|
||||
if x >= artists.x && x < artists.x + artists.width
|
||||
&& y >= artists.y && y < artists.y + artists.height
|
||||
{
|
||||
let row = (y - artists.y) as usize;
|
||||
if row < self.library.artists.len() {
|
||||
self.library.artist_state.select(Some(row));
|
||||
self.library.album_state.select(Some(0));
|
||||
self.library.track_state.select(Some(0));
|
||||
self.library.focus = LibraryFocus::Artists;
|
||||
}
|
||||
} else if rel_y >= ALBUMS_START_ROW {
|
||||
let album_row = rel_y - ALBUMS_START_ROW;
|
||||
let content_height = self.main_area.height.saturating_sub(10) as usize;
|
||||
let albums_section_height = (content_height * 40) / 100;
|
||||
let tracks_start_row = ALBUMS_START_ROW + albums_section_height + DIVIDER_HEIGHT;
|
||||
|
||||
if rel_y < tracks_start_row {
|
||||
if let Some(artist) = self.library.selected_artist()
|
||||
&& album_row < artist.albums.len()
|
||||
{
|
||||
self.library.album_state.select(Some(album_row));
|
||||
self.library.track_state.select(Some(0));
|
||||
self.library.focus = LibraryFocus::Albums;
|
||||
}
|
||||
} else {
|
||||
let track_row = rel_y - tracks_start_row;
|
||||
if let Some(album) = self.library.selected_album()
|
||||
&& track_row < album.total as usize
|
||||
{
|
||||
self.library.track_state.select(Some(track_row));
|
||||
self.library.focus = LibraryFocus::Tracks;
|
||||
}
|
||||
} else if x >= albums.x && x < albums.x + albums.width
|
||||
&& y >= albums.y && y < albums.y + albums.height
|
||||
{
|
||||
let row = (y - albums.y) as usize;
|
||||
if let Some(artist) = self.library.selected_artist()
|
||||
&& row < artist.albums.len()
|
||||
{
|
||||
self.library.album_state.select(Some(row));
|
||||
self.library.track_state.select(Some(0));
|
||||
self.library.focus = LibraryFocus::Albums;
|
||||
}
|
||||
} else if x >= tracks.x && x < tracks.x + tracks.width
|
||||
&& y >= tracks.y && y < tracks.y + tracks.height
|
||||
{
|
||||
let row = (y - tracks.y) as usize;
|
||||
if row < self.library.tracks.len() {
|
||||
self.library.track_state.select(Some(row));
|
||||
self.library.focus = LibraryFocus::Tracks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::widgets::ListState;
|
||||
|
||||
use crate::data::{Album, Artist, Track};
|
||||
@@ -21,6 +22,9 @@ pub struct LibraryState {
|
||||
pub artist_state: ListState,
|
||||
pub album_state: ListState,
|
||||
pub track_state: ListState,
|
||||
pub(crate) artists_inner_area: Rect,
|
||||
pub(crate) albums_inner_area: Rect,
|
||||
pub(crate) tracks_inner_area: Rect,
|
||||
tracks_cache: HashMap<String, Vec<Track>>,
|
||||
pending_album_id: Option<String>,
|
||||
}
|
||||
@@ -46,6 +50,9 @@ impl LibraryState {
|
||||
artist_state,
|
||||
album_state,
|
||||
track_state,
|
||||
artists_inner_area: Rect::default(),
|
||||
albums_inner_area: Rect::default(),
|
||||
tracks_inner_area: Rect::default(),
|
||||
tracks_cache: HashMap::new(),
|
||||
pending_album_id: None,
|
||||
}
|
||||
|
||||
+37
-56
@@ -12,7 +12,7 @@ use crate::application::library_state::{LibraryFocus, LibraryState};
|
||||
use crate::data::{Album, AlbumStatus, Artist, MonitorState};
|
||||
use crate::domain::aggregates::artist_status;
|
||||
use crate::theme;
|
||||
use crate::ui::pane::{Pane, section_divider};
|
||||
use crate::ui::pane::Pane;
|
||||
use crate::ui::progress_bar::progress_bar;
|
||||
|
||||
fn status_icon(status: AlbumStatus, monitored: bool) -> (char, Style) {
|
||||
@@ -64,7 +64,7 @@ pub fn render_library(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
render_detail_pane(frame, chunks[1], state);
|
||||
}
|
||||
|
||||
fn render_artists_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
pub fn render_artists_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
let focused = state.focus == LibraryFocus::Artists;
|
||||
let artist_count = state.artists.len();
|
||||
|
||||
@@ -89,6 +89,7 @@ fn render_artists_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState)
|
||||
let block = pane.build_block();
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
state.artists_inner_area = inner;
|
||||
|
||||
let items: Vec<ListItem> = state
|
||||
.artists
|
||||
@@ -131,70 +132,40 @@ fn render_artists_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState)
|
||||
frame.render_stateful_widget(list, inner, &mut state.artist_state);
|
||||
}
|
||||
|
||||
fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
let focused = state.focus == LibraryFocus::Albums || state.focus == LibraryFocus::Tracks;
|
||||
pub fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
frame.render_widget(
|
||||
Paragraph::new("").style(Style::default().bg(theme::BG0)),
|
||||
area,
|
||||
);
|
||||
|
||||
let artist = state.selected_artist();
|
||||
|
||||
let meta = artist
|
||||
.map(|a| {
|
||||
format!(
|
||||
"{} · {}",
|
||||
a.country,
|
||||
a.genres.first().map(|s| s.as_str()).unwrap_or("")
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let have_tracks: u16 = artist
|
||||
.map(|a| a.albums.iter().map(|al| al.have).sum())
|
||||
.unwrap_or(0);
|
||||
let total_tracks: u16 = artist
|
||||
.map(|a| a.albums.iter().map(|al| al.total).sum())
|
||||
.unwrap_or(0);
|
||||
|
||||
let footer = if artist.is_some() {
|
||||
Line::from(vec![Span::styled(
|
||||
format!("{}/{} tracks", have_tracks, total_tracks),
|
||||
Style::default().fg(theme::GRAY),
|
||||
)])
|
||||
} else {
|
||||
Line::from("")
|
||||
};
|
||||
|
||||
let pane = Pane::new("Detail")
|
||||
.meta(&meta)
|
||||
.focused(focused)
|
||||
.footer(footer);
|
||||
|
||||
let block = pane.build_block();
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let Some(artist) = artist else {
|
||||
let Some(artist) = state.selected_artist() else {
|
||||
let msg = Paragraph::new(Span::styled(
|
||||
"No artist selected",
|
||||
Style::default().fg(theme::GRAY),
|
||||
));
|
||||
frame.render_widget(msg, inner);
|
||||
frame.render_widget(msg, area);
|
||||
return;
|
||||
};
|
||||
|
||||
let chunks = Layout::vertical([
|
||||
Constraint::Length(6),
|
||||
Constraint::Length(1),
|
||||
Constraint::Percentage(40),
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.split(inner);
|
||||
.split(area);
|
||||
|
||||
render_artist_header(frame, chunks[0], artist);
|
||||
|
||||
let albums_count = artist.albums.len();
|
||||
let albums_label = format!("{} releases", albums_count);
|
||||
let album_divider = section_divider("albums", Some(&albums_label));
|
||||
frame.render_widget(Paragraph::new(album_divider), chunks[1]);
|
||||
let albums_focused = state.focus == LibraryFocus::Albums;
|
||||
let albums_pane = Pane::new("Albums")
|
||||
.meta(&albums_label)
|
||||
.focused(albums_focused);
|
||||
let albums_block = albums_pane.build_block();
|
||||
let albums_inner = albums_block.inner(chunks[1]);
|
||||
frame.render_widget(albums_block, chunks[1]);
|
||||
state.albums_inner_area = albums_inner;
|
||||
|
||||
let selected_artist_idx = state.artist_state.selected();
|
||||
if let Some(idx) = selected_artist_idx
|
||||
@@ -202,7 +173,7 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
{
|
||||
let albums = artist.albums.clone();
|
||||
let focus = state.focus;
|
||||
render_albums_list(frame, chunks[2], &albums, focus, &mut state.album_state);
|
||||
render_albums_list(frame, albums_inner, &albums, focus, &mut state.album_state);
|
||||
}
|
||||
|
||||
let album_title = state
|
||||
@@ -213,14 +184,24 @@ fn render_detail_pane(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
.selected_album()
|
||||
.map(|a| format!("{}/{}", a.have, a.total))
|
||||
.unwrap_or_default();
|
||||
let track_label = format!("tracks · {}", album_title);
|
||||
let track_divider = section_divider(&track_label, Some(&track_counts));
|
||||
frame.render_widget(Paragraph::new(track_divider), chunks[3]);
|
||||
let track_meta = if album_title.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{} · {}", album_title, track_counts)
|
||||
};
|
||||
let tracks_focused = state.focus == LibraryFocus::Tracks;
|
||||
let tracks_pane = Pane::new("Tracks")
|
||||
.meta(&track_meta)
|
||||
.focused(tracks_focused);
|
||||
let tracks_block = tracks_pane.build_block();
|
||||
let tracks_inner = tracks_block.inner(chunks[2]);
|
||||
frame.render_widget(tracks_block, chunks[2]);
|
||||
state.tracks_inner_area = tracks_inner;
|
||||
|
||||
render_tracks_list(frame, chunks[4], state);
|
||||
render_tracks_list(frame, tracks_inner, state);
|
||||
}
|
||||
|
||||
fn render_artist_header(frame: &mut Frame, area: Rect, artist: &Artist) {
|
||||
pub fn render_artist_header(frame: &mut Frame, area: Rect, artist: &Artist) {
|
||||
let have: u16 = artist.albums.iter().map(|a| a.have).sum();
|
||||
let total: u16 = artist.albums.iter().map(|a| a.total).sum();
|
||||
|
||||
@@ -287,7 +268,7 @@ fn render_artist_header(frame: &mut Frame, area: Rect, artist: &Artist) {
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
fn render_albums_list(
|
||||
pub fn render_albums_list(
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
albums: &[Album],
|
||||
@@ -348,7 +329,7 @@ fn render_albums_list(
|
||||
frame.render_stateful_widget(list, area, album_state);
|
||||
}
|
||||
|
||||
fn render_tracks_list(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
pub fn render_tracks_list(frame: &mut Frame, area: Rect, state: &mut LibraryState) {
|
||||
let focused = state.focus == LibraryFocus::Tracks;
|
||||
|
||||
if state.tracks.is_empty() {
|
||||
|
||||
+4
-1
@@ -1,2 +1,5 @@
|
||||
pub use crate::application::library_state::{LibraryFocus, LibraryState};
|
||||
pub use crate::presentation::library::render_library;
|
||||
pub use crate::presentation::library::{
|
||||
render_albums_list, render_artist_header, render_artists_pane, render_detail_pane,
|
||||
render_library, render_tracks_list,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"● Complete Album [Album] 2020 ▰▰▰▰▰▰▰▰▰▰ 10/10 FLAC "
|
||||
"○ Wanted Album [Album] 2021 ▱▱▱▱▱▱▱▱▱▱ 0/8 — "
|
||||
"◌ Unmonitored Single [Single] 2022 ▱▱▱▱▱▱▱▱▱▱ 0/1 — "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC "
|
||||
"◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC "
|
||||
"◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"Radiohead "
|
||||
" "
|
||||
"status ● Monitored path /music/Radiohead "
|
||||
"quality FLAC size 2.5 GB "
|
||||
"albums 2 tracks 17 / 22 "
|
||||
" "
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"Tool "
|
||||
" "
|
||||
"status ◌ Unmonitored path /music/Tool "
|
||||
"quality FLAC size 3.8 GB "
|
||||
"albums 0 tracks 0 / 0 "
|
||||
" "
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 0 ]──────────────┐"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────┘"
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 2 ]──────────────┐"
|
||||
"│◐ Radiohead 17/22 │"
|
||||
"│! Pink Floyd 0/10 │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────┘"
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 2 ]──────────────┐"
|
||||
"│◐ Radiohead 17/22 │"
|
||||
"│! Pink Floyd 0/10 │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────┘"
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"No artist selected "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"Radiohead "
|
||||
" "
|
||||
"status ● Monitored path /music/Radiohead "
|
||||
"quality FLAC size 2.5 GB "
|
||||
"albums 2 tracks 17 / 22 "
|
||||
" "
|
||||
"┌─[ Albums · 2 releases ]──────────────────────────────────────────┐"
|
||||
"│● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC │"
|
||||
"│◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────────────────────────────────────────┘"
|
||||
"┌─[ Tracks · OK Computer · 12/12 ]─────────────────────────────────┐"
|
||||
"│(no album selected) │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────────────────────────────────────────┘"
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"Radiohead "
|
||||
" "
|
||||
"status ● Monitored path /music/Radiohead "
|
||||
"quality FLAC size 2.5 GB "
|
||||
"albums 2 tracks 17 / 22 "
|
||||
" "
|
||||
"┌─[ Albums · 2 releases ]──────────────────────────────────────────┐"
|
||||
"│● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC │"
|
||||
"│◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────────────────────────────────────────┘"
|
||||
"┌─[ Tracks · OK Computer · 12/12 ]─────────────────────────────────┐"
|
||||
"│✓ 01 Airbag 4:44 FLAC │"
|
||||
"│✓ 02 Paranoid Android 6:23 FLAC │"
|
||||
"│✗ 03 Subterranean Homesick Alien 4:27 — │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"│ │"
|
||||
"└──────────────────────────────────────────────────────────────────┘"
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 0 ]──────────────┐No artist selected "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"│ │ "
|
||||
"└──────────────────────────────┘ "
|
||||
+10
-10
@@ -2,14 +2,13 @@
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 2 ]──────────────┐┌─[ Detail · UK · Alternative ]────────────────────────────────────┐"
|
||||
"│◐ Radiohead 17/22 ││Radiohead │"
|
||||
"│! Pink Floyd 0/10 ││ │"
|
||||
"│ ││status ● Monitored path /music/Radiohead │"
|
||||
"│ ││quality FLAC size 2.5 GB │"
|
||||
"│ ││albums 2 tracks 17 / 22 │"
|
||||
"│ ││ │"
|
||||
"│ ││─ albums ─ 2 releases │"
|
||||
"┌─[ Artists · 2 ]──────────────┐Radiohead "
|
||||
"│◐ Radiohead 17/22 │ "
|
||||
"│! Pink Floyd 0/10 │status ● Monitored path /music/Radiohead "
|
||||
"│ │quality FLAC size 2.5 GB "
|
||||
"│ │albums 2 tracks 17 / 22 "
|
||||
"│ │ "
|
||||
"│ │┌─[ Albums · 2 releases ]──────────────────────────────────────────┐"
|
||||
"│ ││● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC │"
|
||||
"│ ││◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC │"
|
||||
"│ ││ │"
|
||||
@@ -20,8 +19,8 @@ expression: terminal.backend()
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││─ tracks · OK Computer ─ 12/12 │"
|
||||
"│ │└──────────────────────────────────────────────────────────────────┘"
|
||||
"│ │┌─[ Tracks · OK Computer · 12/12 ]─────────────────────────────────┐"
|
||||
"│ ││(no album selected) │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
@@ -31,4 +30,5 @@ expression: terminal.backend()
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└──────────────────────────────┘└──────────────────────────────────────────────────────────────────┘"
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 0 ]──────────────┐┌─[ Detail · ]────────────────────────────────────────────────────┐"
|
||||
"│ ││No artist selected │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└──────────────────────────────┘└──────────────────────────────────────────────────────────────────┘"
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"┌─[ Artists · 2 ]──────────────┐┌─[ Detail · UK · Alternative ]────────────────────────────────────┐"
|
||||
"│◐ Radiohead 17/22 ││Radiohead │"
|
||||
"│! Pink Floyd 0/10 ││ │"
|
||||
"│ ││status ● Monitored path /music/Radiohead │"
|
||||
"│ ││quality FLAC size 2.5 GB │"
|
||||
"│ ││albums 2 tracks 17 / 22 │"
|
||||
"│ ││ │"
|
||||
"│ ││─ albums ─ 2 releases │"
|
||||
"│ ││● OK Computer [Album] 1997 ▰▰▰▰▰▰▰▰▰▰ 12/12 FLAC │"
|
||||
"│ ││◐ Kid A [Album] 2000 ▰▰▰▰▰▱▱▱▱▱ 5/10 FLAC │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││─ tracks · OK Computer ─ 12/12 │"
|
||||
"│ ││(no album selected) │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"│ ││ │"
|
||||
"└──────────────────────────────┘└──────────────────────────────────────────────────────────────────┘"
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"(no album selected) "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"✓ 01 Airbag 4:44 FLAC "
|
||||
"✓ 02 Paranoid Android 6:23 FLAC "
|
||||
"✗ 03 Subterranean Homesick Alien 4:27 — "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: tests/ui_snapshots.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
"✓ 01 Airbag 4:44 FLAC "
|
||||
"✓ 02 Paranoid Android 6:23 FLAC "
|
||||
"✗ 03 Subterranean Homesick Alien 4:27 — "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
+309
-45
@@ -1,7 +1,10 @@
|
||||
use ratatui::{Terminal, backend::TestBackend, widgets::Paragraph};
|
||||
use ratatui::{backend::TestBackend, widgets::Paragraph, Terminal};
|
||||
use ui_agregator::app::Tab;
|
||||
use ui_agregator::data::{Album, AlbumStatus, Artist, MonitorState};
|
||||
use ui_agregator::ui::library::{LibraryFocus, LibraryState, render_library};
|
||||
use ui_agregator::data::{Album, AlbumStatus, Artist, MonitorState, Track};
|
||||
use ui_agregator::ui::library::{
|
||||
LibraryFocus, LibraryState, render_albums_list, render_artist_header, render_artists_pane,
|
||||
render_detail_pane, render_library, render_tracks_list,
|
||||
};
|
||||
use ui_agregator::ui::modals::render_help_modal;
|
||||
use ui_agregator::ui::progress_bar::progress_bar;
|
||||
use ui_agregator::ui::topbar::render_topbar;
|
||||
@@ -66,6 +69,38 @@ fn test_artists() -> Vec<Artist> {
|
||||
]
|
||||
}
|
||||
|
||||
fn test_tracks() -> Vec<Track> {
|
||||
vec![
|
||||
Track {
|
||||
id: "t1".to_string(),
|
||||
number: 1,
|
||||
disc: 1,
|
||||
title: "Airbag".to_string(),
|
||||
duration: "4:44".to_string(),
|
||||
have: true,
|
||||
quality: "FLAC".to_string(),
|
||||
},
|
||||
Track {
|
||||
id: "t2".to_string(),
|
||||
number: 2,
|
||||
disc: 1,
|
||||
title: "Paranoid Android".to_string(),
|
||||
duration: "6:23".to_string(),
|
||||
have: true,
|
||||
quality: "FLAC".to_string(),
|
||||
},
|
||||
Track {
|
||||
id: "t3".to_string(),
|
||||
number: 3,
|
||||
disc: 1,
|
||||
title: "Subterranean Homesick Alien".to_string(),
|
||||
duration: "4:27".to_string(),
|
||||
have: false,
|
||||
quality: "—".to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
mod progress_bar_snapshots {
|
||||
use super::*;
|
||||
|
||||
@@ -143,47 +178,6 @@ mod topbar_snapshots {
|
||||
}
|
||||
}
|
||||
|
||||
mod library_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
|
||||
let mut state = LibraryState::new(vec![]);
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_library(f, f.area(), &mut state);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_artists() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_library(f, f.area(), &mut state);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn albums_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.focus = LibraryFocus::Albums;
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_library(f, f.area(), &mut state);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod help_modal_snapshots {
|
||||
use super::*;
|
||||
|
||||
@@ -210,7 +204,277 @@ mod help_modal_snapshots {
|
||||
}
|
||||
}
|
||||
|
||||
mod library_state {
|
||||
mod library_page_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn full_page_empty() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
|
||||
let mut state = LibraryState::new(vec![]);
|
||||
terminal
|
||||
.draw(|f| render_library(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_page_with_data() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
terminal
|
||||
.draw(|f| render_library(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod artists_pane_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(32, 20)).unwrap();
|
||||
let mut state = LibraryState::new(vec![]);
|
||||
terminal
|
||||
.draw(|f| render_artists_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_artists_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(32, 20)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.focus = LibraryFocus::Artists;
|
||||
terminal
|
||||
.draw(|f| render_artists_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_artists_unfocused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(32, 20)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.focus = LibraryFocus::Albums;
|
||||
terminal
|
||||
.draw(|f| render_artists_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod artist_header_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn monitored_artist() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 6)).unwrap();
|
||||
let artists = test_artists();
|
||||
terminal
|
||||
.draw(|f| render_artist_header(f, f.area(), &artists[0]))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unmonitored_artist() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 6)).unwrap();
|
||||
let artist = Artist {
|
||||
id: "3".to_string(),
|
||||
name: "Tool".to_string(),
|
||||
country: "US".to_string(),
|
||||
genres: vec!["Progressive Metal".to_string()],
|
||||
monitor_state: MonitorState::Unmonitored,
|
||||
path: "/music/Tool".to_string(),
|
||||
quality: "FLAC".to_string(),
|
||||
size_gb: 3.8,
|
||||
albums: vec![],
|
||||
};
|
||||
terminal
|
||||
.draw(|f| render_artist_header(f, f.area(), &artist))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod albums_list_snapshots {
|
||||
use super::*;
|
||||
use ratatui::widgets::ListState;
|
||||
|
||||
#[test]
|
||||
fn with_albums_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 10)).unwrap();
|
||||
let artists = test_artists();
|
||||
let albums = &artists[0].albums;
|
||||
let mut album_state = ListState::default();
|
||||
album_state.select(Some(0));
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_albums_list(
|
||||
f,
|
||||
f.area(),
|
||||
albums,
|
||||
LibraryFocus::Albums,
|
||||
&mut album_state,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_albums_unfocused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 10)).unwrap();
|
||||
let artists = test_artists();
|
||||
let albums = &artists[0].albums;
|
||||
let mut album_state = ListState::default();
|
||||
album_state.select(Some(0));
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_albums_list(
|
||||
f,
|
||||
f.area(),
|
||||
albums,
|
||||
LibraryFocus::Artists,
|
||||
&mut album_state,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_statuses() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 10)).unwrap();
|
||||
let albums = vec![
|
||||
Album {
|
||||
id: "1".to_string(),
|
||||
title: "Complete Album".to_string(),
|
||||
year: 2020,
|
||||
album_type: "Album".to_string(),
|
||||
monitored: true,
|
||||
total: 10,
|
||||
have: 10,
|
||||
quality: "FLAC".to_string(),
|
||||
status: AlbumStatus::Complete,
|
||||
},
|
||||
Album {
|
||||
id: "2".to_string(),
|
||||
title: "Wanted Album".to_string(),
|
||||
year: 2021,
|
||||
album_type: "Album".to_string(),
|
||||
monitored: true,
|
||||
total: 8,
|
||||
have: 0,
|
||||
quality: "—".to_string(),
|
||||
status: AlbumStatus::Wanted,
|
||||
},
|
||||
Album {
|
||||
id: "3".to_string(),
|
||||
title: "Unmonitored Single".to_string(),
|
||||
year: 2022,
|
||||
album_type: "Single".to_string(),
|
||||
monitored: false,
|
||||
total: 1,
|
||||
have: 0,
|
||||
quality: "—".to_string(),
|
||||
status: AlbumStatus::Unmonitored,
|
||||
},
|
||||
];
|
||||
let mut album_state = ListState::default();
|
||||
album_state.select(Some(1));
|
||||
terminal
|
||||
.draw(|f| {
|
||||
render_albums_list(
|
||||
f,
|
||||
f.area(),
|
||||
&albums,
|
||||
LibraryFocus::Albums,
|
||||
&mut album_state,
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod tracks_list_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_tracks() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 8)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
terminal
|
||||
.draw(|f| render_tracks_list(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_tracks_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 8)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.tracks = test_tracks();
|
||||
state.focus = LibraryFocus::Tracks;
|
||||
terminal
|
||||
.draw(|f| render_tracks_list(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_tracks_unfocused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 8)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.tracks = test_tracks();
|
||||
state.focus = LibraryFocus::Artists;
|
||||
terminal
|
||||
.draw(|f| render_tracks_list(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod detail_pane_snapshots {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_artist_selected() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 24)).unwrap();
|
||||
let mut state = LibraryState::new(vec![]);
|
||||
terminal
|
||||
.draw(|f| render_detail_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_artist_albums_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 24)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.focus = LibraryFocus::Albums;
|
||||
terminal
|
||||
.draw(|f| render_detail_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_artist_tracks_focused() {
|
||||
let mut terminal = Terminal::new(TestBackend::new(68, 24)).unwrap();
|
||||
let mut state = LibraryState::new(test_artists());
|
||||
state.tracks = test_tracks();
|
||||
state.focus = LibraryFocus::Tracks;
|
||||
terminal
|
||||
.draw(|f| render_detail_pane(f, f.area(), &mut state))
|
||||
.unwrap();
|
||||
insta::assert_snapshot!(terminal.backend());
|
||||
}
|
||||
}
|
||||
|
||||
mod library_state_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user