daydream

A small matrix web client written in rust
git clone git://archive.git.mtrnord.blog/daydream-mx/daydream.git
Log | Files | Refs | README | LICENSE

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 }