event_list.rs (7813B)
1 use std::{collections::HashMap, rc::Rc}; 2 3 use crate::utils::ruma::AnyMessageEventExt; 4 use log::*; 5 use matrix_sdk::{ 6 events::{room::message::MessageEventContent, AnyMessageEventContent, AnySyncMessageEvent}, 7 identifiers::RoomId, 8 Room, 9 }; 10 use yew::{prelude::*, virtual_dom::VList}; 11 12 use crate::app::components::{ 13 events::{image::Image, notice::Notice, text::Text, video::Video}, 14 input::Input, 15 }; 16 use crate::app::matrix::{MatrixAgent, Request, Response}; 17 18 pub struct EventList { 19 on_submit: Callback<String>, 20 state: State, 21 matrix_agent: Box<dyn Bridge<MatrixAgent>>, 22 props: Props, 23 } 24 25 #[derive(Default)] 26 pub struct State { 27 // TODO handle all events 28 pub events: HashMap<RoomId, Vec<AnySyncMessageEvent>>, 29 } 30 31 #[allow(clippy::large_enum_variant)] 32 pub enum Msg { 33 NewMessage(Response), 34 SendMessage(String), 35 Nope, 36 } 37 38 #[derive(Clone, PartialEq, Properties, Debug)] 39 pub struct Props { 40 pub current_room: Rc<Room>, 41 } 42 43 impl Component for EventList { 44 type Message = Msg; 45 type Properties = Props; 46 47 fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self { 48 let matrix_callback = link.callback(Msg::NewMessage); 49 let mut matrix_agent = MatrixAgent::bridge(matrix_callback); 50 51 let state = State { 52 events: Default::default(), 53 }; 54 55 let room_id = props.current_room.room_id.clone(); 56 if !state.events.contains_key(&room_id) { 57 matrix_agent.send(Request::GetOldMessages((room_id, None))); 58 } 59 60 EventList { 61 on_submit: link.callback(Msg::SendMessage), 62 props, 63 matrix_agent, 64 state, 65 } 66 } 67 68 fn update(&mut self, msg: Self::Message) -> bool { 69 match msg { 70 Msg::NewMessage(Response::Sync((room_id, raw_msg))) => { 71 // TODO handle all events 72 if let Ok(msg) = raw_msg.deserialize() { 73 if self.state.events.contains_key(&room_id) { 74 if !(self.state.events[&room_id] 75 .iter() 76 .any(|x| x.event_id() == msg.event_id())) 77 { 78 self.state.events.get_mut(&room_id).unwrap().push(msg); 79 room_id == self.props.current_room.room_id 80 } else { 81 false 82 } 83 } else { 84 let msgs = vec![msg]; 85 self.state.events.insert(room_id.clone(), msgs); 86 room_id == self.props.current_room.room_id 87 } 88 } else { 89 false 90 } 91 } 92 Msg::NewMessage(Response::OldMessages((room_id, messages))) => { 93 let mut deserialized_messages: Vec<AnySyncMessageEvent> = messages 94 .iter() 95 .map(|x| x.deserialize()) 96 .filter_map(Result::ok) 97 .map(|x| x.without_room_id()) 98 .collect(); 99 // This is a clippy false positive 100 #[allow(clippy::map_entry)] 101 if self.state.events.contains_key(&room_id) { 102 self.state 103 .events 104 .get_mut(&room_id) 105 .unwrap() 106 .append(deserialized_messages.as_mut()); 107 true 108 } else { 109 self.state.events.insert(room_id, deserialized_messages); 110 true 111 } 112 } 113 Msg::NewMessage(_) => false, 114 Msg::SendMessage(message) => { 115 info!("Sending Message"); 116 self.matrix_agent.send(Request::SendMessage(( 117 self.props.current_room.room_id.clone(), 118 message, 119 ))); 120 false 121 } 122 Msg::Nope => false, 123 } 124 } 125 126 fn change(&mut self, props: Self::Properties) -> bool { 127 if self.props != props { 128 let room_id = props.current_room.room_id.clone(); 129 if !self.state.events.contains_key(&room_id) { 130 self.matrix_agent 131 .send(Request::GetOldMessages((room_id, None))); 132 } 133 134 self.props = props; 135 true 136 } else { 137 false 138 } 139 } 140 141 fn view(&self) -> Html { 142 let events = if self 143 .state 144 .events 145 .contains_key(&self.props.current_room.room_id) 146 { 147 let events = &self.state.events[&self.props.current_room.room_id]; 148 149 let mut html_nodes = VList::new(); 150 if let Some(event) = events.first() { 151 html_nodes.add_child(self.get_event(None, event)); 152 } 153 html_nodes.add_children( 154 events 155 .windows(2) 156 .map(|e| self.get_event(Some(&e[0]), &e[1])), 157 ); 158 159 html_nodes.into() 160 } else { 161 html! {} 162 }; 163 164 html! { 165 <div class="event-list"> 166 <div class="room-title"><div><h1>{ self.props.current_room.display_name() }</h1></div></div> 167 <div class="scrollable message-scrollarea"> 168 <div class="message-container"> 169 { events } 170 <div id="anchor"></div> 171 </div> 172 </div> 173 <Input on_submit=&self.on_submit/> 174 </div> 175 } 176 } 177 } 178 179 impl EventList { 180 // Typeinspection of IDEA breaks with this :D 181 //noinspection RsTypeCheck 182 fn get_event( 183 &self, 184 prev_event: Option<&AnySyncMessageEvent>, 185 event: &AnySyncMessageEvent, 186 ) -> Html { 187 // TODO make encryption supported 188 189 match &event.content() { 190 AnyMessageEventContent::RoomMessage(room_message) => match room_message { 191 MessageEventContent::Text(text_event) => { 192 html! { 193 <Text 194 prev_event=prev_event.cloned() 195 event=event.clone() 196 room=self.props.current_room.clone() 197 text_event=text_event.clone() 198 /> 199 } 200 } 201 MessageEventContent::Notice(notice_event) => { 202 html! { 203 <Notice 204 prev_event=prev_event.cloned() 205 event=event.clone() 206 room=self.props.current_room.clone() 207 notice_event=notice_event.clone() 208 /> 209 } 210 } 211 MessageEventContent::Image(image_event) => { 212 html! { 213 <Image 214 prev_event=prev_event.cloned() 215 event=event.clone() 216 room=self.props.current_room.clone() 217 image_event=image_event.clone() 218 /> 219 } 220 } 221 MessageEventContent::Video(video_event) => { 222 html! { 223 <Video 224 prev_event=prev_event.cloned() 225 event=event.clone() 226 room=self.props.current_room.clone() 227 video_event=video_event.clone() 228 /> 229 } 230 } 231 _ => html! {}, 232 }, 233 _ => html! {}, 234 } 235 } 236 }