Render basic body contents
This commit is contained in:
parent
35bfc8d285
commit
32959cf691
6 changed files with 170 additions and 6 deletions
|
|
@ -122,6 +122,10 @@ impl Connected {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn status(&self) -> &Status {
|
||||
&self.status
|
||||
}
|
||||
|
||||
pub fn present(&self) -> Option<&Present> {
|
||||
self.status.present()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::oneshot::{self, Sender};
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
|
|
@ -15,7 +14,7 @@ struct ConnConfig {
|
|||
url: String,
|
||||
room: String,
|
||||
timeout: Duration,
|
||||
ev_tx: UnboundedSender<conn::Event>,
|
||||
ev_tx: UnboundedSender<Event>,
|
||||
}
|
||||
|
||||
impl ConnConfig {
|
||||
|
|
@ -68,11 +67,19 @@ impl CoveRoom {
|
|||
dead_mans_switch: tx,
|
||||
};
|
||||
|
||||
// Spawned separately because otherwise, the last few elements before a
|
||||
// connection is closed might not get shoveled.
|
||||
tokio::spawn(Self::shovel_events(
|
||||
name,
|
||||
ev_rx,
|
||||
event_sender,
|
||||
convert_event,
|
||||
));
|
||||
|
||||
let conn_clone = room.conn.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = rx => {} // Watch dead man's switch
|
||||
_ = Self::shovel_events(name, ev_rx, event_sender, convert_event) => {}
|
||||
_ = Self::run(conn_clone, mt, conf) => {}
|
||||
}
|
||||
});
|
||||
|
|
@ -91,7 +98,7 @@ impl CoveRoom {
|
|||
|
||||
async fn shovel_events<E>(
|
||||
name: String,
|
||||
mut ev_rx: UnboundedReceiver<conn::Event>,
|
||||
mut ev_rx: UnboundedReceiver<Event>,
|
||||
ev_tx: UnboundedSender<E>,
|
||||
convert_event: impl Fn(&str, Event) -> E,
|
||||
) {
|
||||
|
|
@ -115,6 +122,7 @@ impl CoveRoom {
|
|||
// TODO Exponential backoff?
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
}
|
||||
// TODO Note these errors somewhere in the room state
|
||||
Err(conn::Error::CouldNotConnect(_)) => return,
|
||||
Err(conn::Error::InvalidRoom(_)) => return,
|
||||
Err(conn::Error::InvalidIdentity(_)) => return,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
// TODO Make as few things async as necessary
|
||||
|
||||
#![warn(clippy::use_self)]
|
||||
|
||||
pub mod client;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
mod body;
|
||||
mod users;
|
||||
|
||||
use crossterm::event::KeyEvent;
|
||||
|
|
@ -9,6 +10,7 @@ use tui::Frame;
|
|||
|
||||
use crate::client::cove::room::CoveRoom;
|
||||
|
||||
use self::body::Body;
|
||||
use self::users::CoveUsers;
|
||||
|
||||
use super::input::EventHandler;
|
||||
|
|
@ -16,11 +18,15 @@ use super::styles;
|
|||
|
||||
pub struct CoveUi {
|
||||
room: CoveRoom,
|
||||
body: Body,
|
||||
}
|
||||
|
||||
impl CoveUi {
|
||||
pub fn new(room: CoveRoom) -> Self {
|
||||
Self { room }
|
||||
Self {
|
||||
room,
|
||||
body: Body::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
|
|
@ -63,7 +69,8 @@ impl CoveUi {
|
|||
}
|
||||
|
||||
async fn render_body<B: Backend>(&mut self, frame: &mut Frame<'_, B>, area: Rect) {
|
||||
// TODO Implement
|
||||
self.body.update(&self.room).await;
|
||||
self.body.render(frame, area).await
|
||||
}
|
||||
|
||||
pub async fn render_users<B: Backend>(&mut self, frame: &mut Frame<'_, B>, area: Rect) {
|
||||
|
|
|
|||
133
cove-tui/src/ui/cove/body.rs
Normal file
133
cove-tui/src/ui/cove/body.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
use tui::backend::Backend;
|
||||
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||
use tui::text::Span;
|
||||
use tui::widgets::Paragraph;
|
||||
use tui::Frame;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::client::cove::conn::{State, Status};
|
||||
use crate::client::cove::room::CoveRoom;
|
||||
use crate::ui::textline::{TextLine, TextLineState};
|
||||
use crate::ui::{layout, styles};
|
||||
|
||||
pub enum Body {
|
||||
Empty,
|
||||
Connecting,
|
||||
ChoosingRoom,
|
||||
Identifying,
|
||||
ChooseNick {
|
||||
nick: TextLineState,
|
||||
prev_error: Option<String>,
|
||||
},
|
||||
Present,
|
||||
Stopped, // TODO Display reason for stoppage
|
||||
}
|
||||
|
||||
impl Default for Body {
|
||||
fn default() -> Self {
|
||||
Self::Empty
|
||||
}
|
||||
}
|
||||
|
||||
impl Body {
|
||||
pub async fn update(&mut self, room: &CoveRoom) {
|
||||
match &*room.conn().await.state().await {
|
||||
State::Connecting => *self = Self::Connecting,
|
||||
State::Connected(conn) => match conn.status() {
|
||||
Status::ChoosingRoom => *self = Self::ChoosingRoom,
|
||||
Status::Identifying => *self = Self::Identifying,
|
||||
Status::IdRequired(error) => self.choose_nick(error.clone()),
|
||||
Status::Present(_) => *self = Self::Present,
|
||||
},
|
||||
State::Stopped => *self = Self::Stopped,
|
||||
}
|
||||
}
|
||||
|
||||
fn choose_nick(&mut self, error: Option<String>) {
|
||||
match self {
|
||||
Self::ChooseNick { prev_error, .. } => *prev_error = error,
|
||||
_ => {
|
||||
*self = Self::ChooseNick {
|
||||
nick: TextLineState::default(),
|
||||
prev_error: error,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn render<B: Backend>(&mut self, frame: &mut Frame<'_, B>, area: Rect) {
|
||||
match self {
|
||||
Body::Empty => todo!(),
|
||||
Body::Connecting => {
|
||||
let text = "Connecting...";
|
||||
let area = layout::centered(text.width() as u16, 1, area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(text, styles::title())), area);
|
||||
}
|
||||
Body::ChoosingRoom => {
|
||||
let text = "Entering room...";
|
||||
let area = layout::centered(text.width() as u16, 1, area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(text, styles::title())), area);
|
||||
}
|
||||
Body::Identifying => {
|
||||
let text = "Identifying...";
|
||||
let area = layout::centered(text.width() as u16, 1, area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(text, styles::title())), area);
|
||||
}
|
||||
Body::ChooseNick {
|
||||
nick,
|
||||
prev_error: None,
|
||||
} => {
|
||||
let area = layout::centered_v(2, area);
|
||||
let areas = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(1), Constraint::Length(1)])
|
||||
.split(area);
|
||||
let title_area = areas[0];
|
||||
let text_area = areas[1];
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled("Choose a nick:", styles::title())),
|
||||
title_area,
|
||||
);
|
||||
frame.render_stateful_widget(TextLine, text_area, nick);
|
||||
}
|
||||
Body::ChooseNick {
|
||||
nick,
|
||||
prev_error: Some(error),
|
||||
} => {
|
||||
let area = layout::centered_v(3, area);
|
||||
let areas = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.split(area);
|
||||
let title_area = areas[0];
|
||||
let text_area = areas[1];
|
||||
let error_area = areas[2];
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled("Choose a nick:", styles::title())),
|
||||
title_area,
|
||||
);
|
||||
frame.render_stateful_widget(TextLine, text_area, nick);
|
||||
frame.render_widget(
|
||||
Paragraph::new(Span::styled(error as &str, styles::error())),
|
||||
error_area,
|
||||
);
|
||||
}
|
||||
Body::Present => {
|
||||
let text = "Present";
|
||||
let area = layout::centered(text.width() as u16, 1, area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(text, styles::title())), area);
|
||||
}
|
||||
Body::Stopped => {
|
||||
let text = "Stopped";
|
||||
let area = layout::centered(text.width() as u16, 1, area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(text, styles::title())), area);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,3 +12,13 @@ pub fn centered(width: u16, height: u16, area: Rect) -> Rect {
|
|||
height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn centered_v(height: u16, area: Rect) -> Rect {
|
||||
let height = height.min(area.height);
|
||||
let dy = (area.height - height) / 2;
|
||||
Rect {
|
||||
y: area.y + dy,
|
||||
height,
|
||||
..area
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue