Add message inspection popup
This commit is contained in:
parent
bbf6371f87
commit
2d88513a28
4 changed files with 92 additions and 47 deletions
|
|
@ -15,8 +15,10 @@ Procedure when bumping the version number:
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Key bindings to navigate nick list
|
||||||
- Room deletion confirmation popup
|
- Room deletion confirmation popup
|
||||||
- Message inspection popup
|
- Message inspection popup
|
||||||
|
- Session inspection popup
|
||||||
- Error popup when external editor fails
|
- Error popup when external editor fails
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crossterm::style::{ContentStyle, Stylize};
|
use crossterm::style::{ContentStyle, Stylize};
|
||||||
use euphoxide::api::Message;
|
use euphoxide::api::{Message, SessionView};
|
||||||
use toss::styled::Styled;
|
use toss::styled::Styled;
|
||||||
|
|
||||||
use crate::ui::input::{key, InputEvent, KeyBindingsList};
|
use crate::ui::input::{key, InputEvent, KeyBindingsList};
|
||||||
|
|
@ -38,10 +38,32 @@ macro_rules! line {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_widget(msg: &Message) -> BoxedWidget {
|
fn session_lines(mut text: Styled, session: &SessionView) -> Styled {
|
||||||
let heading_style = ContentStyle::default().bold();
|
line!(text, "id", session.id);
|
||||||
|
line!(text, "name", session.name);
|
||||||
|
line!(text, "name (raw)", session.name, debug);
|
||||||
|
line!(text, "server_id", session.server_id);
|
||||||
|
line!(text, "server_era", session.server_era);
|
||||||
|
line!(text, "session_id", session.session_id);
|
||||||
|
line!(text, "is_staff", session.is_staff, yes or no);
|
||||||
|
line!(text, "is_manager", session.is_manager, yes or no);
|
||||||
|
line!(
|
||||||
|
text,
|
||||||
|
"client_address",
|
||||||
|
session.client_address.as_ref(),
|
||||||
|
optional
|
||||||
|
);
|
||||||
|
line!(
|
||||||
|
text,
|
||||||
|
"real_client_address",
|
||||||
|
session.real_client_address.as_ref(),
|
||||||
|
optional
|
||||||
|
);
|
||||||
|
|
||||||
let mut text = Styled::new("Message", heading_style).then_plain("\n");
|
text
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message_lines(mut text: Styled, msg: &Message) -> Styled {
|
||||||
line!(text, "id", msg.id);
|
line!(text, "id", msg.id);
|
||||||
line!(text, "parent", msg.parent, optional);
|
line!(text, "parent", msg.parent, optional);
|
||||||
line!(text, "previous_edit_id", msg.previous_edit_id, optional);
|
line!(text, "previous_edit_id", msg.previous_edit_id, optional);
|
||||||
|
|
@ -50,29 +72,29 @@ pub fn message_widget(msg: &Message) -> BoxedWidget {
|
||||||
line!(text, "edited", msg.edited.map(|t| t.0), optional);
|
line!(text, "edited", msg.edited.map(|t| t.0), optional);
|
||||||
line!(text, "deleted", msg.deleted.map(|t| t.0), optional);
|
line!(text, "deleted", msg.deleted.map(|t| t.0), optional);
|
||||||
line!(text, "truncated", msg.truncated, yes or no);
|
line!(text, "truncated", msg.truncated, yes or no);
|
||||||
text = text.then_plain("\n");
|
|
||||||
|
|
||||||
text = text.then("Sender", heading_style).then_plain("\n");
|
text
|
||||||
line!(text, "id", msg.sender.id);
|
}
|
||||||
line!(text, "name", msg.sender.name);
|
|
||||||
line!(text, "name (raw)", msg.sender.name, debug);
|
pub fn session_widget(session: &SessionView) -> BoxedWidget {
|
||||||
line!(text, "server_id", msg.sender.server_id);
|
let text = session_lines(Styled::default(), session);
|
||||||
line!(text, "server_era", msg.sender.server_era);
|
|
||||||
line!(text, "session_id", msg.sender.session_id);
|
Popup::new(Text::new(text)).title("Inspect session").build()
|
||||||
line!(text, "is_staff", msg.sender.is_staff, yes or no);
|
}
|
||||||
line!(text, "is_manager", msg.sender.is_manager, yes or no);
|
|
||||||
line!(
|
pub fn message_widget(msg: &Message) -> BoxedWidget {
|
||||||
text,
|
let heading_style = ContentStyle::default().bold();
|
||||||
"client_address",
|
|
||||||
msg.sender.client_address.as_ref(),
|
let mut text = Styled::new("Message", heading_style).then_plain("\n");
|
||||||
optional
|
|
||||||
);
|
text = message_lines(text, msg);
|
||||||
line!(
|
|
||||||
text,
|
text = text
|
||||||
"real_client_address",
|
.then_plain("\n")
|
||||||
msg.sender.real_client_address.as_ref(),
|
.then("Sender", heading_style)
|
||||||
optional
|
.then_plain("\n");
|
||||||
);
|
|
||||||
|
text = session_lines(text, &msg.sender);
|
||||||
|
|
||||||
Popup::new(Text::new(text)).title("Inspect message").build()
|
Popup::new(Text::new(text)).title("Inspect message").build()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use crossterm::style::{Color, ContentStyle, Stylize};
|
use crossterm::style::{Color, ContentStyle, Stylize};
|
||||||
use euphoxide::api::{SessionType, SessionView};
|
use euphoxide::api::{SessionType, SessionView, UserId};
|
||||||
use euphoxide::conn::Joined;
|
use euphoxide::conn::Joined;
|
||||||
use toss::styled::Styled;
|
use toss::styled::Styled;
|
||||||
|
|
||||||
|
|
@ -12,13 +12,13 @@ use crate::ui::widgets::list::{List, ListState};
|
||||||
use crate::ui::widgets::text::Text;
|
use crate::ui::widgets::text::Text;
|
||||||
use crate::ui::widgets::BoxedWidget;
|
use crate::ui::widgets::BoxedWidget;
|
||||||
|
|
||||||
pub fn widget(state: &ListState<String>, joined: &Joined, focused: bool) -> BoxedWidget {
|
pub fn widget(state: &ListState<UserId>, joined: &Joined, focused: bool) -> BoxedWidget {
|
||||||
let mut list = state.widget().focus(focused);
|
let mut list = state.widget().focus(focused);
|
||||||
render_rows(&mut list, joined);
|
render_rows(&mut list, joined);
|
||||||
list.into()
|
list.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_rows(list: &mut List<String>, joined: &Joined) {
|
fn render_rows(list: &mut List<UserId>, joined: &Joined) {
|
||||||
let mut people = vec![];
|
let mut people = vec![];
|
||||||
let mut bots = vec![];
|
let mut bots = vec![];
|
||||||
let mut lurkers = vec![];
|
let mut lurkers = vec![];
|
||||||
|
|
@ -49,7 +49,7 @@ fn render_rows(list: &mut List<String>, joined: &Joined) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_section(
|
fn render_section(
|
||||||
list: &mut List<String>,
|
list: &mut List<UserId>,
|
||||||
name: &str,
|
name: &str,
|
||||||
sessions: &[&SessionView],
|
sessions: &[&SessionView],
|
||||||
own_session: &SessionView,
|
own_session: &SessionView,
|
||||||
|
|
@ -74,9 +74,7 @@ fn render_section(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_row(list: &mut List<String>, session: &SessionView, own_session: &SessionView) {
|
fn render_row(list: &mut List<UserId>, session: &SessionView, own_session: &SessionView) {
|
||||||
let id = session.session_id.clone();
|
|
||||||
|
|
||||||
let (name, style, style_inv, perms_style_inv) = if session.name.is_empty() {
|
let (name, style, style_inv, perms_style_inv) = if session.name.is_empty() {
|
||||||
let name = "lurk";
|
let name = "lurk";
|
||||||
let style = ContentStyle::default().grey();
|
let style = ContentStyle::default().grey();
|
||||||
|
|
@ -113,7 +111,7 @@ fn render_row(list: &mut List<String>, session: &SessionView, own_session: &Sess
|
||||||
.then(name, style_inv)
|
.then(name, style_inv)
|
||||||
.then(perms, perms_style_inv);
|
.then(perms, perms_style_inv);
|
||||||
list.add_sel(
|
list.add_sel(
|
||||||
id,
|
session.id.clone(),
|
||||||
Text::new(normal),
|
Text::new(normal),
|
||||||
Background::new(Text::new(selected)).style(style_inv),
|
Background::new(Text::new(selected)).style(style_inv),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::collections::VecDeque;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crossterm::style::{ContentStyle, Stylize};
|
use crossterm::style::{ContentStyle, Stylize};
|
||||||
use euphoxide::api::{Data, Message, PacketType, Snowflake};
|
use euphoxide::api::{Data, Message, PacketType, SessionView, Snowflake, UserId};
|
||||||
use euphoxide::conn::{Joined, Joining, Status};
|
use euphoxide::conn::{Joined, Joining, Status};
|
||||||
use parking_lot::FairMutex;
|
use parking_lot::FairMutex;
|
||||||
use tokio::sync::oneshot::error::TryRecvError;
|
use tokio::sync::oneshot::error::TryRecvError;
|
||||||
|
|
@ -45,7 +45,7 @@ enum State {
|
||||||
Account(AccountUiState),
|
Account(AccountUiState),
|
||||||
Links(LinksState),
|
Links(LinksState),
|
||||||
InspectMessage(Message),
|
InspectMessage(Message),
|
||||||
// TODO Inspect users
|
InspectSession(SessionView),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
|
@ -80,7 +80,7 @@ pub struct EuphRoom {
|
||||||
chat: ChatState<euph::SmallMessage, EuphRoomVault>,
|
chat: ChatState<euph::SmallMessage, EuphRoomVault>,
|
||||||
last_msg_sent: Option<oneshot::Receiver<Snowflake>>,
|
last_msg_sent: Option<oneshot::Receiver<Snowflake>>,
|
||||||
|
|
||||||
nick_list: ListState<String>,
|
nick_list: ListState<UserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EuphRoom {
|
impl EuphRoom {
|
||||||
|
|
@ -243,6 +243,7 @@ impl EuphRoom {
|
||||||
State::Account(account) => layers.push(account.widget()),
|
State::Account(account) => layers.push(account.widget()),
|
||||||
State::Links(links) => layers.push(links.widget()),
|
State::Links(links) => layers.push(links.widget()),
|
||||||
State::InspectMessage(message) => layers.push(inspect::message_widget(message)),
|
State::InspectMessage(message) => layers.push(inspect::message_widget(message)),
|
||||||
|
State::InspectSession(session) => layers.push(inspect::session_widget(session)),
|
||||||
}
|
}
|
||||||
|
|
||||||
for popup in &self.popups {
|
for popup in &self.popups {
|
||||||
|
|
@ -483,14 +484,32 @@ impl EuphRoom {
|
||||||
|
|
||||||
fn list_nick_list_focus_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
fn list_nick_list_focus_key_bindings(&self, bindings: &mut KeyBindingsList) {
|
||||||
util::list_list_key_bindings(bindings);
|
util::list_list_key_bindings(bindings);
|
||||||
|
|
||||||
|
bindings.binding("i", "inspect session");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_nick_list_focus_input_event(&mut self, event: &InputEvent) -> bool {
|
fn handle_nick_list_focus_input_event(
|
||||||
|
&mut self,
|
||||||
|
event: &InputEvent,
|
||||||
|
status: &RoomStatus,
|
||||||
|
) -> bool {
|
||||||
if util::handle_list_input_event(&mut self.nick_list, event) {
|
if util::handle_list_input_event(&mut self.nick_list, event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Inspect users
|
if let key!('i') = event {
|
||||||
|
if let RoomStatus::Connected(Status::Joined(joined)) = status {
|
||||||
|
// TODO Fix euphoxide to use session_id as hash
|
||||||
|
if let Some(id) = self.nick_list.cursor() {
|
||||||
|
if id == joined.session.id {
|
||||||
|
self.state = State::InspectSession(joined.session.clone());
|
||||||
|
} else if let Some(session) = joined.listing.get(&id) {
|
||||||
|
self.state = State::InspectSession(session.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -550,7 +569,7 @@ impl EuphRoom {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.handle_nick_list_focus_input_event(event) {
|
if self.handle_nick_list_focus_input_event(event, &status) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -573,7 +592,9 @@ impl EuphRoom {
|
||||||
State::Nick(_) => nick::list_key_bindings(bindings),
|
State::Nick(_) => nick::list_key_bindings(bindings),
|
||||||
State::Account(account) => account.list_key_bindings(bindings),
|
State::Account(account) => account.list_key_bindings(bindings),
|
||||||
State::Links(links) => links.list_key_bindings(bindings),
|
State::Links(links) => links.list_key_bindings(bindings),
|
||||||
State::InspectMessage(_) => inspect::list_key_bindings(bindings),
|
State::InspectMessage(_) | State::InspectSession(_) => {
|
||||||
|
inspect::list_key_bindings(bindings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -643,13 +664,15 @@ impl EuphRoom {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State::InspectMessage(_) => match inspect::handle_input_event(event) {
|
State::InspectMessage(_) | State::InspectSession(_) => {
|
||||||
|
match inspect::handle_input_event(event) {
|
||||||
inspect::EventResult::NotHandled => false,
|
inspect::EventResult::NotHandled => false,
|
||||||
inspect::EventResult::Close => {
|
inspect::EventResult::Close => {
|
||||||
self.state = State::Normal;
|
self.state = State::Normal;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue