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.
This commit is contained in:
parent
f25d3b6821
commit
a8b76eaded
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
272
src/ui.rs
272
src/ui.rs
|
@ -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,14 +586,17 @@ 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
|
||||||
|
.push(
|
||||||
Column::new()
|
Column::new()
|
||||||
.align_items(iced::Align::Center)
|
.align_items(iced::Align::Center)
|
||||||
.push(Text::new(*emoji).size(32))
|
.push(Text::new(*emoji).size(32))
|
||||||
.push(Text::new(*name)),
|
.push(Text::new(*name)),
|
||||||
);
|
)
|
||||||
|
.spacing(5);
|
||||||
}
|
}
|
||||||
row.push(
|
row.push(iced::Space::with_width(Length::Fill))
|
||||||
|
.push(
|
||||||
Button::new(&mut self.sas_accept_button, Text::new("Confirm"))
|
Button::new(&mut self.sas_accept_button, Text::new("Confirm"))
|
||||||
.on_press(Message::VerificationConfirm),
|
.on_press(Message::VerificationConfirm),
|
||||||
)
|
)
|
||||||
|
@ -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,39 +921,58 @@ 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();
|
||||||
|
let entry = RoomEntry::from_sdk(&joined).await;
|
||||||
|
|
||||||
|
Message::ResetRoom(id, entry)
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
MembershipState::Leave => {
|
||||||
// Deselect room if we're leaving selected room
|
// Deselect room if we're leaving selected room
|
||||||
if view.selected.as_ref() == Some(&member.room_id) {
|
if view.selected.as_ref() == Some(&member.room_id) {
|
||||||
view.selected = None;
|
view.selected = None;
|
||||||
|
@ -847,6 +980,9 @@ impl Application for Retrix {
|
||||||
view.rooms.remove(&member.room_id);
|
view.rooms.remove(&member.room_id);
|
||||||
return Command::none();
|
return Command::none();
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
room.messages.push(AnyRoomEvent::State(event));
|
room.messages.push(AnyRoomEvent::State(event));
|
||||||
}
|
}
|
||||||
ref any => {
|
ref any => {
|
||||||
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue