Migrate room to AsyncWidget

This commit is contained in:
Joscha 2023-04-12 20:17:38 +02:00
parent ead4fa7c8a
commit d8d3e64776
2 changed files with 90 additions and 70 deletions

View file

@ -8,22 +8,18 @@ use euphoxide::conn::{self, Joined, Joining, SessionInfo};
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::oneshot::error::TryRecvError; use tokio::sync::oneshot::error::TryRecvError;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use toss::{Style, Styled, Terminal}; use toss::widgets::{BoxedAsync, Join2, Layer, Text};
use toss::{AsyncWidget, Style, Styled, Terminal, WidgetExt};
use crate::config; use crate::config;
use crate::euph; use crate::euph;
use crate::macros::logging_unwrap; use crate::macros::logging_unwrap;
use crate::ui::chat::{ChatState, Reaction}; use crate::ui::chat::{ChatState, Reaction};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
use crate::ui::widgets::border::Border; use crate::ui::widgets::editor::EditorState as OldEditorState;
use crate::ui::widgets::editor::EditorState; use crate::ui::widgets::list::ListState as OldListState;
use crate::ui::widgets::join::{HJoin, Segment, VJoin}; use crate::ui::widgets::WidgetWrapper;
use crate::ui::widgets::layer::Layer; use crate::ui::{util, UiError, UiEvent};
use crate::ui::widgets::list::ListState;
use crate::ui::widgets::padding::Padding;
use crate::ui::widgets::text::Text;
use crate::ui::widgets::BoxedWidget;
use crate::ui::{util, UiEvent};
use crate::vault::EuphRoomVault; use crate::vault::EuphRoomVault;
use super::account::{self, AccountUiState}; use super::account::{self, AccountUiState};
@ -31,7 +27,7 @@ use super::links::{self, LinksState};
use super::popup::RoomPopup; use super::popup::RoomPopup;
use super::{auth, inspect, nick, nick_list}; use super::{auth, inspect, nick, nick_list};
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Focus { enum Focus {
Chat, Chat,
NickList, NickList,
@ -40,14 +36,16 @@ enum Focus {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum State { enum State {
Normal, Normal,
Auth(EditorState), Auth(OldEditorState),
Nick(EditorState), Nick(OldEditorState),
Account(AccountUiState), Account(AccountUiState),
Links(LinksState), Links(LinksState),
InspectMessage(Message), InspectMessage(Message),
InspectSession(SessionInfo), InspectSession(SessionInfo),
} }
type EuphChatState = ChatState<euph::SmallMessage, EuphRoomVault>;
pub struct EuphRoom { pub struct EuphRoom {
server_config: ServerConfig, server_config: ServerConfig,
config: config::EuphRoom, config: config::EuphRoom,
@ -59,10 +57,10 @@ pub struct EuphRoom {
state: State, state: State,
popups: VecDeque<RoomPopup>, popups: VecDeque<RoomPopup>,
chat: ChatState<euph::SmallMessage, EuphRoomVault>, chat: EuphChatState,
last_msg_sent: Option<oneshot::Receiver<MessageId>>, last_msg_sent: Option<oneshot::Receiver<MessageId>>,
nick_list: ListState<SessionId>, nick_list: OldListState<SessionId>,
} }
impl EuphRoom { impl EuphRoom {
@ -82,7 +80,7 @@ impl EuphRoom {
popups: VecDeque::new(), popups: VecDeque::new(),
chat: ChatState::new(vault), chat: ChatState::new(vault),
last_msg_sent: None, last_msg_sent: None,
nick_list: ListState::new(), nick_list: OldListState::new(),
} }
} }
@ -205,77 +203,97 @@ impl EuphRoom {
self.stabilize_state(); self.stabilize_state();
} }
pub async fn widget(&mut self) -> BoxedWidget { pub async fn widget(&mut self) -> BoxedAsync<'_, UiError> {
self.stabilize().await; self.stabilize().await;
let room_state = self.room_state(); let room_state = self.room.as_ref().map(|room| room.state());
let status_widget = self.status_widget(room_state).await;
let chat = if let Some(euph::State::Connected(_, conn::State::Joined(joined))) = room_state let chat = if let Some(euph::State::Connected(_, conn::State::Joined(joined))) = room_state
{ {
self.widget_with_nick_list(room_state, joined).await Self::widget_with_nick_list(
&mut self.chat,
status_widget,
&mut self.nick_list,
joined,
self.focus,
)
} else { } else {
self.widget_without_nick_list(room_state).await Self::widget_without_nick_list(&mut self.chat, status_widget)
}; };
let mut layers = vec![chat]; let mut layers = vec![chat];
match &self.state { match &self.state {
State::Normal => {} State::Normal => {}
State::Auth(editor) => layers.push(auth::widget(editor)), State::Auth(editor) => {
State::Nick(editor) => layers.push(nick::widget(editor)), layers.push(WidgetWrapper::new(auth::widget(editor)).boxed_async())
State::Account(account) => layers.push(account.widget()), }
State::Links(links) => layers.push(links.widget()), State::Nick(editor) => {
State::InspectMessage(message) => layers.push(inspect::message_widget(message)), layers.push(WidgetWrapper::new(nick::widget(editor)).boxed_async())
State::InspectSession(session) => layers.push(inspect::session_widget(session)), }
State::Account(account) => {
layers.push(WidgetWrapper::new(account.widget()).boxed_async())
}
State::Links(links) => layers.push(WidgetWrapper::new(links.widget()).boxed_async()),
State::InspectMessage(message) => {
layers.push(WidgetWrapper::new(inspect::message_widget(message)).boxed_async())
}
State::InspectSession(session) => {
layers.push(WidgetWrapper::new(inspect::session_widget(session)).boxed_async())
}
} }
for popup in &self.popups { for popup in &self.popups {
layers.push(popup.widget()); layers.push(WidgetWrapper::new(popup.widget()).boxed_async());
} }
Layer::new(layers).into() Layer::new(layers).boxed_async()
} }
async fn widget_without_nick_list(&self, state: Option<&euph::State>) -> BoxedWidget { fn widget_without_nick_list(
VJoin::new(vec![ chat: &mut EuphChatState,
Segment::new(Border::new( status_widget: impl AsyncWidget<UiError> + Send + Sync + 'static,
Padding::new(self.status_widget(state).await).horizontal(1), ) -> BoxedAsync<'_, UiError> {
)), let chat_widget = WidgetWrapper::new(chat.widget(String::new(), true));
// TODO Use last known nick?
Segment::new(self.chat.widget(String::new(), true)).expanding(true), Join2::vertical(
]) status_widget.segment().with_fixed(true),
.into() chat_widget.segment(),
)
.boxed_async()
} }
async fn widget_with_nick_list( fn widget_with_nick_list<'a>(
&self, chat: &'a mut EuphChatState,
state: Option<&euph::State>, status_widget: impl AsyncWidget<UiError> + Send + Sync + 'static,
nick_list: &mut OldListState<SessionId>,
joined: &Joined, joined: &Joined,
) -> BoxedWidget { focus: Focus,
HJoin::new(vec![ ) -> BoxedAsync<'a, UiError> {
Segment::new(VJoin::new(vec![ let nick_list_widget = WidgetWrapper::new(nick_list::widget(
Segment::new(Border::new( nick_list,
Padding::new(self.status_widget(state).await).horizontal(1), joined,
)), focus == Focus::NickList,
Segment::new( ))
self.chat .padding()
.widget(joined.session.name.clone(), self.focus == Focus::Chat), .with_right(1)
) .border();
.expanding(true),
])) let chat_widget =
.expanding(true), WidgetWrapper::new(chat.widget(joined.session.name.clone(), focus == Focus::Chat));
Segment::new(Border::new(
Padding::new(nick_list::widget( Join2::horizontal(
&self.nick_list, Join2::vertical(
joined, status_widget.segment().with_fixed(true),
self.focus == Focus::NickList, chat_widget.segment(),
)) )
.right(1), .segment(),
)), nick_list_widget.segment().with_fixed(true),
]) )
.into() .boxed_async()
} }
async fn status_widget(&self, state: Option<&euph::State>) -> BoxedWidget { async fn status_widget(&self, state: Option<&euph::State>) -> BoxedAsync<'static, UiError> {
let room_style = Style::new().bold().blue(); let room_style = Style::new().bold().blue();
let mut info = Styled::new(format!("&{}", self.name()), room_style); let mut info = Styled::new(format!("&{}", self.name()), room_style);
@ -308,7 +326,11 @@ impl EuphRoom {
.then_plain(")"); .then_plain(")");
} }
Text::new(info).into() Text::new(info)
.padding()
.with_horizontal(1)
.border()
.boxed_async()
} }
async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList) { async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList) {

View file

@ -18,7 +18,6 @@ use crate::vault::Vault;
use super::euph::room::EuphRoom; use super::euph::room::EuphRoom;
use super::input::{key, InputEvent, KeyBindingsList}; use super::input::{key, InputEvent, KeyBindingsList};
use super::widgets::WidgetWrapper;
use super::widgets2::{List, ListState, Popup}; use super::widgets2::{List, ListState, Popup};
use super::{util2, UiError, UiEvent}; use super::{util2, UiError, UiEvent};
@ -176,14 +175,13 @@ impl Rooms {
Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order).await Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order).await
} }
State::ShowRoom(name) => WidgetWrapper::new( State::ShowRoom(name) => {
self.euph_rooms self.euph_rooms
.get_mut(name) .get_mut(name)
.expect("room exists after stabilization") .expect("room exists after stabilization")
.widget() .widget()
.await, .await
) }
.boxed_async(),
State::Connect(editor) => { State::Connect(editor) => {
Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order) Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order)