mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-19 06:04:31 -04:00
refactor(multiverse): Turn the read receipt rendering logic into a widget
This commit is contained in:
@@ -11,13 +11,13 @@ use color_eyre::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
|
||||
use futures_util::{pin_mut, StreamExt as _};
|
||||
use imbl::Vector;
|
||||
use layout::Flex;
|
||||
use matrix_sdk::{
|
||||
authentication::matrix::MatrixSession,
|
||||
config::StoreConfig,
|
||||
encryption::{BackupDownloadStrategy, EncryptionSettings},
|
||||
reqwest::Url,
|
||||
ruma::{
|
||||
api::client::receipt::create_receipt::v3::ReceiptType,
|
||||
events::room::message::{MessageType, RoomMessageEventContent},
|
||||
MilliSecondsSinceUnixEpoch, OwnedRoomId, RoomId,
|
||||
},
|
||||
@@ -34,11 +34,13 @@ use matrix_sdk_ui::{
|
||||
Timeline as SdkTimeline,
|
||||
};
|
||||
use ratatui::{prelude::*, style::palette::tailwind, widgets::*};
|
||||
use read_receipts::ReadReceipts;
|
||||
use status::Status;
|
||||
use tokio::{runtime::Handle, spawn, task::JoinHandle};
|
||||
use tracing::{error, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
mod read_receipts;
|
||||
mod room_list;
|
||||
mod status;
|
||||
|
||||
@@ -67,6 +69,16 @@ struct Cli {
|
||||
proxy: Option<Url>,
|
||||
}
|
||||
|
||||
/// Helper function to create a centered rect using up certain percentage of the
|
||||
/// available rect `r`
|
||||
fn popup_area(area: Rect, percent_x: u16, percent_y: u16) -> Rect {
|
||||
let vertical = Layout::vertical([Constraint::Percentage(percent_y)]).flex(Flex::Center);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(percent_x)]).flex(Flex::Center);
|
||||
let [area] = vertical.areas(area);
|
||||
let [area] = horizontal.areas(area);
|
||||
area
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let file_writer = tracing_appender::rolling::hourly("/tmp/", "logs-");
|
||||
@@ -159,8 +171,14 @@ impl App {
|
||||
// stopped.
|
||||
sync_service.start().await;
|
||||
|
||||
let room_list = RoomList::new(rooms, ui_rooms.clone(), room_infos, sync_service.clone());
|
||||
let status = Status::new();
|
||||
let room_list = RoomList::new(
|
||||
rooms,
|
||||
ui_rooms.clone(),
|
||||
room_infos,
|
||||
sync_service.clone(),
|
||||
status.handle(),
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
sync_service,
|
||||
@@ -277,31 +295,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark the currently selected room as read.
|
||||
async fn mark_as_read(&mut self) {
|
||||
let Some(room) = self
|
||||
.room_list
|
||||
.get_selected_room_id()
|
||||
.and_then(|room_id| self.ui_rooms.lock().get(&room_id).cloned())
|
||||
else {
|
||||
self.status.set_message("missing room or nothing to show".to_owned());
|
||||
return;
|
||||
};
|
||||
|
||||
// Mark as read!
|
||||
match room.timeline().unwrap().mark_as_read(ReceiptType::Read).await {
|
||||
Ok(did) => {
|
||||
self.status.set_message(format!(
|
||||
"did {}send a read receipt!",
|
||||
if did { "" } else { "not " }
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
self.status.set_message(format!("error when marking a room as read: {err}",));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn toggle_reaction_to_latest_msg(&mut self) {
|
||||
let selected = self.room_list.get_selected_room_id();
|
||||
|
||||
@@ -433,7 +426,11 @@ impl App {
|
||||
|
||||
Char('L') => self.toggle_reaction_to_latest_msg().await,
|
||||
|
||||
Char('r') => self.set_mode(DetailsMode::ReadReceipts),
|
||||
Char('r') => {
|
||||
if self.room_list.get_selected_room_id().is_some() {
|
||||
self.set_mode(DetailsMode::ReadReceipts);
|
||||
}
|
||||
}
|
||||
Char('t') => self.set_mode(DetailsMode::TimelineItems),
|
||||
Char('e') => self.set_mode(DetailsMode::Events),
|
||||
Char('l') => self.set_mode(DetailsMode::LinkedChunk),
|
||||
@@ -446,7 +443,7 @@ impl App {
|
||||
}
|
||||
|
||||
Char('m') if self.details_mode == DetailsMode::ReadReceipts => {
|
||||
self.mark_as_read().await
|
||||
self.room_list.mark_as_read().await
|
||||
}
|
||||
|
||||
_ => {}
|
||||
@@ -542,35 +539,14 @@ impl App {
|
||||
if let Some(room_id) = self.room_list.get_selected_room_id() {
|
||||
match self.details_mode {
|
||||
DetailsMode::ReadReceipts => {
|
||||
// In read receipts mode, show the read receipts object as computed
|
||||
// by the client.
|
||||
match self.ui_rooms.lock().get(&room_id).cloned() {
|
||||
Some(room) => {
|
||||
let receipts = room.read_receipts();
|
||||
render_paragraph(
|
||||
buf,
|
||||
format!(
|
||||
r#"Read receipts:
|
||||
- unread: {}
|
||||
- notifications: {}
|
||||
- mentions: {}
|
||||
// In read receipts mode, show the read receipts object as computed by the
|
||||
// client.
|
||||
let rooms = self.ui_rooms.lock();
|
||||
let room = rooms.get(&room_id);
|
||||
|
||||
---
|
||||
let mut read_receipts = ReadReceipts::new(room);
|
||||
|
||||
{:?}
|
||||
"#,
|
||||
receipts.num_unread,
|
||||
receipts.num_notifications,
|
||||
receipts.num_mentions,
|
||||
receipts
|
||||
),
|
||||
)
|
||||
}
|
||||
None => render_paragraph(
|
||||
buf,
|
||||
"(room disappeared in the room list service)".to_owned(),
|
||||
),
|
||||
}
|
||||
read_receipts.render(inner_area, buf);
|
||||
}
|
||||
|
||||
DetailsMode::TimelineItems => {
|
||||
|
||||
65
labs/multiverse/src/read_receipts.rs
Normal file
65
labs/multiverse/src/read_receipts.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use matrix_sdk_ui::room_list_service::Room;
|
||||
use ratatui::{
|
||||
prelude::*,
|
||||
widgets::{Block, Clear, Paragraph, Wrap},
|
||||
};
|
||||
|
||||
use crate::{popup_area, TEXT_COLOR};
|
||||
|
||||
pub struct ReadReceipts<'a> {
|
||||
room: Option<&'a Room>,
|
||||
}
|
||||
|
||||
impl<'a> ReadReceipts<'a> {
|
||||
pub fn new(room: Option<&'a Room>) -> Self {
|
||||
Self { room }
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut ReadReceipts<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let read_receipt_block = Block::bordered().title("Read receipts");
|
||||
let read_receipt_area = popup_area(area, 80, 80);
|
||||
Clear.render(read_receipt_area, buf);
|
||||
|
||||
match self.room {
|
||||
Some(room) => {
|
||||
let receipts = room.read_receipts();
|
||||
let content = format!(
|
||||
r#"Read receipts:
|
||||
- unread: {}
|
||||
- notifications: {}
|
||||
- mentions: {}
|
||||
|
||||
---
|
||||
|
||||
{:?}
|
||||
"#,
|
||||
receipts.num_unread,
|
||||
receipts.num_notifications,
|
||||
receipts.num_mentions,
|
||||
receipts
|
||||
);
|
||||
|
||||
Paragraph::new(content)
|
||||
.block(read_receipt_block.clone())
|
||||
.fg(TEXT_COLOR)
|
||||
.wrap(Wrap { trim: false })
|
||||
.render(read_receipt_area, buf);
|
||||
}
|
||||
None => {
|
||||
let content = "(room disappeared in the room list service)";
|
||||
Paragraph::new(content)
|
||||
.block(read_receipt_block.clone())
|
||||
.fg(TEXT_COLOR)
|
||||
.wrap(Wrap { trim: false })
|
||||
.render(read_receipt_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
read_receipt_block.render(read_receipt_area, buf);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,14 @@ use std::{
|
||||
};
|
||||
|
||||
use imbl::Vector;
|
||||
use matrix_sdk::ruma::OwnedRoomId;
|
||||
use matrix_sdk::ruma::{api::client::receipt::create_receipt::v3::ReceiptType, OwnedRoomId};
|
||||
use matrix_sdk_ui::{room_list_service, sync_service::SyncService};
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
|
||||
use crate::{UiRooms, ALT_ROW_COLOR, HEADER_BG, NORMAL_ROW_COLOR, SELECTED_STYLE_FG, TEXT_COLOR};
|
||||
use crate::{
|
||||
status::StatusHandle, UiRooms, ALT_ROW_COLOR, HEADER_BG, NORMAL_ROW_COLOR, SELECTED_STYLE_FG,
|
||||
TEXT_COLOR,
|
||||
};
|
||||
|
||||
/// Extra room information, like its display name, etc.
|
||||
#[derive(Clone)]
|
||||
@@ -29,6 +32,8 @@ pub type RoomInfos = Arc<Mutex<HashMap<OwnedRoomId, ExtraRoomInfo>>>;
|
||||
pub struct RoomList {
|
||||
pub state: ListState,
|
||||
|
||||
pub status_handle: StatusHandle,
|
||||
|
||||
pub rooms: Rooms,
|
||||
|
||||
/// Room list service rooms known to the app.
|
||||
@@ -50,10 +55,12 @@ impl RoomList {
|
||||
ui_rooms: UiRooms,
|
||||
room_infos: RoomInfos,
|
||||
sync_service: Arc<SyncService>,
|
||||
status_handle: StatusHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
state: Default::default(),
|
||||
rooms,
|
||||
status_handle,
|
||||
room_infos,
|
||||
current_room_subscription: None,
|
||||
ui_rooms,
|
||||
@@ -130,6 +137,31 @@ impl RoomList {
|
||||
self.current_room_subscription = Some(room);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark the currently selected room as read.
|
||||
pub async fn mark_as_read(&mut self) {
|
||||
let Some(room) = self
|
||||
.get_selected_room_id()
|
||||
.and_then(|room_id| self.ui_rooms.lock().get(&room_id).cloned())
|
||||
else {
|
||||
self.status_handle.set_message("missing room or nothing to show".to_owned());
|
||||
return;
|
||||
};
|
||||
|
||||
// Mark as read!
|
||||
match room.timeline().unwrap().mark_as_read(ReceiptType::Read).await {
|
||||
Ok(did) => {
|
||||
self.status_handle.set_message(format!(
|
||||
"did {}send a read receipt!",
|
||||
if did { "" } else { "not " }
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
self.status_handle
|
||||
.set_message(format!("error when marking a room as read: {err}",));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut RoomList {
|
||||
|
||||
Reference in New Issue
Block a user