Support sending and displaying message reactions (#2)

This commit is contained in:
Ulyssa 2023-02-09 17:53:33 -08:00
parent 3629f15e0d
commit c9c547acc1
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
10 changed files with 368 additions and 63 deletions

40
Cargo.lock generated
View file

@ -752,6 +752,15 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "emojis"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fe60b864b6544ad211d4053ced474a9b9d2c8d66b77f01d6c6bcfed10c6bf0"
dependencies = [
"phf 0.11.1",
]
[[package]]
name = "encoding_rs"
version = "0.8.31"
@ -1165,6 +1174,7 @@ dependencies = [
"clap",
"css-color-parser",
"dirs",
"emojis",
"gethostname",
"html5ever",
"lazy_static 1.4.0",
@ -1434,7 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf",
"phf 0.10.1",
"phf_codegen",
"string_cache",
"string_cache_codegen",
@ -1884,7 +1894,16 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_shared",
"phf_shared 0.10.0",
]
[[package]]
name = "phf"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
dependencies = [
"phf_shared 0.11.1",
]
[[package]]
@ -1894,7 +1913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator",
"phf_shared",
"phf_shared 0.10.0",
]
[[package]]
@ -1903,7 +1922,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
"phf_shared 0.10.0",
"rand 0.8.5",
]
@ -1916,6 +1935,15 @@ dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.0.12"
@ -2637,7 +2665,7 @@ dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot 0.12.1",
"phf_shared",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",
]
@ -2649,7 +2677,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [
"phf_generator",
"phf_shared",
"phf_shared 0.10.0",
"proc-macro2",
"quote",
]

View file

@ -19,6 +19,7 @@ chrono = "0.4"
clap = {version = "4.0", features = ["derive"]}
css-color-parser = "0.1.2"
dirs = "4.0.0"
emojis = "~0.5.2"
gethostname = "0.4.1"
html5ever = "0.26.0"
markup5ever_rcdom = "0.2.0"

View file

@ -10,14 +10,19 @@ use matrix_sdk::{
encryption::verification::SasVerification,
room::Joined,
ruma::{
events::room::message::{
OriginalRoomMessageEvent,
Relation,
Replacement,
RoomMessageEvent,
RoomMessageEventContent,
events::{
reaction::ReactionEvent,
room::message::{
OriginalRoomMessageEvent,
Relation,
Replacement,
RoomMessageEvent,
RoomMessageEventContent,
},
tag::{TagName, Tags},
AnyMessageLikeEvent,
MessageLikeEvent,
},
events::tag::{TagName, Tags},
EventId,
OwnedEventId,
OwnedRoomId,
@ -88,11 +93,20 @@ pub enum MessageAction {
/// Edit a sent message.
Edit,
/// Redact a message.
/// React to a message with an Emoji.
React(String),
/// Redact a message, with an optional reason.
Redact(Option<String>),
/// Reply to a message.
Reply,
/// Unreact to a message.
///
/// If no specific Emoji to remove to is specified, then all reactions from the user on the
/// message are removed.
Unreact(Option<String>),
}
bitflags::bitflags! {
@ -226,6 +240,12 @@ pub type AsyncProgramStore = Arc<AsyncMutex<ProgramStore>>;
pub type IambResult<T> = UIResult<T, IambInfo>;
/// Reaction events for some message.
///
/// The event identifier used as a key here is the ID for the reaction, and not for the message
/// it's reacting to.
pub type MessageReactions = HashMap<OwnedEventId, (String, OwnedUserId)>;
pub type Receipts = HashMap<OwnedEventId, Vec<OwnedUserId>>;
#[derive(thiserror::Error, Debug)]
@ -292,32 +312,103 @@ pub enum RoomFetchStatus {
NotStarted,
}
pub enum EventLocation {
Message(MessageKey),
Reaction(OwnedEventId),
}
impl EventLocation {
fn to_message_key(&self) -> Option<&MessageKey> {
if let EventLocation::Message(key) = self {
Some(key)
} else {
None
}
}
}
#[derive(Default)]
pub struct RoomInfo {
/// The display name for this room.
pub name: Option<String>,
/// The tags placed on this room.
pub tags: Option<Tags>,
pub keys: HashMap<OwnedEventId, MessageKey>,
/// A map of event IDs to where they are stored in this struct.
pub keys: HashMap<OwnedEventId, EventLocation>,
/// The messages loaded for this room.
pub messages: Messages,
/// A map of read markers to display on different events.
pub receipts: HashMap<OwnedEventId, Vec<OwnedUserId>>,
/// An event ID for where we should indicate we've read up to.
pub read_till: Option<OwnedEventId>,
/// A map of message identifiers to a map of reaction events.
pub reactions: HashMap<OwnedEventId, MessageReactions>,
/// Where to continue fetching from when we continue loading scrollback history.
pub fetch_id: RoomFetchStatus,
/// The time that we last fetched scrollback for this room.
pub fetch_last: Option<Instant>,
/// Users currently typing in this room, and when we received notification of them doing so.
pub users_typing: Option<(Instant, Vec<OwnedUserId>)>,
}
impl RoomInfo {
pub fn get_reactions(&self, event_id: &EventId) -> Vec<(&str, usize)> {
if let Some(reacts) = self.reactions.get(event_id) {
let mut counts = HashMap::new();
for (key, _) in reacts.values() {
let count = counts.entry(key.as_str()).or_default();
*count += 1;
}
let mut reactions = counts.into_iter().collect::<Vec<_>>();
reactions.sort();
reactions
} else {
vec![]
}
}
pub fn get_event(&self, event_id: &EventId) -> Option<&Message> {
self.messages.get(self.keys.get(event_id)?)
self.messages.get(self.keys.get(event_id)?.to_message_key()?)
}
pub fn insert_reaction(&mut self, react: ReactionEvent) {
match react {
MessageLikeEvent::Original(react) => {
let rel_id = react.content.relates_to.event_id;
let key = react.content.relates_to.key;
let message = self.reactions.entry(rel_id.clone()).or_default();
let event_id = react.event_id;
let user_id = react.sender;
message.insert(event_id.clone(), (key, user_id));
let loc = EventLocation::Reaction(rel_id);
self.keys.insert(event_id, loc);
},
MessageLikeEvent::Redacted(_) => {
return;
},
}
}
pub fn insert_edit(&mut self, msg: Replacement) {
let event_id = msg.event_id;
let new_content = msg.new_content;
let key = if let Some(k) = self.keys.get(&event_id) {
let key = if let Some(EventLocation::Message(k)) = self.keys.get(&event_id) {
k
} else {
return;
@ -346,7 +437,7 @@ impl RoomInfo {
let event_id = msg.event_id().to_owned();
let key = (msg.origin_server_ts().into(), event_id.clone());
self.keys.insert(event_id.clone(), key.clone());
self.keys.insert(event_id.clone(), EventLocation::Message(key.clone()));
self.messages.insert(key, msg.into());
// Remove any echo.
@ -519,7 +610,15 @@ impl ChatStore {
match res {
Ok((fetch_id, msgs)) => {
for msg in msgs.into_iter() {
info.insert(msg);
match msg {
AnyMessageLikeEvent::RoomMessage(msg) => {
info.insert(msg);
},
AnyMessageLikeEvent::Reaction(ev) => {
info.insert_reaction(ev);
},
_ => continue,
}
}
info.fetch_id =

View file

@ -163,8 +163,8 @@ fn iamb_cancel(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
return Result::Err(CommandError::InvalidArgument);
}
let ract = IambAction::from(MessageAction::Cancel);
let step = CommandStep::Continue(ract.into(), ctx.context.take());
let mact = IambAction::from(MessageAction::Cancel);
let step = CommandStep::Continue(mact.into(), ctx.context.take());
return Ok(step);
}
@ -174,8 +174,55 @@ fn iamb_edit(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
return Result::Err(CommandError::InvalidArgument);
}
let ract = IambAction::from(MessageAction::Edit);
let step = CommandStep::Continue(ract.into(), ctx.context.take());
let mact = IambAction::from(MessageAction::Edit);
let step = CommandStep::Continue(mact.into(), ctx.context.take());
return Ok(step);
}
fn iamb_react(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
let args = desc.arg.strings()?;
if args.len() != 1 {
return Result::Err(CommandError::InvalidArgument);
}
let k = args[0].as_str();
if let Some(emoji) = emojis::get(k).or_else(|| emojis::get_by_shortcode(k)) {
let mact = IambAction::from(MessageAction::React(emoji.to_string()));
let step = CommandStep::Continue(mact.into(), ctx.context.take());
return Ok(step);
} else {
let msg = format!("Invalid Emoji or shortcode: {k}");
return Result::Err(CommandError::Error(msg));
}
}
fn iamb_unreact(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
let mut args = desc.arg.strings()?;
if args.len() > 1 {
return Result::Err(CommandError::InvalidArgument);
}
let mact = if let Some(k) = args.pop() {
let k = k.as_str();
if let Some(emoji) = emojis::get(k).or_else(|| emojis::get_by_shortcode(k)) {
IambAction::from(MessageAction::Unreact(Some(emoji.to_string())))
} else {
let msg = format!("Invalid Emoji or shortcode: {k}");
return Result::Err(CommandError::Error(msg));
}
} else {
IambAction::from(MessageAction::Unreact(None))
};
let step = CommandStep::Continue(mact.into(), ctx.context.take());
return Ok(step);
}
@ -356,11 +403,13 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
cmds.add_command(ProgramCommand { names: vec!["invite".into()], f: iamb_invite });
cmds.add_command(ProgramCommand { names: vec!["join".into()], f: iamb_join });
cmds.add_command(ProgramCommand { names: vec!["members".into()], f: iamb_members });
cmds.add_command(ProgramCommand { names: vec!["react".into()], f: iamb_react });
cmds.add_command(ProgramCommand { names: vec!["redact".into()], f: iamb_redact });
cmds.add_command(ProgramCommand { names: vec!["reply".into()], f: iamb_reply });
cmds.add_command(ProgramCommand { names: vec!["rooms".into()], f: iamb_rooms });
cmds.add_command(ProgramCommand { names: vec!["room".into()], f: iamb_room });
cmds.add_command(ProgramCommand { names: vec!["spaces".into()], f: iamb_spaces });
cmds.add_command(ProgramCommand { names: vec!["unreact".into()], f: iamb_unreact });
cmds.add_command(ProgramCommand { names: vec!["upload".into()], f: iamb_upload });
cmds.add_command(ProgramCommand { names: vec!["verify".into()], f: iamb_verify });
cmds.add_command(ProgramCommand { names: vec!["welcome".into()], f: iamb_welcome });

View file

@ -178,6 +178,8 @@ fn merge_users(a: Option<UserOverrides>, b: Option<UserOverrides>) -> Option<Use
#[derive(Clone)]
pub struct TunableValues {
pub reaction_display: bool,
pub reaction_shortcode_display: bool,
pub read_receipt_send: bool,
pub read_receipt_display: bool,
pub typing_notice_send: bool,
@ -188,6 +190,8 @@ pub struct TunableValues {
#[derive(Clone, Default, Deserialize)]
pub struct Tunables {
pub reaction_display: Option<bool>,
pub reaction_shortcode_display: Option<bool>,
pub read_receipt_send: Option<bool>,
pub read_receipt_display: Option<bool>,
pub typing_notice_send: Option<bool>,
@ -199,6 +203,10 @@ pub struct Tunables {
impl Tunables {
fn merge(self, other: Self) -> Self {
Tunables {
reaction_display: self.reaction_display.or(other.reaction_display),
reaction_shortcode_display: self
.reaction_shortcode_display
.or(other.reaction_shortcode_display),
read_receipt_send: self.read_receipt_send.or(other.read_receipt_send),
read_receipt_display: self.read_receipt_display.or(other.read_receipt_display),
typing_notice_send: self.typing_notice_send.or(other.typing_notice_send),
@ -210,6 +218,8 @@ impl Tunables {
fn values(self) -> TunableValues {
TunableValues {
reaction_display: self.reaction_display.unwrap_or(true),
reaction_shortcode_display: self.reaction_shortcode_display.unwrap_or(false),
read_receipt_send: self.read_receipt_send.unwrap_or(true),
read_receipt_display: self.read_receipt_display.unwrap_or(true),
typing_notice_send: self.typing_notice_send.unwrap_or(true),

View file

@ -24,6 +24,7 @@ use matrix_sdk::ruma::{
},
redaction::SyncRoomRedactionEvent,
},
AnyMessageLikeEvent,
Redact,
},
EventId,
@ -52,7 +53,7 @@ use crate::{
mod html;
mod printer;
pub type MessageFetchResult = IambResult<(Option<String>, Vec<RoomMessageEvent>)>;
pub type MessageFetchResult = IambResult<(Option<String>, Vec<AnyMessageLikeEvent>)>;
pub type MessageKey = (MessageTimeStamp, OwnedEventId);
pub type Messages = BTreeMap<MessageKey, Message>;
@ -682,6 +683,43 @@ impl Message {
fmt.push_spans(space_span(width, style).into(), style, &mut text);
}
if settings.tunables.reaction_display {
let mut emojis = printer::TextPrinter::new(width, style, false);
let mut reactions = 0;
for (key, count) in info.get_reactions(self.event.event_id()).into_iter() {
if reactions != 0 {
emojis.push_str(" ", style);
}
let name = if settings.tunables.reaction_shortcode_display {
if let Some(emoji) = emojis::get(key) {
if let Some(short) = emoji.shortcode() {
short
} else {
// No ASCII shortcode name to show.
continue;
}
} else if key.chars().all(|c| c.is_ascii_alphanumeric()) {
key
} else {
// Not an Emoji or a printable ASCII string.
continue;
}
} else {
key
};
emojis.push_str(format!("[{name} {count}]"), style);
reactions += 1;
}
if reactions > 0 {
fmt.push_text(emojis.finish(), style, &mut text);
}
}
return text;
}

View file

@ -20,7 +20,7 @@ use tokio::sync::mpsc::unbounded_channel;
use url::Url;
use crate::{
base::{ChatStore, ProgramStore, RoomFetchStatus, RoomInfo},
base::{ChatStore, EventLocation, ProgramStore, RoomFetchStatus, RoomInfo},
config::{
user_color,
user_style_from_color,
@ -117,14 +117,14 @@ pub fn mock_message5() -> Message {
mock_room1_message(content, TEST_USER2.clone(), MSG4_KEY.clone())
}
pub fn mock_keys() -> HashMap<OwnedEventId, MessageKey> {
pub fn mock_keys() -> HashMap<OwnedEventId, EventLocation> {
let mut keys = HashMap::new();
keys.insert(MSG1_EVID.clone(), MSG1_KEY.clone());
keys.insert(MSG2_EVID.clone(), MSG2_KEY.clone());
keys.insert(MSG3_EVID.clone(), MSG3_KEY.clone());
keys.insert(MSG4_EVID.clone(), MSG4_KEY.clone());
keys.insert(MSG5_EVID.clone(), MSG5_KEY.clone());
keys.insert(MSG1_EVID.clone(), EventLocation::Message(MSG1_KEY.clone()));
keys.insert(MSG2_EVID.clone(), EventLocation::Message(MSG2_KEY.clone()));
keys.insert(MSG3_EVID.clone(), EventLocation::Message(MSG3_KEY.clone()));
keys.insert(MSG4_EVID.clone(), EventLocation::Message(MSG4_KEY.clone()));
keys.insert(MSG5_EVID.clone(), EventLocation::Message(MSG5_KEY.clone()));
keys
}
@ -151,6 +151,7 @@ pub fn mock_room() -> RoomInfo {
receipts: HashMap::new(),
read_till: None,
reactions: HashMap::new(),
fetch_id: RoomFetchStatus::NotStarted,
fetch_last: None,
@ -169,6 +170,8 @@ pub fn mock_dirs() -> DirectoryValues {
pub fn mock_tunables() -> TunableValues {
TunableValues {
default_room: None,
reaction_display: true,
reaction_shortcode_display: false,
read_receipt_send: true,
read_receipt_display: true,
typing_notice_send: true,

View file

@ -9,8 +9,9 @@ use tokio;
use matrix_sdk::{
attachment::AttachmentConfig,
media::{MediaFormat, MediaRequest},
room::Room as MatrixRoom,
room::{Joined, Room as MatrixRoom},
ruma::{
events::reaction::{ReactionEventContent, Relation as Reaction},
events::room::message::{
MessageType,
OriginalRoomMessageEvent,
@ -19,6 +20,7 @@ use matrix_sdk::{
RoomMessageEventContent,
TextMessageEventContent,
},
EventId,
OwnedRoomId,
RoomId,
},
@ -73,6 +75,7 @@ use crate::base::{
};
use crate::message::{Message, MessageEvent, MessageKey, MessageTimeStamp};
use crate::worker::Requester;
use super::scrollback::{Scrollback, ScrollbackState};
@ -115,6 +118,10 @@ impl ChatState {
}
}
fn get_joined(&self, worker: &Requester) -> Result<Joined, IambError> {
worker.client.get_joined_room(self.id()).ok_or(IambError::NotJoined)
}
fn get_reply_to<'a>(&self, info: &'a RoomInfo) -> Option<&'a OriginalRoomMessageEvent> {
let key = self.reply_to.as_ref()?;
let msg = info.messages.get(key)?;
@ -149,7 +156,10 @@ impl ChatState {
let settings = &store.application.settings;
let info = store.application.rooms.entry(self.room_id.clone()).or_default();
let msg = self.scrollback.get_mut(info).ok_or(IambError::NoSelectedMessage)?;
let msg = self
.scrollback
.get_mut(&mut info.messages)
.ok_or(IambError::NoSelectedMessage)?;
match act {
MessageAction::Cancel => {
@ -282,19 +292,32 @@ impl ChatState {
Ok(None)
},
MessageAction::Redact(reason) => {
let room = store
.application
.worker
.client
.get_joined_room(self.id())
.ok_or(IambError::NotJoined)?;
MessageAction::React(emoji) => {
let room = self.get_joined(&store.application.worker)?;
let event_id = match &msg.event {
MessageEvent::Original(ev) => ev.event_id.clone(),
MessageEvent::Local(event_id, _) => event_id.clone(),
MessageEvent::Redacted(_) => {
let msg = "";
let msg = "Cannot react to a redacted message";
let err = UIError::Failure(msg.into());
return Err(err);
},
};
let reaction = Reaction::new(event_id, emoji);
let msg = ReactionEventContent::new(reaction);
let _ = room.send(msg, None).await.map_err(IambError::from)?;
Ok(None)
},
MessageAction::Redact(reason) => {
let room = self.get_joined(&store.application.worker)?;
let event_id = match &msg.event {
MessageEvent::Original(ev) => ev.event_id.clone(),
MessageEvent::Local(event_id, _) => event_id.clone(),
MessageEvent::Redacted(_) => {
let msg = "Cannot redact already redacted message";
let err = UIError::Failure(msg.into());
return Err(err);
@ -311,6 +334,46 @@ impl ChatState {
self.reply_to = self.scrollback.get_key(info);
self.focus = RoomFocus::MessageBar;
Ok(None)
},
MessageAction::Unreact(emoji) => {
let room = self.get_joined(&store.application.worker)?;
let event_id: &EventId = match &msg.event {
MessageEvent::Original(ev) => ev.event_id.as_ref(),
MessageEvent::Local(event_id, _) => event_id.as_ref(),
MessageEvent::Redacted(_) => {
let msg = "Cannot unreact to a redacted message";
let err = UIError::Failure(msg.into());
return Err(err);
},
};
let reactions = match info.reactions.get(event_id) {
Some(r) => r,
None => return Ok(None),
};
let reactions = reactions.iter().filter_map(|(event_id, (reaction, user_id))| {
if user_id != &settings.profile.user_id {
return None;
}
if let Some(emoji) = &emoji {
if emoji == reaction {
return Some(event_id);
} else {
return None;
}
} else {
return Some(event_id);
}
});
for reaction in reactions {
let _ = room.redact(reaction, None, None).await.map_err(IambError::from)?;
}
Ok(None)
},
}

View file

@ -62,7 +62,7 @@ use modalkit::editing::{
use crate::{
base::{IambBufferId, IambInfo, ProgramContext, ProgramStore, RoomFocus, RoomInfo},
config::ApplicationSettings,
message::{Message, MessageCursor, MessageKey},
message::{Message, MessageCursor, MessageKey, Messages},
};
fn nth_key_before(pos: MessageKey, n: usize, info: &RoomInfo) -> MessageKey {
@ -164,11 +164,11 @@ impl ScrollbackState {
.or_else(|| info.messages.last_key_value().map(|kv| kv.0.clone()))
}
pub fn get_mut<'a>(&mut self, info: &'a mut RoomInfo) -> Option<&'a mut Message> {
pub fn get_mut<'a>(&mut self, messages: &'a mut Messages) -> Option<&'a mut Message> {
if let Some(k) = &self.cursor.timestamp {
info.messages.get_mut(k)
messages.get_mut(k)
} else {
info.messages.last_entry().map(|o| o.into_mut())
messages.last_entry().map(|o| o.into_mut())
}
}

View file

@ -32,6 +32,7 @@ use matrix_sdk::{
start::{OriginalSyncKeyVerificationStartEvent, ToDeviceKeyVerificationStartEvent},
VerificationMethod,
},
reaction::ReactionEventContent,
room::{
message::{MessageType, RoomMessageEventContent},
name::RoomNameEventContent,
@ -39,7 +40,6 @@ use matrix_sdk::{
},
tag::Tags,
typing::SyncTypingEvent,
AnyMessageLikeEvent,
AnyTimelineEvent,
SyncMessageLikeEvent,
SyncStateEvent,
@ -57,7 +57,7 @@ use matrix_sdk::{
use modalkit::editing::action::{EditInfo, InfoMessage, UIError};
use crate::{
base::{AsyncProgramStore, IambError, IambResult, Receipts, VerifyAction},
base::{AsyncProgramStore, EventLocation, IambError, IambResult, Receipts, VerifyAction},
message::MessageFetchResult,
ApplicationSettings,
};
@ -552,6 +552,20 @@ impl ClientWorker {
},
);
let _ = self.client.add_event_handler(
|ev: SyncMessageLikeEvent<ReactionEventContent>,
room: MatrixRoom,
store: Ctx<AsyncProgramStore>| {
async move {
let room_id = room.room_id();
let mut locked = store.lock().await;
let info = locked.application.get_room_info(room_id.to_owned());
info.insert_reaction(ev.into_full_event(room_id.to_owned()));
}
},
);
let _ = self.client.add_event_handler(
|ev: OriginalSyncRoomRedactionEvent,
room: MatrixRoom,
@ -564,15 +578,21 @@ impl ClientWorker {
let mut locked = store.lock().await;
let info = locked.application.get_room_info(room_id.to_owned());
let key = if let Some(k) = info.keys.get(&ev.redacts) {
k
} else {
return;
};
match info.keys.get(&ev.redacts) {
None => return,
Some(EventLocation::Message(key)) => {
if let Some(msg) = info.messages.get_mut(key) {
let ev = SyncRoomRedactionEvent::Original(ev);
msg.event.redact(ev, room_version);
}
},
Some(EventLocation::Reaction(event_id)) => {
if let Some(reactions) = info.reactions.get_mut(event_id) {
reactions.remove(&ev.redacts);
}
if let Some(msg) = info.messages.get_mut(key) {
let ev = SyncRoomRedactionEvent::Original(ev);
msg.event.redact(ev, room_version);
info.keys.remove(&ev.redacts);
},
}
}
},
@ -891,13 +911,7 @@ impl ClientWorker {
let msgs = chunk.into_iter().filter_map(|ev| {
match ev.event.deserialize() {
Ok(AnyTimelineEvent::MessageLike(msg)) => {
if let AnyMessageLikeEvent::RoomMessage(msg) = msg {
Some(msg)
} else {
None
}
},
Ok(AnyTimelineEvent::MessageLike(msg)) => Some(msg),
Ok(AnyTimelineEvent::State(_)) => None,
Err(_) => None,
}