Skip to content

Commit

Permalink
feat: impl naive scrolling for tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
MishkaRogachev committed Oct 2, 2024
1 parent de9fcc0 commit 7dc94a0
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ sha3 = "0.10.8"
secp256k1 = { version = "0.20.3", features = ["rand"] }
hdkey = "0.0.5"
sled = "0.34.7"
ratatui = "0.28.0"
ratatui = "0.28.1"
bip39 = { version = "2.0.0", features = ["rand", "serde", "zeroize"] }
copypasta = "0.10.1"
qrcode = "0.14.1"
Expand Down
25 changes: 19 additions & 6 deletions src/tui/screens/porfolio_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;
use tokio::sync::Mutex;
use ratatui::{
crossterm::event::Event,
layout::{Alignment, Constraint, Direction, Layout, Rect},
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
style::{Color, Style},
widgets::{Paragraph, Widget}, Frame
};
Expand All @@ -21,18 +21,21 @@ pub struct Screen {

accounts: Vec<account::Account>,
busy: controls::Busy,
scroll: controls::Scroll,
}

impl Screen {
pub fn new(session: Session, crypto: Arc<Mutex<Crypto>>) -> Self {
let accounts = vec![account::Account::new(session.account)];
let busy = controls::Busy::new("Loading..");
let scroll = controls::Scroll::new();

Self {
crypto,
last_update: None,
accounts,
busy,
scroll
}
}

Expand Down Expand Up @@ -63,8 +66,9 @@ impl Screen {

#[async_trait::async_trait]
impl AppScreen for Screen {
async fn handle_event(&mut self, _event: Event) -> anyhow::Result<bool> {
return Ok(false);
async fn handle_event(&mut self, event: Event) -> anyhow::Result<bool> {
self.scroll.handle_event(&event);
Ok(false)
}

async fn update(&mut self) {
Expand All @@ -86,7 +90,7 @@ impl AppScreen for Screen {
.direction(Direction::Vertical)
.constraints([
Constraint::Length(SUMMARY_HEIGHT),
Constraint::Fill(0), // Fill height for accounts
Constraint::Fill(0), // Fill height for accounts
])
.split(area);

Expand All @@ -108,10 +112,19 @@ impl AppScreen for Screen {
let accounts_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(self.accounts.iter().map(|_| Constraint::Fill(1)).collect::<Vec<_>>().as_slice())
.split(content_layout[1]);
.split(content_layout[1].inner(Margin {
vertical: 0,
horizontal: 1,
}));

let mut total_content_height = 0;
for (account, account_layout) in self.accounts.iter_mut().zip(accounts_layout.iter()) {
account.scroll_offset = self.scroll.position;
account.render(frame, *account_layout);
total_content_height += account.implicit_height();
}

self.scroll.total = total_content_height;
self.scroll.render(frame, content_layout[1]);
}
}

13 changes: 12 additions & 1 deletion src/tui/widgets/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Account {
pub name: String,
pub address: web3::types::Address,
pub balances: Option<Balances>,
pub scroll_offset: usize,
busy: controls::Busy,
}

Expand All @@ -24,10 +25,15 @@ impl Account {
name: "Master Keypair".to_string(), // TODO: account name
address,
balances: None,
scroll_offset: 0,
busy,
}
}

pub fn implicit_height(&self) -> usize {
3 + if let Some(balances) = &self.balances { balances.len() } else { 0 }
}

pub fn get_total_usd_balance(&self) -> Option<(f64, bool)> {
self.balances.as_ref().map(|balances| {
balances.iter().fold((0.0, false), |(total_usd, from_test), balance| {
Expand Down Expand Up @@ -88,6 +94,10 @@ impl Account {
self.render_total_balances(frame, header_layout[2]);

for i in 0..balances_cnt {
if self.scroll_offset + i >= balances_cnt {
break;
}

let token_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Expand All @@ -98,7 +108,8 @@ impl Account {
])
.split(tokens_layout[i + 1]);

let token = &self.balances.as_ref().unwrap()[i];
// TODO: don't render extra items exeeding the height
let token = &self.balances.as_ref().unwrap()[i + self.scroll_offset];
let token_label = Paragraph::new(format!("{}", token.currency))
.style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Left);
Expand Down
64 changes: 62 additions & 2 deletions src/tui/widgets/controls.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::collections::HashMap;

use ratatui::{
crossterm::event::{Event, KeyCode, KeyEvent, MouseButton, MouseEventKind},
crossterm::event::{Event, KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind},
layout::{Alignment, Constraint, Direction, Layout, Position, Rect},
style::{Color, Modifier, Style, Stylize},
symbols,
text::{Line, Span},
widgets::{Block, Borders, Clear, Gauge, Paragraph, Widget},
widgets::{Block, Borders, Clear, Gauge, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, Widget},
Frame
};
use zeroize::Zeroizing;
Expand Down Expand Up @@ -99,6 +99,12 @@ pub struct Busy {
start_time: tokio::time::Instant,
}

pub struct Scroll {
pub total: usize,
pub visible: usize,
pub position: usize,
}

impl Button {
pub fn new(label: &str, hotkey: Option<char>) -> Self {
Button {
Expand Down Expand Up @@ -693,4 +699,58 @@ impl Busy {
.alignment(Alignment::Right)
.render(area, frame.buffer_mut());
}
}

impl Scroll {
pub fn new() -> Self {
Self {
total: 0,
visible: 0,
position: 0,
}
}

pub fn handle_event(&mut self, event: &Event) -> Option<usize> {
if self.visible >= self.total {
return None;
}

match event {
Event::Key(KeyEvent { code: KeyCode::Up, .. }) |
Event::Mouse(MouseEvent { kind: MouseEventKind::ScrollUp, .. }) => {
if self.position > 0 {
self.position -= 1;
return Some(self.position);
}
},
Event::Key(KeyEvent { code: KeyCode::Down, .. }) |
Event::Mouse(MouseEvent { kind: MouseEventKind::ScrollDown, .. }) => {
if self.position < self.total - self.visible {
self.position += 1;
return Some(self.position);
}
},
_ => {}
}
None
}

pub fn render(&mut self, frame: &mut Frame, area: Rect) {
self.visible = area.height as usize;

if self.total >= self.visible {
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
.begin_symbol(Some("⬆"))
.end_symbol(Some("⬇"))
.track_symbol(None)
.style(Style::default().fg(Color::Yellow));

let mut scrollbar_state = ScrollbarState::new(self.total - self.visible)
.position(self.position);

frame.render_stateful_widget(scrollbar, area, &mut scrollbar_state);
} else {
self.position = 0;
}
}
}

0 comments on commit 7dc94a0

Please sign in to comment.