1
0
Fork 0

feature: (room timeline): Changed timeline view to a scrollable list / Made single messages/events selectable / Implemented Message sending

This commit is contained in:
antifallobst 2023-07-08 23:09:53 +02:00
parent 658f05b8d3
commit 98f7e806de
4 changed files with 139 additions and 30 deletions

View File

@ -108,20 +108,84 @@ impl Event {
}
input => {
match app.ui.input_position() {
ui::MainInputPosition::MessageCompose => { app.ui.message_compose.input(input); },
ui::MainInputPosition::MessageCompose => {
match input {
tui_textarea::Input {key: tui_textarea::Key::Enter, alt: true, ..} => {
match app.status.room_mut() {
Some(room) => {
room.send(app.ui.message_compose.lines().join("\n")).await?;
app.ui.message_compose_clear();
},
None => ()
};
}
_ => { app.ui.message_compose.input(input); }
};
},
ui::MainInputPosition::Rooms => {
match input {
tui_textarea::Input {key: tui_textarea::Key::Down, ..} => {
tui_textarea::Input {key: tui_textarea::Key::Up, ..} => {
let i = match app.ui.rooms_state.selected() {
Some(i) => {
if i >= app.status.rooms().len() - 1 { 0 }
else { i + 1 }
if i > 0 { i - 1 }
else { i }
},
None => 0,
};
app.ui.rooms_state.select(Some(i));
app.status.set_room_by_index(i)?;
},
tui_textarea::Input {key: tui_textarea::Key::Down, ..} => {
let i = match app.ui.rooms_state.selected() {
Some(i) => {
if i < app.status.rooms().len() { i + 1 }
else { i }
},
None => 0,
};
app.ui.rooms_state.select(Some(i));
app.status.set_room_by_index(i)?;
},
_ => ()
};
},
ui::MainInputPosition::Messages => {
match input {
tui_textarea::Input {key: tui_textarea::Key::Up, ..} => {
match app.status.room_mut(){
Some(room) => {
let len = room.timeline().len();
let i = match room.view_scroll() {
Some(i) => i+1,
None => 0,
};
if i < len {
room.set_view_scroll(Some(i))
}
if i <= len - 5 {
room.poll_old_timeline().await?;
}
},
None => (),
};
},
tui_textarea::Input {key: tui_textarea::Key::Down, ..} => {
match app.status.room_mut(){
Some(room) => {
match room.view_scroll() {
Some(i) => {
if i == 0 {
room.set_view_scroll(None);
} else {
room.set_view_scroll(Some(i - 1));
}
}
None => (),
};
},
None => (),
};
},
_ => ()
};
},

View File

@ -142,9 +142,7 @@ impl App<'_> {
for (_, room) in self.status.rooms_mut() {
room.update_name().await?;
room.poll_old_timeline().await?;
room.poll_old_timeline().await?;
room.poll_old_timeline().await?;
for _ in 0..3 { room.poll_old_timeline().await?; }
}
info!("Initializing client for the current account");

View File

@ -1,6 +1,11 @@
use std::any::Any;
use indexmap::IndexMap;
use matrix_sdk::{Client,
ruma::{events::{AnyTimelineEvent}, RoomId},
ruma::{events::{AnyTimelineEvent,
room::message::RoomMessageEventContent,
StateEventType},
RoomId,
TransactionId},
room::MessagesOptions};
use anyhow::{Result, Error};
use cli_log::{error, warn, info};
@ -16,6 +21,7 @@ pub struct Room {
name: String,
timeline: Vec<AnyTimelineEvent>,
timeline_end: Option<String>,
view_scroll: Option<usize>,
}
pub struct Status {
@ -35,10 +41,17 @@ impl Room {
name: "".to_string(),
timeline: Vec::new(),
timeline_end: None,
view_scroll: None,
}
}
pub async fn poll_old_timeline(&mut self) -> Result<()> {
if let Some(AnyTimelineEvent::State(event)) = &self.timeline.get(0) {
if event.event_type() == StateEventType::RoomCreate {
return Ok(())
}
}
let mut messages_options = MessagesOptions::backward();
messages_options = match &self.timeline_end {
Some(end) => messages_options.from(end.as_str()),
@ -73,6 +86,21 @@ impl Room {
}
pub fn timeline(&self) -> &Vec<AnyTimelineEvent> { &self.timeline }
pub async fn send(&mut self, message: String) -> Result<()> {
let content = RoomMessageEventContent::text_plain(message);
let id = TransactionId::new();
self.matrix_room.send(content, Some(&id)).await?;
Ok(())
}
pub fn view_scroll(&self) -> Option<usize> {
self.view_scroll
}
pub fn set_view_scroll(&mut self, scroll: Option<usize>) {
self.view_scroll = scroll;
}
}
impl Status {
@ -116,6 +144,10 @@ impl Status {
self.rooms.get(self.current_room_id.as_str())
}
pub fn room_mut(&mut self) -> Option<&mut Room> {
self.rooms.get_mut(self.current_room_id.as_str())
}
pub fn rooms(&self) -> &IndexMap<String, Room> {
&self.rooms
}

View File

@ -11,7 +11,7 @@ use anyhow::{Error, Result};
use std::io::Stdout;
use std::io;
use tui::{backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, widgets::{Block, Borders, Widget}, Terminal, Frame};
use tui::layout::Alignment;
use tui::layout::{Alignment, Corner};
use tui::style::{Color, Modifier, Style};
use tui::text::{Spans, Span, Text};
use tui::widgets::{List, ListItem, ListState, Paragraph, Wrap};
@ -225,7 +225,7 @@ impl UI<'_> {
let mut message_compose = TextArea::default();
message_compose.set_block(
Block::default()
.title("Message Compose")
.title("Message Compose (send: <Alt>+<Enter>)")
.borders(Borders::ALL));
info!("Initialized UI");
@ -251,6 +251,14 @@ impl UI<'_> {
pub fn input_position(&self) -> &MainInputPosition { &self.input_position }
pub fn message_compose_clear(&mut self) {
self.message_compose = TextArea::default();
self.message_compose.set_block(
Block::default()
.title("Message Compose (send: <Alt>+<Enter>)")
.borders(Borders::ALL));
}
pub async fn update(&mut self, status: &Status) -> Result<()> {
let chunk = self.terminal.size()?;
@ -289,6 +297,7 @@ impl UI<'_> {
Some(r) => {
r.timeline()
.iter()
.rev()
.map(|event| {
match event {
@ -306,28 +315,33 @@ impl UI<'_> {
},
_ => ("~~~ not supported message like event ~~~".to_string(), Color::Red)
};
Spans::from(vec![
Span::styled(message_like_event.sender().to_string(), Style::default().fg(Color::Cyan)),
Span::styled(": ", Style::default().fg(Color::Cyan)),
Span::styled(content, Style::default().fg(color)),
])
let mut text = Text::styled(message_like_event.sender().to_string(),
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD));
text.extend(Text::styled(content.to_string(),
Style::default().fg(color)));
ListItem::new(text)
},
// State Events
AnyTimelineEvent::State(state) => {
Spans::from(vec![
ListItem::new(vec![Spans::from(vec![
Span::styled(state.sender().to_string(), Style::default().fg(Color::DarkGray)),
Span::styled(": ", Style::default().fg(Color::DarkGray)),
Span::styled(state.event_type().to_string(), Style::default().fg(Color::DarkGray))
])
])])
}
}
})
.collect::<Vec<_>>()
},
None => {
vec![Spans::from(Span::styled("No room selected!", Style::default().fg(Color::Magenta)))]
},
vec![ListItem::new(Text::styled("No room selected!", Style::default().fg(Color::Magenta)))]
}
};
let mut messages_state = ListState::default();
if let Some(room) = status.room() {
messages_state.select(room.view_scroll());
};
// calculate to widgets colors, based of which widget is currently selected
@ -355,31 +369,32 @@ impl UI<'_> {
};
// initiate the widgets
let status = Paragraph::new(status_content)
let status_panel = Paragraph::new(status_content)
.block(Block::default()
.title("Status")
.borders(Borders::ALL)
.style(Style::default().fg(colors[MainInputPosition::Status as usize])))
.alignment(Alignment::Left);
let rooms = List::new(rooms_content)
let rooms_panel = List::new(rooms_content)
.block(Block::default()
.title("Rooms")
.title("Rooms (navigate: arrow keys)")
.borders(Borders::ALL)
.style(Style::default().fg(colors[MainInputPosition::Rooms as usize])))
.style(Style::default().fg(Color::DarkGray))
.highlight_style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD))
.highlight_symbol(">");
let messages = Paragraph::new(messages_content)
let messages_panel = List::new(messages_content)
.block(Block::default()
.title("Messages")
.borders(Borders::ALL)
.style(Style::default().fg(colors[MainInputPosition::Messages as usize])))
.alignment(Alignment::Left)
.wrap(Wrap {trim: false});
.start_corner(Corner::BottomLeft)
.highlight_symbol(">")
.highlight_style(Style::default().fg(Color::LightMagenta).add_modifier(Modifier::BOLD));
let room_info = Block::default()
let room_info_panel = Block::default()
.title("Room Info")
.borders(Borders::ALL)
.style(Style::default().fg(colors[MainInputPosition::RoomInfo as usize]));
@ -387,11 +402,11 @@ impl UI<'_> {
// render the widgets
self.terminal.draw(|frame| {
frame.render_widget(status, left_chunks[0]);
frame.render_stateful_widget(rooms, left_chunks[1], &mut self.rooms_state);
frame.render_widget(messages, middle_chunks[0]);
frame.render_widget(status_panel, left_chunks[0]);
frame.render_stateful_widget(rooms_panel, left_chunks[1], &mut self.rooms_state);
frame.render_stateful_widget(messages_panel, middle_chunks[0], & mut messages_state);
frame.render_widget(self.message_compose.widget(), middle_chunks[1]);
frame.render_widget(room_info, right_chunks[0]);
frame.render_widget(room_info_panel, right_chunks[0]);
})?;
Ok(())