Restructure ui into view structs
This commit is contained in:
		
							parent
							
								
									41b8756ac2
								
							
						
					
					
						commit
						774198933c
					
				
					 1 changed files with 289 additions and 303 deletions
				
			
		
							
								
								
									
										592
									
								
								src/ui.rs
									
										
									
									
									
								
							
							
						
						
									
										592
									
								
								src/ui.rs
									
										
									
									
									
								
							|  | @ -15,52 +15,258 @@ use matrix_sdk::{ | ||||||
| 
 | 
 | ||||||
| use crate::matrix; | use crate::matrix; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone)] | /// View for the login prompt
 | ||||||
| pub enum Retrix { | #[derive(Debug, Clone, Default)] | ||||||
|     Prompt { | pub struct PromptView { | ||||||
|         user_input: text_input::State, |     user_input: text_input::State, | ||||||
|         password_input: text_input::State, |     password_input: text_input::State, | ||||||
|         server_input: text_input::State, |     server_input: text_input::State, | ||||||
|         login_button: iced::button::State, |     login_button: iced::button::State, | ||||||
| 
 | 
 | ||||||
|         user: String, |     user: String, | ||||||
|         password: String, |     password: String, | ||||||
|         server: String, |     server: String, | ||||||
|         action: PromptAction, |     action: PromptAction, | ||||||
|         error: Option<String>, |     error: Option<String>, | ||||||
|     }, |  | ||||||
|     AwaitLogin(std::time::Instant), |  | ||||||
|     LoggedIn { |  | ||||||
|         client: matrix_sdk::Client, |  | ||||||
|         session: matrix::Session, |  | ||||||
| 
 |  | ||||||
|         rooms: BTreeMap<RoomId, String>, |  | ||||||
|         buttons: HashMap<RoomId, iced::button::State>, |  | ||||||
|         messages: BTreeMap<RoomId, MessageEventContent>, |  | ||||||
|         selected: Option<RoomId>, |  | ||||||
|         room_scroll: iced::scrollable::State, |  | ||||||
|         message_scroll: iced::scrollable::State, |  | ||||||
|         message_input: iced::text_input::State, |  | ||||||
|         draft: String, |  | ||||||
|         send_button: iced::button::State, |  | ||||||
|     }, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Retrix { | impl PromptView { | ||||||
|     pub fn new_prompt() -> Retrix { |     pub fn new() -> Self { | ||||||
|         Retrix::Prompt { |         Self::default() | ||||||
|             user_input: text_input::State::new(), |     } | ||||||
|             password_input: text_input::State::new(), |  | ||||||
|             server_input: text_input::State::new(), |  | ||||||
|             login_button: Default::default(), |  | ||||||
| 
 | 
 | ||||||
|             user: String::new(), |     pub fn view(&mut self) -> Element<Message> { | ||||||
|             password: String::new(), |         let mut content = Column::new() | ||||||
|             server: String::new(), |             .width(500.into()) | ||||||
|             action: PromptAction::Login, |             .push( | ||||||
|             error: None, |                 Row::new() | ||||||
|  |                     .spacing(15) | ||||||
|  |                     .push(Radio::new( | ||||||
|  |                         PromptAction::Login, | ||||||
|  |                         "Login", | ||||||
|  |                         Some(self.action), | ||||||
|  |                         Message::SetAction, | ||||||
|  |                     )) | ||||||
|  |                     .push(Radio::new( | ||||||
|  |                         PromptAction::Signup, | ||||||
|  |                         "Sign up", | ||||||
|  |                         Some(self.action), | ||||||
|  |                         Message::SetAction, | ||||||
|  |                     )), | ||||||
|  |             ) | ||||||
|  |             .push(Text::new("Username")) | ||||||
|  |             .push( | ||||||
|  |                 TextInput::new( | ||||||
|  |                     &mut self.user_input, | ||||||
|  |                     "Username", | ||||||
|  |                     &self.user, | ||||||
|  |                     Message::SetUser, | ||||||
|  |                 ) | ||||||
|  |                 .padding(5), | ||||||
|  |             ) | ||||||
|  |             .push(Text::new("Password")) | ||||||
|  |             .push( | ||||||
|  |                 TextInput::new( | ||||||
|  |                     &mut self.password_input, | ||||||
|  |                     "Password", | ||||||
|  |                     &self.password, | ||||||
|  |                     Message::SetPassword, | ||||||
|  |                 ) | ||||||
|  |                 .password() | ||||||
|  |                 .padding(5), | ||||||
|  |             ) | ||||||
|  |             .push(Text::new("Homeserver")) | ||||||
|  |             .push( | ||||||
|  |                 TextInput::new( | ||||||
|  |                     &mut self.server_input, | ||||||
|  |                     "Server", | ||||||
|  |                     &self.server, | ||||||
|  |                     Message::SetServer, | ||||||
|  |                 ) | ||||||
|  |                 .padding(5), | ||||||
|  |             ); | ||||||
|  |         let button = match self.action { | ||||||
|  |             PromptAction::Login => { | ||||||
|  |                 Button::new(&mut self.login_button, Text::new("Login")).on_press(Message::Login) | ||||||
|  |             } | ||||||
|  |             PromptAction::Signup => { | ||||||
|  |                 content = content.push( | ||||||
|  |                     Text::new("NB: Signup is very naively implemented, and prone to breaking") | ||||||
|  |                         .color([0.2, 0.2, 0.0]), | ||||||
|  |                 ); | ||||||
|  |                 Button::new(&mut self.login_button, Text::new("Sign up")).on_press(Message::Signup) | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         content = content.push(button); | ||||||
|  |         if let Some(ref error) = self.error { | ||||||
|  |             content = content.push(Text::new(error).color([1.0, 0.0, 0.0])); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Container::new(content) | ||||||
|  |             .center_x() | ||||||
|  |             .center_y() | ||||||
|  |             .width(iced::Length::Fill) | ||||||
|  |             .height(iced::Length::Fill) | ||||||
|  |             .into() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Main view after successful login
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct MainView { | ||||||
|  |     client: matrix_sdk::Client, | ||||||
|  |     session: matrix::Session, | ||||||
|  | 
 | ||||||
|  |     rooms: BTreeMap<RoomId, String>, | ||||||
|  |     buttons: HashMap<RoomId, iced::button::State>, | ||||||
|  |     messages: BTreeMap<RoomId, MessageEventContent>, | ||||||
|  |     selected: Option<RoomId>, | ||||||
|  |     room_scroll: iced::scrollable::State, | ||||||
|  |     message_scroll: iced::scrollable::State, | ||||||
|  |     message_input: iced::text_input::State, | ||||||
|  |     draft: String, | ||||||
|  |     send_button: iced::button::State, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl MainView { | ||||||
|  |     pub fn new(client: matrix_sdk::Client, session: matrix::Session) -> Self { | ||||||
|  |         Self { | ||||||
|  |             client, | ||||||
|  |             session, | ||||||
|  |             rooms: Default::default(), | ||||||
|  |             selected: None, | ||||||
|  |             room_scroll: Default::default(), | ||||||
|  |             message_scroll: Default::default(), | ||||||
|  |             message_input: Default::default(), | ||||||
|  |             buttons: Default::default(), | ||||||
|  |             messages: Default::default(), | ||||||
|  |             draft: String::new(), | ||||||
|  |             send_button: Default::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn view(&mut self) -> Element<Message> { | ||||||
|  |         let mut root_row = Row::new().width(Length::Fill).height(Length::Fill); | ||||||
|  | 
 | ||||||
|  |         // Room list
 | ||||||
|  |         let joined = self.client.joined_rooms(); | ||||||
|  |         let rooms = futures::executor::block_on(async { joined.read().await }); | ||||||
|  |         let mut room_col = Scrollable::new(&mut self.room_scroll) | ||||||
|  |             .width(400.into()) | ||||||
|  |             .height(Length::Fill) | ||||||
|  |             .scrollbar_width(5); | ||||||
|  |         // We have to iterate the buttons map and not the other way around to make the
 | ||||||
|  |         // borrow checker happy. First we make sure there's a button entry for every room
 | ||||||
|  |         // entry, and clean up button entries from removed rooms.
 | ||||||
|  |         for (id, _) in rooms.iter() { | ||||||
|  |             self.buttons.entry(id.to_owned()).or_default(); | ||||||
|  |         } | ||||||
|  |         self.buttons.retain(|id, _| rooms.contains_key(id)); | ||||||
|  |         // Then we make our buttons
 | ||||||
|  |         let buttons: Vec<Button<_>> = self | ||||||
|  |             .buttons | ||||||
|  |             .iter_mut() | ||||||
|  |             .map(|(id, state)| { | ||||||
|  |                 // Get read lock for the room
 | ||||||
|  |                 let room = | ||||||
|  |                     futures::executor::block_on(async { rooms.get(id).unwrap().read().await }); | ||||||
|  |                 Button::new(state, Text::new(room.display_name())) | ||||||
|  |                     .on_press(Message::SelectRoom(id.to_owned())) | ||||||
|  |                     .width(400.into()) | ||||||
|  |             }) | ||||||
|  |             .collect(); | ||||||
|  |         // Then we add them to our room column. What a mess.
 | ||||||
|  |         for button in buttons { | ||||||
|  |             room_col = room_col.push(button); | ||||||
|  |         } | ||||||
|  |         root_row = root_row.push(room_col); | ||||||
|  | 
 | ||||||
|  |         // Messages.
 | ||||||
|  |         //
 | ||||||
|  |         // Get selected room.
 | ||||||
|  |         let selected_room = self.selected.as_ref().and_then(|selected| { | ||||||
|  |             futures::executor::block_on(async { | ||||||
|  |                 match rooms.get(selected) { | ||||||
|  |                     Some(room) => Some(room.read().await), | ||||||
|  |                     None => None, | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         }); | ||||||
|  |         if let Some(room) = selected_room { | ||||||
|  |             let mut col = Column::new() | ||||||
|  |                 .spacing(5) | ||||||
|  |                 .padding(5) | ||||||
|  |                 .push(Text::new(room.display_name()).size(25)) | ||||||
|  |                 .push(Rule::horizontal(2)); | ||||||
|  |             let mut scroll = Scrollable::new(&mut self.message_scroll) | ||||||
|  |                 .scrollbar_width(2) | ||||||
|  |                 .height(Length::Fill); | ||||||
|  |             for message in room.messages.iter() { | ||||||
|  |                 if let AnyPossiblyRedactedSyncMessageEvent::Regular(event) = message { | ||||||
|  |                     if let AnySyncMessageEvent::RoomMessage(room_message) = event { | ||||||
|  |                         match &room_message.content { | ||||||
|  |                             MessageEventContent::Text(text) => { | ||||||
|  |                                 let row = Row::new() | ||||||
|  |                                     .spacing(5) | ||||||
|  |                                     .push( | ||||||
|  |                                         // Render senders disambiguated name or fallback to
 | ||||||
|  |                                         // mxid
 | ||||||
|  |                                         Text::new( | ||||||
|  |                                             room.joined_members | ||||||
|  |                                                 .get(&room_message.sender) | ||||||
|  |                                                 .map(|sender| sender.disambiguated_name()) | ||||||
|  |                                                 .unwrap_or(room_message.sender.to_string()), | ||||||
|  |                                         ) | ||||||
|  |                                         .color([0.2, 0.2, 1.0]), | ||||||
|  |                                     ) | ||||||
|  |                                     .push(Text::new(&text.body).width(Length::Fill)) | ||||||
|  |                                     .push(Text::new(format_systime(room_message.origin_server_ts))); | ||||||
|  |                                 scroll = scroll.push(row); | ||||||
|  |                             } | ||||||
|  |                             _ => (), | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             col = col.push(scroll).push( | ||||||
|  |                 Row::new() | ||||||
|  |                     .push( | ||||||
|  |                         TextInput::new( | ||||||
|  |                             &mut self.message_input, | ||||||
|  |                             "Write a message...", | ||||||
|  |                             &self.draft, | ||||||
|  |                             Message::SetMessage, | ||||||
|  |                         ) | ||||||
|  |                         .width(Length::Fill) | ||||||
|  |                         .padding(5) | ||||||
|  |                         .on_submit(Message::SendMessage), | ||||||
|  |                     ) | ||||||
|  |                     .push( | ||||||
|  |                         Button::new(&mut self.send_button, Text::new("Send")) | ||||||
|  |                             .on_press(Message::SendMessage), | ||||||
|  |                     ), | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |             root_row = root_row.push(col); | ||||||
|  |         } else { | ||||||
|  |             root_row = root_row.push( | ||||||
|  |                 Container::new(Text::new("Select a room to start chatting")) | ||||||
|  |                     .center_x() | ||||||
|  |                     .center_y() | ||||||
|  |                     .width(Length::Fill) | ||||||
|  |                     .height(Length::Fill), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         root_row.into() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum Retrix { | ||||||
|  |     Prompt(PromptView), | ||||||
|  |     AwaitLogin, | ||||||
|  |     LoggedIn(MainView), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||||
|  | @ -69,6 +275,12 @@ pub enum PromptAction { | ||||||
|     Signup, |     Signup, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl Default for PromptAction { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         PromptAction::Login | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub enum Message { | pub enum Message { | ||||||
|     // Login form messages
 |     // Login form messages
 | ||||||
|  | @ -106,9 +318,9 @@ impl Application for Retrix { | ||||||
|                         Err(e) => Message::LoginFailed(e.to_string()), |                         Err(e) => Message::LoginFailed(e.to_string()), | ||||||
|                     }, |                     }, | ||||||
|                 ); |                 ); | ||||||
|                 (Retrix::AwaitLogin(std::time::Instant::now()), command) |                 (Retrix::AwaitLogin, command) | ||||||
|             } |             } | ||||||
|             None => (Retrix::new_prompt(), Command::none()), |             None => (Retrix::Prompt(PromptView::new()), Command::none()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -118,8 +330,8 @@ impl Application for Retrix { | ||||||
| 
 | 
 | ||||||
|     fn subscription(&self) -> Subscription<Self::Message> { |     fn subscription(&self) -> Subscription<Self::Message> { | ||||||
|         match self { |         match self { | ||||||
|             Retrix::LoggedIn { client, .. } => { |             Retrix::LoggedIn(view) => { | ||||||
|                 matrix::MatrixSync::subscription(client.clone()).map(Message::Sync) |                 matrix::MatrixSync::subscription(view.client.clone()).map(Message::Sync) | ||||||
|             } |             } | ||||||
|             _ => Subscription::none(), |             _ => Subscription::none(), | ||||||
|         } |         } | ||||||
|  | @ -127,22 +339,16 @@ impl Application for Retrix { | ||||||
| 
 | 
 | ||||||
|     fn update(&mut self, message: Self::Message) -> Command<Self::Message> { |     fn update(&mut self, message: Self::Message) -> Command<Self::Message> { | ||||||
|         match self { |         match self { | ||||||
|             Retrix::Prompt { |             Retrix::Prompt(prompt) => match message { | ||||||
|                 user, |                 Message::SetUser(u) => prompt.user = u, | ||||||
|                 password, |                 Message::SetPassword(p) => prompt.password = p, | ||||||
|                 server, |                 Message::SetServer(s) => prompt.server = s, | ||||||
|                 action, |                 Message::SetAction(a) => prompt.action = a, | ||||||
|                 .. |  | ||||||
|             } => match message { |  | ||||||
|                 Message::SetUser(u) => *user = u, |  | ||||||
|                 Message::SetPassword(p) => *password = p, |  | ||||||
|                 Message::SetServer(s) => *server = s, |  | ||||||
|                 Message::SetAction(a) => *action = a, |  | ||||||
|                 Message::Login => { |                 Message::Login => { | ||||||
|                     let user = user.clone(); |                     let user = prompt.user.clone(); | ||||||
|                     let password = password.clone(); |                     let password = prompt.password.clone(); | ||||||
|                     let server = server.clone(); |                     let server = prompt.server.clone(); | ||||||
|                     *self = Retrix::AwaitLogin(std::time::Instant::now()); |                     *self = Retrix::AwaitLogin; | ||||||
|                     return Command::perform( |                     return Command::perform( | ||||||
|                         async move { matrix::login(&user, &password, &server).await }, |                         async move { matrix::login(&user, &password, &server).await }, | ||||||
|                         |result| match result { |                         |result| match result { | ||||||
|  | @ -152,10 +358,10 @@ impl Application for Retrix { | ||||||
|                     ); |                     ); | ||||||
|                 } |                 } | ||||||
|                 Message::Signup => { |                 Message::Signup => { | ||||||
|                     let user = user.clone(); |                     let user = prompt.user.clone(); | ||||||
|                     let password = password.clone(); |                     let password = prompt.password.clone(); | ||||||
|                     let server = server.clone(); |                     let server = prompt.server.clone(); | ||||||
|                     *self = Retrix::AwaitLogin(std::time::Instant::now()); |                     *self = Retrix::AwaitLogin; | ||||||
|                     return Command::perform( |                     return Command::perform( | ||||||
|                         async move { matrix::signup(&user, &password, &server).await }, |                         async move { matrix::signup(&user, &password, &server).await }, | ||||||
|                         |result| match result { |                         |result| match result { | ||||||
|  | @ -166,27 +372,14 @@ impl Application for Retrix { | ||||||
|                 } |                 } | ||||||
|                 _ => (), |                 _ => (), | ||||||
|             }, |             }, | ||||||
|             Retrix::AwaitLogin(_) => match message { |             Retrix::AwaitLogin => match message { | ||||||
|                 Message::LoginFailed(e) => { |                 Message::LoginFailed(e) => { | ||||||
|                     *self = Retrix::new_prompt(); |                     let mut view = PromptView::default(); | ||||||
|                     if let Retrix::Prompt { ref mut error, .. } = *self { |                     view.error = Some(e); | ||||||
|                         *error = Some(e); |                     *self = Retrix::Prompt(view); | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|                 Message::LoggedIn(client, session) => { |                 Message::LoggedIn(client, session) => { | ||||||
|                     *self = Retrix::LoggedIn { |                     *self = Retrix::LoggedIn(MainView::new(client, session)); | ||||||
|                         client: client.clone(), |  | ||||||
|                         session, |  | ||||||
|                         rooms: BTreeMap::new(), |  | ||||||
|                         selected: None, |  | ||||||
|                         room_scroll: Default::default(), |  | ||||||
|                         message_scroll: Default::default(), |  | ||||||
|                         message_input: Default::default(), |  | ||||||
|                         buttons: Default::default(), |  | ||||||
|                         messages: Default::default(), |  | ||||||
|                         draft: String::new(), |  | ||||||
|                         send_button: Default::default(), |  | ||||||
|                     }; |  | ||||||
|                     /*let client = client.clone();
 |                     /*let client = client.clone();
 | ||||||
|                     return Command::perform( |                     return Command::perform( | ||||||
|                         async move { |                         async move { | ||||||
|  | @ -202,26 +395,20 @@ impl Application for Retrix { | ||||||
|                 } |                 } | ||||||
|                 _ => (), |                 _ => (), | ||||||
|             }, |             }, | ||||||
|             Retrix::LoggedIn { |             Retrix::LoggedIn(view) => match message { | ||||||
|                 rooms, |                 Message::ResetRooms(r) => view.rooms = r, | ||||||
|                 selected, |                 Message::SelectRoom(r) => view.selected = Some(r), | ||||||
|                 draft, |  | ||||||
|                 client, |  | ||||||
|                 .. |  | ||||||
|             } => match message { |  | ||||||
|                 Message::ResetRooms(r) => *rooms = r, |  | ||||||
|                 Message::SelectRoom(r) => *selected = Some(r), |  | ||||||
|                 Message::Sync(event) => match event { |                 Message::Sync(event) => match event { | ||||||
|                     AnyRoomEvent::Message(_message) => (), |                     AnyRoomEvent::Message(_message) => (), | ||||||
|                     AnyRoomEvent::State(_state) => (), |                     AnyRoomEvent::State(_state) => (), | ||||||
|                     AnyRoomEvent::RedactedMessage(_message) => (), |                     AnyRoomEvent::RedactedMessage(_message) => (), | ||||||
|                     AnyRoomEvent::RedactedState(_state) => (), |                     AnyRoomEvent::RedactedState(_state) => (), | ||||||
|                 }, |                 }, | ||||||
|                 Message::SetMessage(m) => *draft = m, |                 Message::SetMessage(m) => view.draft = m, | ||||||
|                 Message::SendMessage => { |                 Message::SendMessage => { | ||||||
|                     let selected = selected.to_owned(); |                     let selected = view.selected.to_owned(); | ||||||
|                     let draft = draft.clone(); |                     let draft = view.draft.clone(); | ||||||
|                     let client = client.clone(); |                     let client = view.client.clone(); | ||||||
|                     return Command::perform( |                     return Command::perform( | ||||||
|                         async move { |                         async move { | ||||||
|                             client |                             client | ||||||
|  | @ -248,215 +435,14 @@ impl Application for Retrix { | ||||||
| 
 | 
 | ||||||
|     fn view(&mut self) -> Element<Self::Message> { |     fn view(&mut self) -> Element<Self::Message> { | ||||||
|         match self { |         match self { | ||||||
|             Retrix::Prompt { |             Retrix::Prompt(prompt) => prompt.view(), | ||||||
|                 user_input, |             Retrix::AwaitLogin => Container::new(Text::new(format!("Logging in..."))) | ||||||
|                 password_input, |                 .center_x() | ||||||
|                 server_input, |                 .center_y() | ||||||
|                 login_button, |                 .width(Length::Fill) | ||||||
|                 user, |                 .height(Length::Fill) | ||||||
|                 password, |                 .into(), | ||||||
|                 server, |             Retrix::LoggedIn(view) => view.view(), | ||||||
|                 action, |  | ||||||
|                 error, |  | ||||||
|             } => { |  | ||||||
|                 // Login form
 |  | ||||||
|                 let mut content = Column::new() |  | ||||||
|                     .width(500.into()) |  | ||||||
|                     .push( |  | ||||||
|                         Row::new() |  | ||||||
|                             .push(Radio::new( |  | ||||||
|                                 PromptAction::Login, |  | ||||||
|                                 "Login", |  | ||||||
|                                 Some(*action), |  | ||||||
|                                 Message::SetAction, |  | ||||||
|                             )) |  | ||||||
|                             .push(Radio::new( |  | ||||||
|                                 PromptAction::Signup, |  | ||||||
|                                 "Sign up", |  | ||||||
|                                 Some(*action), |  | ||||||
|                                 Message::SetAction, |  | ||||||
|                             )), |  | ||||||
|                     ) |  | ||||||
|                     .push(Text::new("Username")) |  | ||||||
|                     .push(TextInput::new(user_input, "Username", user, Message::SetUser).padding(5)) |  | ||||||
|                     .push(Text::new("Password")) |  | ||||||
|                     .push( |  | ||||||
|                         TextInput::new(password_input, "Password", password, Message::SetPassword) |  | ||||||
|                             .password() |  | ||||||
|                             .padding(5), |  | ||||||
|                     ) |  | ||||||
|                     .push(Text::new("Homeserver")) |  | ||||||
|                     .push( |  | ||||||
|                         TextInput::new(server_input, "Server", server, Message::SetServer) |  | ||||||
|                             .padding(5), |  | ||||||
|                     ); |  | ||||||
|                 let button = match *action { |  | ||||||
|                     PromptAction::Login => { |  | ||||||
|                         Button::new(login_button, Text::new("Login")).on_press(Message::Login) |  | ||||||
|                     } |  | ||||||
|                     PromptAction::Signup => { |  | ||||||
|                         content = content.push( |  | ||||||
|                             Text::new( |  | ||||||
|                                 "NB: Signup is very naively implemented, and prone to breaking", |  | ||||||
|                             ) |  | ||||||
|                             .color([0.2, 0.2, 0.0]), |  | ||||||
|                         ); |  | ||||||
|                         Button::new(login_button, Text::new("Sign up")).on_press(Message::Signup) |  | ||||||
|                     } |  | ||||||
|                 }; |  | ||||||
|                 content = content.push(button); |  | ||||||
|                 if let Some(ref error) = error { |  | ||||||
|                     content = content.push(Text::new(error).color([1.0, 0.0, 0.0])); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 Container::new(content) |  | ||||||
|                     .center_x() |  | ||||||
|                     .center_y() |  | ||||||
|                     .width(iced::Length::Fill) |  | ||||||
|                     .height(iced::Length::Fill) |  | ||||||
|                     .into() |  | ||||||
|             } |  | ||||||
|             Retrix::AwaitLogin(instant) => Container::new(Text::new(format!( |  | ||||||
|                 "Logging in{}", |  | ||||||
|                 match instant.elapsed().subsec_millis() / 333 { |  | ||||||
|                     0 => ".", |  | ||||||
|                     1 => "..", |  | ||||||
|                     2 => "...", |  | ||||||
|                     _ => "....", |  | ||||||
|                 } |  | ||||||
|             ))) |  | ||||||
|             .center_x() |  | ||||||
|             .center_y() |  | ||||||
|             .width(Length::Fill) |  | ||||||
|             .height(Length::Fill) |  | ||||||
|             .into(), |  | ||||||
|             Retrix::LoggedIn { |  | ||||||
|                 client, |  | ||||||
|                 room_scroll, |  | ||||||
|                 message_scroll, |  | ||||||
|                 message_input, |  | ||||||
|                 send_button, |  | ||||||
|                 buttons, |  | ||||||
|                 selected, |  | ||||||
|                 draft, |  | ||||||
|                 .. |  | ||||||
|             } => { |  | ||||||
|                 let mut root_row = Row::new().width(Length::Fill).height(Length::Fill); |  | ||||||
| 
 |  | ||||||
|                 // Room list
 |  | ||||||
|                 let joined = client.joined_rooms(); |  | ||||||
|                 let rooms = futures::executor::block_on(async { joined.read().await }); |  | ||||||
|                 let mut room_col = Scrollable::new(room_scroll) |  | ||||||
|                     .width(400.into()) |  | ||||||
|                     .height(Length::Fill) |  | ||||||
|                     .scrollbar_width(5); |  | ||||||
|                 // We have to iterate the buttons map and not the other way around to make the
 |  | ||||||
|                 // borrow checker happy. First we make sure there's a button entry for every room
 |  | ||||||
|                 // entry, and clean up button entries from removed rooms.
 |  | ||||||
|                 for (id, _) in rooms.iter() { |  | ||||||
|                     buttons.entry(id.to_owned()).or_default(); |  | ||||||
|                 } |  | ||||||
|                 buttons.retain(|id, _| rooms.contains_key(id)); |  | ||||||
|                 // Then we make our buttons
 |  | ||||||
|                 let buttons: Vec<Button<_>> = buttons |  | ||||||
|                     .iter_mut() |  | ||||||
|                     .map(|(id, state)| { |  | ||||||
|                         // Get read lock for the room
 |  | ||||||
|                         let room = futures::executor::block_on(async { |  | ||||||
|                             rooms.get(id).unwrap().read().await |  | ||||||
|                         }); |  | ||||||
|                         Button::new(state, Text::new(room.display_name())) |  | ||||||
|                             .on_press(Message::SelectRoom(id.to_owned())) |  | ||||||
|                             .width(400.into()) |  | ||||||
|                     }) |  | ||||||
|                     .collect(); |  | ||||||
|                 // Then we add them to our room column. What a mess.
 |  | ||||||
|                 for button in buttons { |  | ||||||
|                     room_col = room_col.push(button); |  | ||||||
|                 } |  | ||||||
|                 root_row = root_row.push(room_col); |  | ||||||
| 
 |  | ||||||
|                 // Messages.
 |  | ||||||
|                 //
 |  | ||||||
|                 // Get selected room.
 |  | ||||||
|                 let selected_room = selected.as_ref().and_then(|selected| { |  | ||||||
|                     futures::executor::block_on(async { |  | ||||||
|                         match rooms.get(selected) { |  | ||||||
|                             Some(room) => Some(room.read().await), |  | ||||||
|                             None => None, |  | ||||||
|                         } |  | ||||||
|                     }) |  | ||||||
|                 }); |  | ||||||
|                 if let Some(room) = selected_room { |  | ||||||
|                     let mut col = Column::new() |  | ||||||
|                         .spacing(5) |  | ||||||
|                         .padding(5) |  | ||||||
|                         .push(Text::new(room.display_name()).size(25)) |  | ||||||
|                         .push(Rule::horizontal(2)); |  | ||||||
|                     let mut scroll = Scrollable::new(message_scroll) |  | ||||||
|                         .scrollbar_width(2) |  | ||||||
|                         .height(Length::Fill); |  | ||||||
|                     for message in room.messages.iter() { |  | ||||||
|                         if let AnyPossiblyRedactedSyncMessageEvent::Regular(event) = message { |  | ||||||
|                             if let AnySyncMessageEvent::RoomMessage(room_message) = event { |  | ||||||
|                                 match &room_message.content { |  | ||||||
|                                     MessageEventContent::Text(text) => { |  | ||||||
|                                         let row = Row::new() |  | ||||||
|                                             .spacing(5) |  | ||||||
|                                             .push( |  | ||||||
|                                                 // Render senders disambiguated name or fallback to
 |  | ||||||
|                                                 // mxid
 |  | ||||||
|                                                 Text::new( |  | ||||||
|                                                     room.joined_members |  | ||||||
|                                                         .get(&room_message.sender) |  | ||||||
|                                                         .map(|sender| sender.disambiguated_name()) |  | ||||||
|                                                         .unwrap_or(room_message.sender.to_string()), |  | ||||||
|                                                 ) |  | ||||||
|                                                 .color([0.2, 0.2, 1.0]), |  | ||||||
|                                             ) |  | ||||||
|                                             .push(Text::new(&text.body).width(Length::Fill)) |  | ||||||
|                                             .push(Text::new(format_systime( |  | ||||||
|                                                 room_message.origin_server_ts, |  | ||||||
|                                             ))); |  | ||||||
|                                         scroll = scroll.push(row); |  | ||||||
|                                     } |  | ||||||
|                                     _ => (), |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     col = col.push(scroll).push( |  | ||||||
|                         Row::new() |  | ||||||
|                             .push( |  | ||||||
|                                 TextInput::new( |  | ||||||
|                                     message_input, |  | ||||||
|                                     "Write a message...", |  | ||||||
|                                     draft, |  | ||||||
|                                     Message::SetMessage, |  | ||||||
|                                 ) |  | ||||||
|                                 .width(Length::Fill) |  | ||||||
|                                 .padding(5) |  | ||||||
|                                 .on_submit(Message::SendMessage), |  | ||||||
|                             ) |  | ||||||
|                             .push( |  | ||||||
|                                 Button::new(send_button, Text::new("Send")) |  | ||||||
|                                     .on_press(Message::SendMessage), |  | ||||||
|                             ), |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     root_row = root_row.push(col); |  | ||||||
|                 } else { |  | ||||||
|                     root_row = root_row.push( |  | ||||||
|                         Container::new(Text::new("Select a room to start chatting")) |  | ||||||
|                             .center_x() |  | ||||||
|                             .center_y() |  | ||||||
|                             .width(Length::Fill) |  | ||||||
|                             .height(Length::Fill), |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 root_row.into() |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue