Skip to content

Commit 663d505

Browse files
committed
allow switching systems in tui
1 parent b11f012 commit 663d505

14 files changed

Lines changed: 262 additions & 27 deletions

File tree

coman/src/app/ids.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ pub enum Id {
77
InfoPopup,
88
ErrorPopup,
99
LoginPopup,
10+
SystemSelectPopup,
1011
}

coman/src/app/messages.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
use crate::cscs::api_client::System;
2+
13
#[derive(Debug, PartialEq)]
24
pub enum MenuMsg {
35
Opened,
46
Closed,
57
CscsLogin,
8+
CscsSwitchSystem,
69
}
710

811
#[derive(Debug, PartialEq)]
@@ -22,10 +25,18 @@ pub enum LoginPopupMsg {
2225
Closed,
2326
LoginDone(String, String),
2427
}
28+
#[derive(Debug, PartialEq)]
29+
pub enum SystemSelectMsg {
30+
Opened(Vec<System>),
31+
Closed,
32+
SystemSelected(String),
33+
}
2534

2635
#[derive(Debug, PartialEq)]
2736
pub enum CscsMsg {
2837
Login(String, String),
38+
SelectSystem,
39+
SystemSelected(String),
2940
}
3041

3142
#[derive(Debug, PartialEq)]
@@ -35,6 +46,7 @@ pub enum Msg {
3546
InfoPopup(InfoPopupMsg),
3647
ErrorPopup(ErrorPopupMsg),
3748
LoginPopup(LoginPopupMsg),
49+
SystemSelectPopup(SystemSelectMsg),
3850
Error(String),
3951
Info(String),
4052
Cscs(CscsMsg),

coman/src/app/model.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ use tuirealm::{
99
use crate::{
1010
app::{
1111
ids::Id,
12-
messages::{CscsMsg, ErrorPopupMsg, InfoPopupMsg, LoginPopupMsg, MenuMsg, Msg},
12+
messages::{
13+
CscsMsg, ErrorPopupMsg, InfoPopupMsg, LoginPopupMsg, MenuMsg, Msg, SystemSelectMsg,
14+
},
1315
user_events::UserEvent,
1416
},
1517
components::{
1618
error_popup::ErrorPopup, info_popup::InfoPopup, login_popup::LoginPopup,
17-
workload_menu::WorkloadMenu,
19+
system_select_popup::SystemSelectPopup, workload_menu::WorkloadMenu,
1820
},
19-
cscs::cli::cscs_login,
21+
cscs::{cli::cscs_login, handlers::cscs_system_set},
2022
trace_dbg,
2123
util::ui::draw_area_in_absolute,
2224
};
@@ -37,6 +39,9 @@ where
3739

3840
///Used to allow sending errors from tokio::spawn async jobs
3941
pub error_tx: mpsc::Sender<String>,
42+
43+
/// Triggers async request to select current system
44+
pub select_system_tx: mpsc::Sender<()>,
4045
}
4146

4247
impl<T> Model<T>
@@ -47,13 +52,15 @@ where
4752
app: Application<Id, Msg, UserEvent>,
4853
bridge: TerminalBridge<T>,
4954
error_tx: mpsc::Sender<String>,
55+
select_system_tx: mpsc::Sender<()>,
5056
) -> Self {
5157
Self {
5258
app,
5359
quit: false,
5460
redraw: true,
5561
terminal: bridge,
5662
error_tx,
63+
select_system_tx,
5764
}
5865
}
5966

@@ -91,6 +98,10 @@ where
9198
let popup = draw_area_in_absolute(f.area(), 10);
9299
f.render_widget(Clear, popup);
93100
self.app.view(&Id::LoginPopup, f, popup);
101+
} else if self.app.mounted(&Id::SystemSelectPopup) {
102+
let popup = draw_area_in_absolute(f.area(), 10);
103+
f.render_widget(Clear, popup);
104+
self.app.view(&Id::SystemSelectPopup, f, popup);
94105
}
95106
})
96107
.is_ok()
@@ -117,6 +128,32 @@ where
117128
}
118129
}
119130
}
131+
fn handle_system_select_popup_msg(&mut self, msg: SystemSelectMsg) -> Option<Msg> {
132+
match msg {
133+
SystemSelectMsg::Opened(systems) => {
134+
assert!(
135+
self.app
136+
.mount(
137+
Id::SystemSelectPopup,
138+
Box::new(SystemSelectPopup::new(systems)),
139+
vec![]
140+
)
141+
.is_ok()
142+
);
143+
assert!(self.app.active(&Id::SystemSelectPopup).is_ok());
144+
trace_dbg!("mounted system select popup");
145+
None
146+
}
147+
SystemSelectMsg::Closed => {
148+
assert!(self.app.umount(&Id::SystemSelectPopup).is_ok());
149+
None
150+
}
151+
SystemSelectMsg::SystemSelected(system) => {
152+
assert!(self.app.umount(&Id::SystemSelectPopup).is_ok());
153+
Some(Msg::Cscs(CscsMsg::SystemSelected(system)))
154+
}
155+
}
156+
}
120157
fn handle_error_popup_msg(&mut self, msg: ErrorPopupMsg) -> Option<Msg> {
121158
match msg {
122159
ErrorPopupMsg::Opened(error_msg) => {
@@ -174,6 +211,10 @@ where
174211
assert!(self.app.umount(&Id::Menu).is_ok());
175212
Some(Msg::LoginPopup(LoginPopupMsg::Opened))
176213
}
214+
MenuMsg::CscsSwitchSystem => {
215+
assert!(self.app.umount(&Id::Menu).is_ok());
216+
Some(Msg::Cscs(CscsMsg::SelectSystem))
217+
}
177218
}
178219
}
179220
}
@@ -218,8 +259,32 @@ where
218259
});
219260
None
220261
}
221-
Msg::None => None,
262+
Msg::Cscs(CscsMsg::SelectSystem) => {
263+
let system_select_tx = self.select_system_tx.clone();
264+
tokio::spawn(async move {
265+
system_select_tx.send(()).await.unwrap();
266+
});
267+
None
268+
}
269+
Msg::Cscs(CscsMsg::SystemSelected(system)) => {
270+
let error_tx = self.error_tx.clone();
271+
tokio::spawn(async move {
272+
match cscs_system_set(system, true).await {
273+
Ok(_) => {}
274+
Err(e) => error_tx
275+
.send(format!(
276+
"{:?}",
277+
Err::<(), Report>(e).wrap_err("failed to set current system")
278+
))
279+
.await
280+
.unwrap(),
281+
};
282+
});
283+
None
284+
}
222285
Msg::LoginPopup(msg) => self.handle_login_popup_msg(msg),
286+
Msg::SystemSelectPopup(msg) => self.handle_system_select_popup_msg(msg),
287+
Msg::None => None,
223288
}
224289
} else {
225290
None

coman/src/app/user_events.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use crate::cscs::api_client::Job;
1+
use crate::cscs::api_client::{Job, System};
22

33
#[derive(Debug, Eq, Clone, PartialEq, PartialOrd, Ord)]
44
pub enum CscsEvent {
55
LoggedIn,
66
GotWorkloadData(Vec<Job>),
7+
SelectSystemList(Vec<System>),
78
}
89

910
#[derive(Debug, Eq, Clone, PartialOrd, Ord)]

coman/src/components/global_listener.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use tuirealm::{
55
};
66

77
use crate::app::{
8-
messages::{MenuMsg, Msg},
8+
messages::{MenuMsg, Msg, SystemSelectMsg},
99
user_events::{CscsEvent, UserEvent},
1010
};
1111

@@ -34,6 +34,9 @@ impl Component<Msg, UserEvent> for GlobalListener {
3434
Event::User(UserEvent::Cscs(CscsEvent::LoggedIn)) => {
3535
Some(Msg::Info("Successfully logged in".to_string()))
3636
}
37+
Event::User(UserEvent::Cscs(CscsEvent::SelectSystemList(systems))) => {
38+
Some(Msg::SystemSelectPopup(SystemSelectMsg::Opened(systems)))
39+
}
3740
_ => None,
3841
}
3942
}

coman/src/components/login_popup.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl LoginPopup {
5252
Layout::default()
5353
.constraints(&[Constraint::Length(3), Constraint::Length(3)])
5454
.direction(LayoutDirection::Vertical)
55-
.margin(2),
55+
.margin(1),
5656
)
5757
}
5858

@@ -179,7 +179,7 @@ impl Component<Msg, UserEvent> for LoginPopup {
179179
}) => self.perform(Cmd::Delete),
180180
Event::Keyboard(KeyEvent {
181181
code: Key::Char(ch),
182-
modifiers: KeyModifiers::NONE,
182+
..
183183
}) => self.perform(Cmd::Type(ch)),
184184
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
185185
self.focus_next();

coman/src/components/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub(crate) mod error_popup;
22
pub(crate) mod global_listener;
33
pub(crate) mod info_popup;
44
pub(crate) mod login_popup;
5+
pub(crate) mod system_select_popup;
56
pub(crate) mod toolbar;
67
pub(crate) mod workload_list;
78
pub(crate) mod workload_menu;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use tui_realm_stdlib::List;
2+
use tuirealm::{
3+
Component, Event, Frame, MockComponent, State, StateValue,
4+
command::{Cmd, CmdResult, Direction, Position},
5+
event::{Key, KeyEvent, KeyModifiers},
6+
props::{
7+
Alignment, AttrValue, Attribute, BorderType, Borders, Color, InputType, Layout, Props,
8+
Style, TableBuilder, TextSpan,
9+
},
10+
ratatui::{
11+
layout::{Constraint, Direction as LayoutDirection, Rect},
12+
widgets::Block,
13+
},
14+
};
15+
16+
use crate::{
17+
app::{
18+
messages::{Msg, SystemSelectMsg},
19+
user_events::UserEvent,
20+
},
21+
cscs::api_client::System,
22+
};
23+
24+
#[derive(MockComponent)]
25+
pub struct SystemSelectPopup {
26+
component: List,
27+
systems: Vec<System>,
28+
}
29+
30+
impl SystemSelectPopup {
31+
pub fn new(systems: Vec<System>) -> Self {
32+
let mut rows = TableBuilder::default();
33+
for system in systems.clone() {
34+
rows.add_col(TextSpan::from(system.name).fg(Color::Cyan))
35+
.add_row();
36+
}
37+
Self {
38+
component: List::default()
39+
.borders(
40+
Borders::default()
41+
.modifiers(BorderType::Thick)
42+
.color(Color::Green),
43+
)
44+
.title("Select System", Alignment::Left)
45+
.scroll(true)
46+
.highlighted_color(Color::LightYellow)
47+
.highlighted_str("-")
48+
.rewind(true)
49+
.step(4)
50+
.rows(rows.build()),
51+
systems,
52+
}
53+
}
54+
}
55+
56+
impl Component<Msg, UserEvent> for SystemSelectPopup {
57+
fn on(&mut self, ev: Event<UserEvent>) -> Option<Msg> {
58+
let _ = match ev {
59+
Event::Keyboard(KeyEvent {
60+
code: Key::Down, ..
61+
}) => self.perform(Cmd::Move(Direction::Down)),
62+
Event::Keyboard(KeyEvent {
63+
code: Key::PageDown,
64+
..
65+
}) => self.perform(Cmd::Move(Direction::Down)),
66+
Event::Keyboard(KeyEvent { code: Key::Up, .. }) => {
67+
self.perform(Cmd::Move(Direction::Up))
68+
}
69+
Event::Keyboard(KeyEvent {
70+
code: Key::PageUp, ..
71+
}) => self.perform(Cmd::Move(Direction::Up)),
72+
Event::Keyboard(KeyEvent {
73+
code: Key::Home, ..
74+
}) => self.perform(Cmd::GoTo(Position::Begin)),
75+
Event::Keyboard(KeyEvent { code: Key::End, .. }) => {
76+
self.perform(Cmd::GoTo(Position::End))
77+
}
78+
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
79+
return Some(Msg::SystemSelectPopup(SystemSelectMsg::Closed));
80+
}
81+
Event::Keyboard(KeyEvent {
82+
code: Key::Char('x'),
83+
..
84+
}) => {
85+
return Some(Msg::SystemSelectPopup(SystemSelectMsg::Closed));
86+
}
87+
Event::Keyboard(KeyEvent {
88+
code: Key::Enter, ..
89+
}) => {
90+
let msg = if let State::One(StateValue::Usize(index)) = self.state() {
91+
let selected_system = self.systems[index].clone();
92+
Some(Msg::SystemSelectPopup(SystemSelectMsg::SystemSelected(
93+
selected_system.name,
94+
)))
95+
} else {
96+
Some(Msg::SystemSelectPopup(SystemSelectMsg::Closed))
97+
};
98+
return msg;
99+
}
100+
101+
_ => CmdResult::None,
102+
};
103+
Some(Msg::None)
104+
}
105+
}

coman/src/components/workload_list.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,23 @@ impl Component<Msg, UserEvent> for WorkloadList {
5858
self.perform(Cmd::GoTo(Position::End))
5959
}
6060
Event::User(UserEvent::Cscs(CscsEvent::GotWorkloadData(data))) => {
61-
let mut table = TableBuilder::default();
62-
for (idx, job) in data.iter().enumerate() {
63-
if idx > 0 {
64-
table.add_row();
61+
if data.len() == 0 {
62+
self.attr(Attribute::Content, AttrValue::Table(vec![]));
63+
} else {
64+
let mut table = TableBuilder::default();
65+
for (idx, job) in data.iter().enumerate() {
66+
if idx > 0 {
67+
table.add_row();
68+
}
69+
table
70+
.add_col(TextSpan::from(job.name.clone()).bold())
71+
.add_col(TextSpan::from(" "))
72+
.add_col(TextSpan::from(job.status.to_string()))
73+
.add_col(TextSpan::from(" "))
74+
.add_col(TextSpan::from(job.id.to_string()));
6575
}
66-
table
67-
.add_col(TextSpan::from(job.name.clone()).bold())
68-
.add_col(TextSpan::from(" "))
69-
.add_col(TextSpan::from(job.status.to_string()))
70-
.add_col(TextSpan::from(" "))
71-
.add_col(TextSpan::from(job.id.to_string()));
76+
self.attr(Attribute::Content, AttrValue::Table(table.build()));
7277
}
73-
self.attr(Attribute::Content, AttrValue::Table(table.build()));
7478
self.perform(Cmd::Change)
7579
}
7680
_ => CmdResult::None,

coman/src/components/workload_menu.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ impl Default for WorkloadMenu {
3535
TableBuilder::default()
3636
.add_col(TextSpan::from("Login to CSCS").fg(Color::Cyan))
3737
.add_row()
38+
.add_col(TextSpan::from("Switch System").fg(Color::Cyan))
39+
.add_row()
3840
.add_col(TextSpan::from("Quit").fg(Color::Cyan))
3941
.add_row()
4042
.build(),
@@ -81,6 +83,8 @@ impl Component<Msg, UserEvent> for WorkloadMenu {
8183
let msg = if let State::One(StateValue::Usize(index)) = self.state() {
8284
match index {
8385
0 => Some(Msg::Menu(MenuMsg::CscsLogin)),
86+
1 => Some(Msg::Menu(MenuMsg::CscsSwitchSystem)),
87+
2 => Some(Msg::AppClose),
8488
_ => Some(Msg::Menu(MenuMsg::Closed)),
8589
}
8690
} else {

0 commit comments

Comments
 (0)