Show room avatars, handle edits and redactions

Also hide rooms with tombstone in roomlist if we've joined the room the
tombstone points to.

Fixed rooms joined from different clients having an empty display name.
main
Amanda Graven 2021-01-16 14:44:18 +01:00
parent f25d3b6821
commit a8b76eaded
Signed by: amanda
GPG Key ID: 45C461CDC9286390
3 changed files with 280 additions and 90 deletions

View File

@ -9,12 +9,12 @@ edition = "2018"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
async-stream = "0.3" async-stream = "0.3"
async-trait = "0.1"
dirs-next = "2.0" dirs-next = "2.0"
futures = "0.3" futures = "0.3"
iced = { git = "https://github.com/hecrj/iced", rev = "31522e3", features = ["debug", "image", "tokio"] } iced = { git = "https://github.com/hecrj/iced", rev = "31522e3", features = ["debug", "image", "tokio"] }
iced_futures = { git = "https://github.com/hecrj/iced", rev = "31522e3" } iced_futures = { git = "https://github.com/hecrj/iced", rev = "31522e3" }
#iced_glow = { git = "https://github.com/hecrj/iced", rev = "31522e3", features = ["image"] } #iced_glow = { git = "https://github.com/hecrj/iced", rev = "31522e3", features = ["image"] }
#matrix-sdk-common-macros = { git = "https://github.com/matrix-org/matrix-rust-sdk", rev = "e65915e" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
time = "0.2" time = "0.2"
tokio = { version = "1.0", features = ["sync"] } tokio = { version = "1.0", features = ["sync"] }
@ -23,7 +23,7 @@ tracing-subscriber = { version = "0.2", features = ["parking_lot"] }
[dependencies.matrix-sdk] [dependencies.matrix-sdk]
git = "https://github.com/matrix-org/matrix-rust-sdk" git = "https://github.com/matrix-org/matrix-rust-sdk"
rev = "6435269" rev = "40c53f0"
default_features = false default_features = false
features = ["encryption", "sqlite_cryptostore", "messages", "rustls-tls", "unstable-synapse-quirks"] features = ["encryption", "sqlite_cryptostore", "messages", "rustls-tls", "unstable-synapse-quirks"]

View File

@ -22,9 +22,9 @@ pub type Error = anyhow::Error;
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Session { pub struct Session {
access_token: String, access_token: String,
user_id: UserId, pub user_id: UserId,
device_id: Box<DeviceId>, pub device_id: Box<DeviceId>,
homeserver: String, pub homeserver: String,
} }
impl From<Session> for matrix_sdk::Session { impl From<Session> for matrix_sdk::Session {
@ -121,7 +121,7 @@ pub async fn restore_login(session: Session) -> Result<(Client, Session), Error>
let client = client(url)?; let client = client(url)?;
client.restore_login(session.clone().into()).await?; client.restore_login(session.clone().into()).await?;
//client.sync_once(SyncSettings::new()).await?; client.sync_once(SyncSettings::new()).await?;
Ok((client, session)) Ok((client, session))
} }
@ -162,7 +162,7 @@ fn write_session(session: &Session) -> Result<(), Error> {
pub fn parse_mxc(url: &str) -> Result<(Box<ServerName>, String), Error> { pub fn parse_mxc(url: &str) -> Result<(Box<ServerName>, String), Error> {
let url = Url::parse(&url)?; let url = Url::parse(&url)?;
anyhow::ensure!(url.scheme() == "mxc", "Not an mxc url"); anyhow::ensure!(url.scheme() == "mxc", "Not an mxc url");
let host = url.host_str().ok_or(anyhow::anyhow!("url"))?; let host = url.host_str().ok_or_else(|| anyhow::anyhow!("url"))?;
let server_name: Box<ServerName> = <&ServerName>::try_from(host)?.into(); let server_name: Box<ServerName> = <&ServerName>::try_from(host)?.into();
let path = url.path_segments().and_then(|mut p| p.next()); let path = url.path_segments().and_then(|mut p| p.next());
match path { match path {
@ -190,6 +190,45 @@ pub enum Event {
Token(String), Token(String),
} }
/*pub enum Sync {
MessageInvited()
MessageJoined(RoomId, AnyRoomEvent),
MessageLeft(RoomId, AnyRoomEvent),
}
struct Emitter {
client: Client,
sender: tokio::sync::mpsc::Sender<Sync>,
}
#[async_trait]
impl matrix_sdk::EventEmitter for Emitter {
async fn on_room_message(
&self,
room: RoomState,
message: &SyncMessageEvent<MessageEventContent>,
) {
let id = room.room_id().to_owned();
let full = AnyRoomEvent::Message(
AnyMessageEvent::RoomMessage(
message
.to_owned()
.into_full_event(id.clone()),
),
);
if let RoomState::Joined(room) = room {
self.sender
.send(Sync::MessageJoined(id, full))
.await
.ok();
}
}
async fn on_room_member(&self, room: RoomState, member: &SyncStateEvent<MemberEventContent>) {
}
}*/
impl<H, I> iced_futures::subscription::Recipe<H, I> for MatrixSync impl<H, I> iced_futures::subscription::Recipe<H, I> for MatrixSync
where where
H: std::hash::Hasher, H: std::hash::Hasher,
@ -214,11 +253,18 @@ where
.sync_with_callback( .sync_with_callback(
SyncSettings::new() SyncSettings::new()
.token(client.sync_token().await.unwrap()) .token(client.sync_token().await.unwrap())
.timeout(Duration::from_secs(90)) .timeout(Duration::from_secs(30)),
.full_state(true),
|response| async { |response| async {
sender.send(Event::Token(response.next_batch)).ok(); //sender.send(Event::Token(response.next_batch)).ok();
for (id, room) in response.rooms.join { for (id, room) in response.rooms.join {
for event in room.state.events {
let id = id.clone();
sender
.send(Event::Room(AnyRoomEvent::State(
event.into_full_event(id),
)))
.ok();
}
for event in room.timeline.events { for event in room.timeline.events {
let id = id.clone(); let id = id.clone();
let event = match event { let event = match event {

304
src/ui.rs
View File

@ -5,8 +5,8 @@ use std::{
use futures::executor::block_on; use futures::executor::block_on;
use iced::{ use iced::{
Align, Application, Button, Column, Command, Container, Element, Length, Row, Rule, Scrollable, Align, Application, Button, Column, Command, Container, Element, Image, Length, Row, Rule,
Subscription, Text, TextInput, Scrollable, Subscription, Text, TextInput,
}; };
use matrix_sdk::{ use matrix_sdk::{
api::r0::{ api::r0::{
@ -15,7 +15,10 @@ use matrix_sdk::{
}, },
events::{ events::{
key::verification::cancel::CancelCode as VerificationCancelCode, key::verification::cancel::CancelCode as VerificationCancelCode,
room::{member::MembershipState, message::MessageEventContent}, room::{
member::MembershipState,
message::{MessageEventContent, Relation, TextMessageEventContent},
},
AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, AnyStateEvent, AnyToDeviceEvent, AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, AnyStateEvent, AnyToDeviceEvent,
}, },
identifiers::{EventId, RoomAliasId, RoomId, UserId}, identifiers::{EventId, RoomAliasId, RoomId, UserId},
@ -47,6 +50,8 @@ pub struct RoomEntry {
pub alias: Option<RoomAliasId>, pub alias: Option<RoomAliasId>,
/// Defined display name /// Defined display name
pub display_name: Option<String>, pub display_name: Option<String>,
/// mxc url for the rooms avatar
pub avatar: Option<String>,
/// Person we're in a direct message with /// Person we're in a direct message with
pub direct: Option<UserId>, pub direct: Option<UserId>,
/// Cache of messages /// Cache of messages
@ -54,12 +59,13 @@ pub struct RoomEntry {
} }
impl RoomEntry { impl RoomEntry {
pub fn from_sdk(room: &matrix_sdk::JoinedRoom) -> Self { pub async fn from_sdk(room: &matrix_sdk::JoinedRoom) -> Self {
Self { Self {
direct: room.direct_target(), direct: room.direct_target(),
name: block_on(async { room.display_name().await }), name: room.display_name().await,
topic: room.topic().unwrap_or_default(), topic: room.topic().unwrap_or_default(),
alias: room.canonical_alias(), alias: room.canonical_alias(),
avatar: room.avatar_url(),
..Default::default() ..Default::default()
} }
} }
@ -99,9 +105,30 @@ impl MessageBuffer {
}; };
} }
fn remove(&mut self, id: &EventId) {
self.messages.retain(|e| e.event_id() != id);
self.known_ids.remove(&id);
}
/// Add a message to the buffer. /// Add a message to the buffer.
pub fn push(&mut self, event: AnyRoomEvent) { pub fn push(&mut self, event: AnyRoomEvent) {
self.known_ids.insert(event.event_id().clone()); self.known_ids.insert(event.event_id().clone());
if let AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(
matrix_sdk::events::MessageEvent {
content:
MessageEventContent::Text(TextMessageEventContent {
relates_to: Some(Relation::Replacement(ref replacement)),
..
}),
..
},
)) = event
{
self.remove(&replacement.event_id);
}
if let AnyRoomEvent::Message(AnyMessageEvent::RoomRedaction(ref redaction)) = event {
self.remove(&redaction.redacts);
}
self.messages.push(event); self.messages.push(event);
self.sort(); self.sort();
self.update_time(); self.update_time();
@ -111,6 +138,23 @@ impl MessageBuffer {
pub fn append(&mut self, mut events: Vec<AnyRoomEvent>) { pub fn append(&mut self, mut events: Vec<AnyRoomEvent>) {
events.retain(|e| !self.known_ids.contains(e.event_id())); events.retain(|e| !self.known_ids.contains(e.event_id()));
for event in events.iter() { for event in events.iter() {
// Handle replacement
if let AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(
matrix_sdk::events::MessageEvent {
content:
MessageEventContent::Text(TextMessageEventContent {
relates_to: Some(Relation::Replacement(replacement)),
..
}),
..
},
)) = event
{
self.remove(&replacement.event_id);
}
if let AnyRoomEvent::Message(AnyMessageEvent::RoomRedaction(redaction)) = event {
self.remove(&redaction.redacts);
}
self.known_ids.insert(event.event_id().clone()); self.known_ids.insert(event.event_id().clone());
} }
self.messages.append(&mut events); self.messages.append(&mut events);
@ -216,6 +260,23 @@ impl MainView {
} }
} }
pub fn room<'a>(&'a self, id: &RoomId) -> Result<&'a RoomEntry, Command<Message>> {
match self.rooms.get(&id) {
Some(room) => Ok(room),
None => {
let id = id.clone();
let client = self.client.clone();
let cmd = async move {
let room = client.get_joined_room(&id).unwrap();
let entry = RoomEntry::from_sdk(&room).await;
Message::ResetRoom(id, entry)
}
.into();
Err(cmd)
}
}
}
pub fn view(&mut self) -> Element<Message> { pub fn view(&mut self) -> Element<Message> {
// If settings view is open, display that instead // If settings view is open, display that instead
if let Some(ref mut settings) = self.settings_view { if let Some(ref mut settings) = self.settings_view {
@ -228,14 +289,23 @@ impl MainView {
.height(Length::Fill) .height(Length::Fill)
.scrollbar_width(5); .scrollbar_width(5);
let client = &self.client;
let rooms = &self.rooms;
// Group by DM and group conversation // Group by DM and group conversation
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
let (mut dm_rooms, mut group_rooms): ( let (mut dm_rooms, mut group_rooms): (
Vec<(&RoomId, &RoomEntry)>, Vec<(&RoomId, &RoomEntry)>,
Vec<(&RoomId, &RoomEntry)>, Vec<(&RoomId, &RoomEntry)>,
) = self ) = rooms
.rooms
.iter() .iter()
// Hide if we're in the room the tombstone points to
.filter(|(id, _)| {
!client
.get_joined_room(id)
.and_then(|j| j.tombstone())
.map(|t| rooms.contains_key(&t.replacement_room))
.unwrap_or(false)
})
.partition(|(_, room)| room.direct.is_some()); .partition(|(_, room)| room.direct.is_some());
// Sort // Sort
for list in [&mut dm_rooms, &mut group_rooms].iter_mut() { for list in [&mut dm_rooms, &mut group_rooms].iter_mut() {
@ -243,7 +313,6 @@ impl MainView {
RoomSorting::Alphabetic => { RoomSorting::Alphabetic => {
list.sort_unstable_by_key(|(_, room)| room.name.to_uppercase()) list.sort_unstable_by_key(|(_, room)| room.name.to_uppercase())
} }
// TODO: fix this
RoomSorting::Recent => list.sort_unstable_by(|(_, a), (_, b)| { RoomSorting::Recent => list.sort_unstable_by(|(_, a), (_, b)| {
a.messages.updated.cmp(&b.messages.updated).reverse() a.messages.updated.cmp(&b.messages.updated).reverse()
}), }),
@ -255,21 +324,32 @@ impl MainView {
self.group_buttons self.group_buttons
.resize_with(group_rooms.len(), Default::default); .resize_with(group_rooms.len(), Default::default);
// Create buttons // Create buttons
let images = &self.images;
let dm_buttons: Vec<Button<_>> = self let dm_buttons: Vec<Button<_>> = self
.dm_buttons .dm_buttons
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.map(|(idx, button)| { .map(|(idx, button)| {
// TODO: highlight selected // TODO: highlight selected
let (id, room) = dm_rooms[idx]; let (id, room) = unsafe { dm_rooms.get_unchecked(idx) };
let name = if room.name.is_empty() { let name = if room.name.is_empty() {
"Missing name" "Missing name"
} else { } else {
&room.name &room.name
}; };
Button::new(button, Text::new(name)) let mut row = Row::new().align_items(Align::Center);
if let Some(ref url) = room.avatar {
if let Some(handle) = images.get(url) {
row = row.push(
Image::new(handle.clone())
.width(20.into())
.height(20.into()),
);
}
}
Button::new(button, row.push(Text::new(name)))
.width(300.into()) .width(300.into())
.on_press(Message::SelectRoom(id.to_owned())) .on_press(Message::SelectRoom(id.to_owned().to_owned()))
}) })
.collect(); .collect();
let room_buttons: Vec<Button<_>> = self let room_buttons: Vec<Button<_>> = self
@ -277,10 +357,25 @@ impl MainView {
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.map(|(idx, button)| { .map(|(idx, button)| {
let (id, room) = group_rooms[idx]; let (id, room) = unsafe { group_rooms.get_unchecked(idx) };
Button::new(button, Text::new(&room.name)) let name = if room.name.is_empty() {
"Missing name"
} else {
&room.name
};
let mut row = Row::new().align_items(Align::Center);
if let Some(ref url) = room.avatar {
if let Some(handle) = images.get(url) {
row = row.push(
Image::new(handle.clone())
.width(20.into())
.height(20.into()),
);
}
}
Button::new(button, row.push(Text::new(name)))
.width(300.into()) .width(300.into())
.on_press(Message::SelectRoom(id.to_owned())) .on_press(Message::SelectRoom(id.to_owned().to_owned()))
}) })
.collect(); .collect();
// Add buttons to container // Add buttons to container
@ -322,11 +417,20 @@ impl MainView {
room.name.clone() room.name.clone()
}; };
let mut title_row = Row::new().align_items(Align::Center);
if let Some(handle) = room.avatar.as_deref().and_then(|a| images.get(a)) {
title_row = title_row.push(
Image::new(handle.to_owned())
.width(24.into())
.height(24.into()),
);
}
message_col = message_col message_col = message_col
.push(Text::new(title).size(25)) .push(title_row.push(Text::new(title).size(25)))
.push(Rule::horizontal(2)); .push(Rule::horizontal(2));
let mut scroll = Scrollable::new(&mut self.message_scroll) let mut scroll = Scrollable::new(&mut self.message_scroll)
.scrollbar_width(2) .scrollbar_width(2)
.spacing(4)
.height(Length::Fill); .height(Length::Fill);
// Backfill button or loading message // Backfill button or loading message
let backfill: Element<_> = if room.messages.loading { let backfill: Element<_> = if room.messages.loading {
@ -348,16 +452,28 @@ impl MainView {
.into() .into()
}; };
scroll = scroll.push(Container::new(backfill).width(Length::Fill).center_x()); scroll = scroll.push(Container::new(backfill).width(Length::Fill).center_x());
// mxid of most recent sender
let mut last_sender: Option<UserId> = None;
// Rendered display name of most recent sender
let mut sender = String::from("Unknown sender");
// Messages // Messages
for event in room.messages.messages.iter() { for event in room.messages.messages.iter() {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match event { match event {
AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(message)) => { AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(message)) => {
let sender = // Display sender if message is from new sender
match block_on(async { joined.get_member(&message.sender).await }) { if last_sender.as_ref() != Some(&message.sender) {
last_sender = Some(message.sender.clone());
sender = match block_on(async {
joined.get_member(&message.sender).await
}) {
Some(member) => member.name().to_owned(), Some(member) => member.name().to_owned(),
None => message.sender.to_string(), None => message.sender.to_string(),
}; };
scroll = scroll
.push(iced::Space::with_height(4.into()))
.push(Text::new(&sender).color([0.0, 0.0, 1.0]));
}
let content: Element<_> = match &message.content { let content: Element<_> = match &message.content {
MessageEventContent::Audio(audio) => { MessageEventContent::Audio(audio) => {
Text::new(format!("Audio message: {}", audio.body)) Text::new(format!("Audio message: {}", audio.body))
@ -380,7 +496,7 @@ impl MainView {
if let Some(ref url) = image.url { if let Some(ref url) = image.url {
match self.images.get(url) { match self.images.get(url) {
Some(handle) => Container::new( Some(handle) => Container::new(
iced::Image::new(handle.to_owned()) Image::new(handle.to_owned())
.width(800.into()) .width(800.into())
.height(1200.into()), .height(1200.into()),
) )
@ -414,13 +530,15 @@ impl MainView {
}; };
let row = Row::new() let row = Row::new()
.spacing(5) .spacing(5)
.push(Text::new(sender).color([0.0, 0.0, 1.0]))
.push(content) .push(content)
.push(Text::new(format_systime(message.origin_server_ts))); .push(Text::new(format_systime(message.origin_server_ts)));
scroll = scroll.push(row); scroll = scroll.push(row);
} }
AnyRoomEvent::Message(AnyMessageEvent::RoomEncrypted(_encrypted)) => { AnyRoomEvent::Message(AnyMessageEvent::RoomEncrypted(_encrypted)) => {
scroll = scroll.push(Text::new("Encrypted event").color([0.3, 0.3, 0.3])) scroll = scroll.push(Text::new("Encrypted event").color([0.3, 0.3, 0.3]));
}
AnyRoomEvent::RedactedMessage(_) => {
scroll = scroll.push(Text::new("Deleted message").color([0.3, 0.3, 0.3]));
} }
_ => (), _ => (),
} }
@ -468,21 +586,24 @@ impl MainView {
Some(emojis) => { Some(emojis) => {
let mut row = Row::new().push(Text::new("Verify emojis match:")); let mut row = Row::new().push(Text::new("Verify emojis match:"));
for (emoji, name) in emojis.iter() { for (emoji, name) in emojis.iter() {
row = row.push( row = row
Column::new() .push(
.align_items(iced::Align::Center) Column::new()
.push(Text::new(*emoji).size(32)) .align_items(iced::Align::Center)
.push(Text::new(*name)), .push(Text::new(*emoji).size(32))
); .push(Text::new(*name)),
)
.spacing(5);
} }
row.push( row.push(iced::Space::with_width(Length::Fill))
Button::new(&mut self.sas_accept_button, Text::new("Confirm")) .push(
.on_press(Message::VerificationConfirm), Button::new(&mut self.sas_accept_button, Text::new("Confirm"))
) .on_press(Message::VerificationConfirm),
.push( )
Button::new(&mut self.sas_deny_button, Text::new("Deny")) .push(
.on_press(Message::VerificationCancel), Button::new(&mut self.sas_deny_button, Text::new("Deny"))
) .on_press(Message::VerificationCancel),
)
} }
None => Row::new() None => Row::new()
.push( .push(
@ -564,9 +685,10 @@ pub enum Message {
// Main state messages // Main state messages
/// Reset state for room /// Reset state for room
ResetRoom(RoomId, RoomEntry), ResetRoom(RoomId, RoomEntry),
RoomName(RoomId, String),
/// Get backfill for given room /// Get backfill for given room
BackFill(RoomId), BackFill(RoomId),
/// Received backfille /// Received backfill
BackFilled(RoomId, MessageResponse), BackFilled(RoomId, MessageResponse),
/// Fetch an image pointed to by an mxc url /// Fetch an image pointed to by an mxc url
FetchImage(String), FetchImage(String),
@ -719,24 +841,16 @@ impl Application for Retrix {
*self = Retrix::LoggedIn(MainView::new(client.clone(), session)); *self = Retrix::LoggedIn(MainView::new(client.clone(), session));
let mut commands: Vec<Command<Message>> = Vec::new(); let mut commands: Vec<Command<Message>> = Vec::new();
for room in client.joined_rooms().into_iter() { for room in client.joined_rooms().into_iter() {
//let client = client.clone(); let room = std::sync::Arc::new(room);
let r = room.clone();
let command: Command<_> = async move { let command: Command<_> = async move {
let room = room.clone(); let entry = RoomEntry::from_sdk(&r).await;
let entry = RoomEntry::from_sdk(&room); Message::ResetRoom(r.room_id().to_owned(), entry)
// Display name calculation for DMs is bronk so we're doing it
// ourselves
/*if let Some(ref direct) = entry.direct {
let request = DisplayNameRequest::new(direct);
if let Ok(response) = client.send(request).await {
if let Some(name) = response.displayname {
entry.name = name;
}
}
}*/
Message::ResetRoom(room.room_id().to_owned(), entry)
} }
.into(); .into();
if let Some(url) = room.avatar_url() {
commands.push(async { Message::FetchImage(url) }.into())
}
commands.push(command); commands.push(command);
} }
return Command::batch(commands); return Command::batch(commands);
@ -807,45 +921,67 @@ impl Application for Retrix {
room.messages.push(AnyRoomEvent::State(event)); room.messages.push(AnyRoomEvent::State(event));
} }
AnyStateEvent::RoomName(ref name) => { AnyStateEvent::RoomName(ref name) => {
let room = view.rooms.entry(name.room_id.clone()).or_default(); let id = name.room_id.clone();
let room = view.rooms.entry(id.clone()).or_default();
room.display_name = name.content.name().map(String::from); room.display_name = name.content.name().map(String::from);
if let Some(joined) = view.client.get_joined_room(&name.room_id) {
room.name = block_on(async { joined.display_name().await });
}
room.messages.push(AnyRoomEvent::State(event)); room.messages.push(AnyRoomEvent::State(event));
let client = view.client.clone();
return async move {
let joined = client.get_joined_room(&id).unwrap();
Message::RoomName(id, joined.display_name().await)
}
.into();
} }
AnyStateEvent::RoomTopic(ref topic) => { AnyStateEvent::RoomTopic(ref topic) => {
let room = view.rooms.entry(topic.room_id.clone()).or_default(); let room = view.rooms.entry(topic.room_id.clone()).or_default();
room.topic = topic.content.topic.clone(); room.topic = topic.content.topic.clone();
room.messages.push(AnyRoomEvent::State(event)); room.messages.push(AnyRoomEvent::State(event));
} }
AnyStateEvent::RoomAvatar(ref avatar) => {
let room = view.rooms.entry(avatar.room_id.clone()).or_default();
room.messages.push(AnyRoomEvent::State(event));
if let Some(url) = room.avatar.clone() {
room.avatar = Some(url.clone());
return async { Message::FetchImage(url) }.into();
}
}
AnyStateEvent::RoomCreate(ref create) => { AnyStateEvent::RoomCreate(ref create) => {
// Add room to the entry list // Add room to the entry list
let room = match view.client.get_joined_room(&create.room_id) { let joined = view.client.get_joined_room(&create.room_id).unwrap();
Some(joined) => view let id = create.room_id.clone();
.rooms return async move {
.entry(create.room_id.clone()) let mut entry = RoomEntry::from_sdk(&joined).await;
.or_insert_with(|| RoomEntry::from_sdk(&joined)), entry.messages.push(AnyRoomEvent::State(event));
None => view.rooms.entry(create.room_id.clone()).or_default(), Message::ResetRoom(id, entry)
}; }
room.messages.push(AnyRoomEvent::State(event)); .into();
} }
AnyStateEvent::RoomMember(ref member) => { AnyStateEvent::RoomMember(ref member) => {
let room = view.rooms.entry(member.room_id.clone()).or_default(); let room = view.rooms.entry(member.room_id.clone()).or_default();
let client = view.client.clone(); let client = view.client.clone();
// If we left a room, remove it from the RoomEntry list // If we left a room, remove it from the RoomEntry list
let own_id = block_on(async { client.user_id().await }) if member.state_key == view.session.user_id {
.unwrap() match member.content.membership {
.to_string(); MembershipState::Join => {
if member.content.membership == MembershipState::Leave let id = member.room_id.clone();
&& member.state_key == own_id return async move {
{ let joined = client.get_joined_room(&id).unwrap();
// Deselect room if we're leaving selected room let entry = RoomEntry::from_sdk(&joined).await;
if view.selected.as_ref() == Some(&member.room_id) {
view.selected = None; Message::ResetRoom(id, entry)
}
.into();
}
MembershipState::Leave => {
// Deselect room if we're leaving selected room
if view.selected.as_ref() == Some(&member.room_id) {
view.selected = None;
}
view.rooms.remove(&member.room_id);
return Command::none();
}
_ => (),
} }
view.rooms.remove(&member.room_id);
return Command::none();
} }
room.messages.push(AnyRoomEvent::State(event)); room.messages.push(AnyRoomEvent::State(event));
} }
@ -855,14 +991,20 @@ impl Application for Retrix {
room.messages.push(AnyRoomEvent::State(event)); room.messages.push(AnyRoomEvent::State(event));
} }
}, },
_ => (), AnyRoomEvent::RedactedMessage(redacted) => {
let room = view.rooms.entry(redacted.room_id().clone()).or_default();
room.messages.push(AnyRoomEvent::RedactedMessage(redacted));
}
AnyRoomEvent::RedactedState(redacted) => {
let room = view.rooms.entry(redacted.room_id().clone()).or_default();
room.messages.push(AnyRoomEvent::RedactedState(redacted));
}
}, },
matrix::Event::ToDevice(event) => match event { matrix::Event::ToDevice(event) => match event {
AnyToDeviceEvent::KeyVerificationStart(start) => { AnyToDeviceEvent::KeyVerificationStart(start) => {
let client = view.client.clone(); let client = view.client.clone();
return Command::perform( return Command::perform(
async move { async move {
//tokio::time::delay_for(std::time::Duration::from_secs(2)).await;
client.get_verification(&start.content.transaction_id).await client.get_verification(&start.content.transaction_id).await
}, },
Message::SetVerification, Message::SetVerification,
@ -891,7 +1033,8 @@ impl Application for Retrix {
.unwrap_or_else(|| view.sync_token.clone()), .unwrap_or_else(|| view.sync_token.clone()),
}; };
return async move { return async move {
let request = MessageRequest::backward(&id, &token); let mut request = MessageRequest::backward(&id, &token);
request.limit = matrix_sdk::uint!(30);
match client.room_messages(request).await { match client.room_messages(request).await {
Ok(response) => Message::BackFilled(id, response), Ok(response) => Message::BackFilled(id, response),
Err(e) => Message::ErrorMessage(e.to_string()), Err(e) => Message::ErrorMessage(e.to_string()),
@ -935,10 +1078,6 @@ impl Application for Retrix {
return async move { Message::ErrorMessage(e.to_string()) }.into() return async move { Message::ErrorMessage(e.to_string()) }.into()
} }
}; };
println!(
"Getting '{}' from '{}', with url '{}'",
&server, &path, &url
);
let client = view.client.clone(); let client = view.client.clone();
return async move { return async move {
let request = ImageRequest::new(&path, &*server); let request = ImageRequest::new(&path, &*server);
@ -956,6 +1095,11 @@ impl Application for Retrix {
Message::FetchedImage(url, handle) => { Message::FetchedImage(url, handle) => {
view.images.insert(url, handle); view.images.insert(url, handle);
} }
Message::RoomName(id, name) => {
if let Some(room) = view.rooms.get_mut(&id) {
room.name = name;
}
}
Message::SetVerification(v) => view.sas = v, Message::SetVerification(v) => view.sas = v,
Message::VerificationAccept => { Message::VerificationAccept => {
let sas = match &view.sas { let sas = match &view.sas {