From 1eb04dd23ad7a45b2a3fdd258e1902c2ca4219e4 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Thu, 6 Jul 2023 20:47:06 +0200 Subject: [PATCH] feature (UI - main): started work on timeline view implementation --- src/app/mod.rs | 14 +++++----- src/app/status.rs | 26 ++++++++++++------ src/ui/mod.rs | 69 +++++++++++++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 52db608..478db4f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -55,7 +55,7 @@ impl App<'_> { Self { ui: ui::UI::new(), accounts_manager: AccountsManager::new(config), - status: Status::default(), + status: Status::new(None), channel_tx, channel_rx, @@ -124,12 +124,14 @@ impl App<'_> { None => return Err(Error::msg("failed to get current client")) }.clone(); - if !self.matrix_listener_killer.is_cancelled() { - self.matrix_listener_killer.cancel(); - self.matrix_listener_killer = CancellationToken::new(); - } + self.matrix_listener_killer.cancel(); + self.matrix_listener_killer = CancellationToken::new(); - tokio::task::spawn(event::poll_matrix_events(self.channel_tx.clone(), self.matrix_listener_killer.clone(), client)); + // Spawn Matrix Event Listener + tokio::task::spawn(event::poll_matrix_events(self.channel_tx.clone(), self.matrix_listener_killer.clone(), client.clone())); + + // Reset Status + self.status = Status::new(Some(client)); let account = self.account()?; let name = account.name().clone(); diff --git a/src/app/status.rs b/src/app/status.rs index 44c6c96..a2fbdda 100644 --- a/src/app/status.rs +++ b/src/app/status.rs @@ -1,3 +1,5 @@ +use matrix_sdk::Client; +use matrix_sdk::ruma::{room_id, RoomId, ServerName}; pub enum State { None, @@ -9,21 +11,29 @@ pub struct Status { state: State, account_name: String, account_user_id: String, - current_room_id: u32, + + client: Option, + rooms: Vec, + current_room_id: Option, } -impl Default for Status { - fn default() -> Self { +impl Status { + pub fn new(client: Option) -> Self { + let rooms = match &client { + Some(c) => c.joined_rooms(), + None => Vec::new(), + }; + Self { state: State::None, account_name: "".to_string(), account_user_id: "".to_string(), - current_room_id: 0, + client, + rooms, + current_room_id: Some(0), } } -} -impl Status { pub fn account_name(&self) -> &String { &self.account_name } @@ -40,8 +50,8 @@ impl Status { self.account_user_id = user_id; } - pub fn room(&self) -> Option<()> { - None + pub fn room(&self) -> Option<&matrix_sdk::room::Joined> { + self.rooms.get(self.current_room_id? as usize) } pub fn state(&self) -> &State { diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 73e56dd..aad617f 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -17,6 +17,7 @@ use tui::text::{Spans, Span, Text}; use tui::widgets::{Paragraph, Wrap}; use tui_textarea::{Input, Key, TextArea}; use cli_log::{error, warn, info}; +use matrix_sdk::{room::MessagesOptions, ruma::events::{AnyTimelineEvent, AnyMessageLikeEvent}}; #[derive(Clone, Copy)] pub enum SetupInputPosition { @@ -248,9 +249,7 @@ impl UI<'_> { pub fn input_position(&self) -> &MainInputPosition { &self.input_position } - pub async fn update(&'_ mut self, status: &Status) -> Result<()> { - // TODO: make thread safe - + pub async fn update(&mut self, status: &Status) -> Result<()> { let chunk = self.terminal.size()?; let main_chunks = Layout::default() @@ -277,23 +276,59 @@ impl UI<'_> { status_content.extend(Text::styled(status.account_user_id(), Style::default())); status_content.extend(Text::styled("settings", Style::default().fg(Color::LightMagenta).add_modifier(Modifier::ITALIC | Modifier::UNDERLINED))); + let messages_config = MessagesOptions::backward(); + let messages_content = match status.room() { - _ => { + Some(r) => { + r.messages(messages_config) + .await? + .chunk + .iter() + .rev() + .map(|event| { + match event.event.deserialize() { + Ok(timeline_event) => match timeline_event { + + // Message Like Events + AnyTimelineEvent::MessageLike(message_like_event) => { + let (content, color) = match &message_like_event { + AnyMessageLikeEvent::RoomMessage(room_message_event) => { + let message_content = &room_message_event + .as_original() + .unwrap() + .content + .body(); + + (message_content.to_string(), Color::White) + }, + _ => ("~~~ 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)), + ]) + }, + + // State Events + AnyTimelineEvent::State(state) => { + 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)) + ]) + } + }, + Err(_) => Spans::from( + Span::styled("Failed to deserialize event", Style::default().fg(Color::Red)) + ) + } + }) + .collect::>() + }, + None => { vec![Spans::from(Span::styled("No room selected!", Style::default().fg(Color::Magenta)))] }, - // Some(r) => { - // r.messages - // .iter() - // .rev() - // .map(|msg| { - // Spans::from(vec![ - // Span::styled(&msg.author, Style::default().fg(Color::Cyan)), - // Span::styled(": ", Style::default().fg(Color::Cyan)), - // Span::styled(&msg.message, Style::default().fg(Color::White)), - // ]) - // }) - // .collect::>() - // }, }; // calculate to widgets colors, based of which widget is currently selected