Support choosing domain in room connection popup

This commit is contained in:
Joscha 2024-01-02 13:51:51 +01:00
parent f4967731a1
commit 970bc07ed9
3 changed files with 142 additions and 38 deletions

View file

@ -1,3 +1,5 @@
mod connect;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::iter;
@ -17,6 +19,8 @@ use crate::euph;
use crate::macros::logging_unwrap;
use crate::vault::{EuphVault, RoomIdentifier, Vault};
use self::connect::{ConnectResult, ConnectState};
use super::euph::room::EuphRoom;
use super::widgets::{ListBuilder, ListState, Popup};
use super::{key_bindings, util, UiError, UiEvent};
@ -24,7 +28,7 @@ use super::{key_bindings, util, UiError, UiEvent};
enum State {
ShowList,
ShowRoom(RoomIdentifier),
Connect(EditorState),
Connect(ConnectState),
Delete(RoomIdentifier, EditorState),
}
@ -242,10 +246,10 @@ impl Rooms {
.await
}
State::Connect(editor) => {
State::Connect(connect) => {
Self::rooms_widget(self.config, &mut self.list, self.order, &self.euph_rooms)
.await
.below(Self::new_room_widget(editor))
.below(connect.widget())
.desync()
.boxed_async()
}
@ -261,20 +265,6 @@ impl Rooms {
}
}
fn new_room_widget(editor: &mut EditorState) -> impl Widget<UiError> + '_ {
let room_style = Style::new().bold().blue();
let inner = Join2::horizontal(
Text::new(("&", room_style)).segment().with_fixed(true),
editor
.widget()
.with_highlight(|s| Styled::new(s, room_style))
.segment(),
);
Popup::new(inner, "Connect to")
}
fn delete_room_widget<'a>(
id: &RoomIdentifier,
editor: &'a mut EditorState,
@ -472,10 +462,6 @@ impl Rooms {
)
}
fn room_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_'
}
async fn handle_showlist_input_event(
&mut self,
event: &mut InputEvent<'_>,
@ -534,7 +520,7 @@ impl Rooms {
return true;
}
if event.matches(&keys.rooms.action.new) {
self.state = State::Connect(EditorState::new());
self.state = State::Connect(ConnectState::new());
return true;
}
if event.matches(&keys.rooms.action.delete) {
@ -574,28 +560,21 @@ impl Rooms {
}
}
}
State::Connect(editor) => {
if event.matches(&keys.general.abort) {
State::Connect(connect) => match connect.handle_input_event(event, keys) {
ConnectResult::Close => {
self.state = State::ShowList;
return true;
}
if event.matches(&keys.general.confirm) {
let name = editor.text().to_string();
if !name.is_empty() {
let room = RoomIdentifier {
// TODO Remove hardcoded domain
domain: "euphoria.leet.nu".to_string(),
name,
};
self.connect_to_room(room.clone()).await;
self.state = State::ShowRoom(room);
}
ConnectResult::Connect(room) => {
self.connect_to_room(room.clone()).await;
self.state = State::ShowRoom(room);
return true;
}
if util::handle_editor_input_event(editor, event, keys, Self::room_char) {
ConnectResult::Handled => {
return true;
}
}
ConnectResult::Unhandled => {}
},
State::Delete(id, editor) => {
if event.matches(&keys.general.abort) {
self.state = State::ShowList;
@ -607,7 +586,7 @@ impl Rooms {
self.state = State::ShowList;
return true;
}
if util::handle_editor_input_event(editor, event, keys, Self::room_char) {
if util::handle_editor_input_event(editor, event, keys, util::is_room_char) {
return true;
}
}

View file

@ -0,0 +1,120 @@
use cove_config::Keys;
use cove_input::InputEvent;
use crossterm::style::Stylize;
use toss::widgets::{EditorState, Empty, Join2, Join3, Text};
use toss::{Style, Styled, Widget, WidgetExt};
use crate::ui::widgets::Popup;
use crate::ui::{util, UiError};
use crate::vault::RoomIdentifier;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Focus {
Name,
Domain,
}
impl Focus {
fn advance(self) -> Self {
match self {
Self::Name => Self::Domain,
Self::Domain => Self::Name,
}
}
}
pub struct ConnectState {
focus: Focus,
name: EditorState,
domain: EditorState,
}
pub enum ConnectResult {
Close,
Connect(RoomIdentifier),
Handled,
Unhandled,
}
impl ConnectState {
pub fn new() -> Self {
Self {
focus: Focus::Name,
name: EditorState::new(),
domain: EditorState::with_initial_text("euphoria.leet.nu".to_string()),
}
}
pub fn handle_input_event(&mut self, event: &mut InputEvent<'_>, keys: &Keys) -> ConnectResult {
if event.matches(&keys.general.abort) {
return ConnectResult::Close;
}
if event.matches(&keys.general.focus) {
self.focus = self.focus.advance();
return ConnectResult::Handled;
}
if event.matches(&keys.general.confirm) {
let id = RoomIdentifier {
domain: self.domain.text().to_string(),
name: self.name.text().to_string(),
};
if !id.domain.is_empty() && !id.name.is_empty() {
return ConnectResult::Connect(id);
}
}
let handled = match self.focus {
Focus::Name => {
util::handle_editor_input_event(&mut self.name, event, keys, util::is_room_char)
}
Focus::Domain => {
util::handle_editor_input_event(&mut self.domain, event, keys, util::is_room_char)
}
};
if handled {
return ConnectResult::Handled;
}
ConnectResult::Unhandled
}
pub fn widget(&mut self) -> impl Widget<UiError> + '_ {
let room_style = Style::new().bold().blue();
let domain_style = Style::new().grey();
let name = Join2::horizontal(
Text::new(Styled::new_plain("Room: ").then("&", room_style))
.with_wrap(false)
.segment()
.with_fixed(true),
self.name
.widget()
.with_highlight(|s| Styled::new(s, room_style))
.with_focus(self.focus == Focus::Name)
.segment(),
);
let domain = Join3::horizontal(
Text::new("Domain:")
.with_wrap(false)
.segment()
.with_fixed(true),
Empty::new().with_width(1).segment().with_fixed(true),
self.domain
.widget()
.with_highlight(|s| Styled::new(s, domain_style))
.with_focus(self.focus == Focus::Domain)
.segment(),
);
let inner = Join2::vertical(
name.segment().with_fixed(true),
domain.segment().with_fixed(true),
);
Popup::new(inner, "Connect to")
}
}

View file

@ -5,6 +5,11 @@ use toss::widgets::EditorState;
use super::widgets::ListState;
/// Test if a character is allowed to be typed in a room name.
pub fn is_room_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_'
}
//////////
// List //
//////////