7DWCVQ7NSWW52VKMR7DM27CMOVDXECTXNHYNLDF4NVAI6YH3HHVQC NY4FHE2BHD6ZALKMHLI4UR2LRNJJNZ4VWJ7XZFUMVSTTC6XBX2OQC CEXASC5XWUEA4SWUKX6HSAXV7DSZTIIBXIQEFZAHAFZ7NUIWQLXAC K772IAVA7N3R2TAC7SAXK4FVUOOROWMKFMUN3HX7A276H6JA6Y7QC J7PC7UGJE64XZIJXE5YETSHYZUUHSH2B52OA5E5JVQUIF6P72JAQC MVB4BCD4FIJBLC5ISSG2ZKPYDSEV6IBB3YL75LXVEVZ3BEJDZ52QC C6ZDDCL46FXQ7W2UEDZ62YJWI5LUEI4ONHQWU3CHWRVTYSLWRDWAC VO4UOVTJKRL5LOOMKGMQ5DGN5SAZGCEUAUZTKGRE6N3KSCCXHWFAC }pub fn rooms_queued_events(&self,) -> Vec<(RoomId, Vec<(Uuid, AnySyncRoomEvent, Option<Duration>)>)> {self.rooms.iter().map(|(id, room)| {(id.clone(),room.queued_events().map(|event| {let txn_id = event.transaction_id().copied().unwrap();(txn_id,event.event_content().clone(),room.get_wait_for_duration(&txn_id),)}).collect(),)}).collect()
alt_aliases: vec![],timeline: vec![],members: Members::new(),display_name_to_user_id: HashMap::new(),
alt_aliases: Default::default(),timeline: Default::default(),members: Default::default(),display_name_to_user_id: Default::default(),wait_for_duration: Default::default(),
pub fn ack_event(&mut self, ack_event: &TimelineEvent) {
pub fn wait_for_duration(&mut self, duration: Duration, transaction_id: Uuid) {*self.wait_for_duration.entry(transaction_id).or_insert(duration) = duration;}pub fn get_wait_for_duration(&self, transaction_id: &Uuid) -> Option<Duration> {self.wait_for_duration.get(transaction_id).copied()}pub fn ack_event(&mut self, transaction_id: &Uuid) {self.wait_for_duration.remove(transaction_id);
if let Some(thumbnail_image) = {if is_thumbnail {Some(content_url.clone())} else {timeline_event.thumbnail_url()}}.map(|thumbnail_url| {thumbnail_store.get_thumbnail(&thumbnail_url).map(|handle| Image::new(handle.clone()).width(Length::Fill))}).flatten()
if let Some(thumbnail_image) = thumbnail_store.get_thumbnail(&content_url).or_else(|| {timeline_event.thumbnail_url().map(|url| thumbnail_store.get_thumbnail(&url)).flatten()}).map(|handle| Image::new(handle.clone()).width(Length::Units(360)))
MessageHistoryScrolled(f32, f32),/// Sent when the user clicks the logout button.
MessageHistoryScrolled {prev_scroll_perc: f32,scroll_perc: f32,},/// Sent when the user selects an option from the bottom menu.
/// Sent when a `main::Message::SendMessage` message returns a `LimitExceeded` error./// This is used to retry sending a message. The duration is/// the "retry after" time. The UUID is the transaction ID of/// this message, used to identify which message to re-send./// The room ID is the ID of the room in which the message is/// stored.RetrySendMessage {retry_after: Duration,transaction_id: Uuid,room_id: RoomId,},
if let Some(room_id) = self.current_room_id.as_ref() {if let Some(room) = rooms.get(room_id) {let message_composer = TextInput::new(&mut self.composer_state,"Enter your message here...",self.message.as_str(),Message::MessageChanged,).padding(12).size(16).style(DarkTextInput).on_submit(Message::SendMessageComposer(room_id.clone()));
if let Some((room, room_id)) = self.current_room_id.as_ref().map(|id| Some((rooms.get(id)?, id))).flatten(){let message_composer = TextInput::new(&mut self.composer_state,"Enter your message here...",self.message.as_str(),Message::MessageChanged,).padding(12).size(16).style(DarkTextInput).on_submit(Message::SendMessageComposer(room_id.clone()));let current_user_id = self.client.current_user_id();let displayable_event_count = room.displayable_events().count();
let current_user_id = self.client.current_user_id();let room_disp_len = room.displayable_events().len();
let message_history_list = build_event_history(&self.thumbnail_store,room,¤t_user_id,self.looking_at_event.get(room_id).copied().unwrap_or_else(|| displayable_event_count.saturating_sub(1)),&mut self.event_history_state,&mut self.content_open_buts_state,theme,);
let message_history_list = build_event_history(&self.thumbnail_store,room,¤t_user_id,self.looking_at_event.get(room_id).copied().unwrap_or_else(|| room_disp_len.saturating_sub(1)),&mut self.event_history_state,&mut self.content_open_buts_state,theme,);
let mut typing_users_combined = String::new();let mut typing_members = room.typing_members();// Remove own user id from the list (if its there)if let Some(index) = typing_members.iter().position(|id| *id == ¤t_user_id) {typing_members.remove(index);}let typing_members_count = typing_members.len();
let mut typing_users_combined = String::new();let mut typing_members = room.typing_members();// Remove own user id from the list (if its there)if let Some(index) = typing_members.iter().position(|id| *id == ¤t_user_id) {typing_members.remove(index);
for (index, member_id) in typing_members.iter().enumerate() {if index > 2 {typing_users_combined += " and others are typing...";break;
typing_users_combined += match typing_members_count {x if x > index + 1 => ", ",1 => " is typing...",_ => " are typing...",};}
let typing_users = Column::with_children(vec![Space::with_width(Length::Units(6)).into(),Row::with_children(vec![Space::with_width(Length::Units(9)).into(),Text::new(typing_users_combined).size(14).into(),]).into(),]).height(Length::Units(14));
let typing_users = Column::with_children(vec![Space::with_width(Length::Units(6)).into(),Row::with_children(vec![Space::with_width(Length::Units(9)).into(),Text::new(typing_users_combined).size(14).into(),]).into(),]);
let send_file_button =Button::new(&mut self.send_file_but_state, Text::new("↑").size(28)).style(DarkButton).on_press(Message::SendFile(room_id.clone()));
let send_file_button =Button::new(&mut self.send_file_but_state, Text::new("↑").size(28)).style(DarkButton).on_press(Message::SendFile(room_id.clone()));
let mut bottom_area_widgets = vec![send_file_button.into(),message_composer.width(Length::Fill).into(),];
let mut bottom_area_widgets = vec![send_file_button.into(),message_composer.width(Length::Fill).into(),];// This unwrap is safe since we add the room to the map before thisif *self.looking_at_event.get(room_id).unwrap()< room_disp_len.saturating_sub(SHOWN_MSGS_LIMIT){bottom_area_widgets.push(Button::new(&mut self.scroll_to_bottom_but_state,Text::new("↡").size(28),).style(DarkButton).on_press(Message::ScrollToBottom).into(),);}let message_area = Column::with_children(vec![message_history_list,typing_users.into(),Container::new(Row::with_children(bottom_area_widgets).spacing(8).width(Length::Fill),
// This unwrap is safe since we add the room to the map before thisif *self.looking_at_event.get(room_id).unwrap()< displayable_event_count.saturating_sub(SHOWN_MSGS_LIMIT){bottom_area_widgets.push(Button::new(&mut self.scroll_to_bottom_but_state,Text::new("↡").size(28),
let message_area = Column::with_children(vec![message_history_list,typing_users.into(),Container::new(Row::with_children(bottom_area_widgets).spacing(8).width(Length::Fill),).width(Length::Fill).padding(8).into(),]);screen_widgets.push(Container::new(message_area).width(Length::Fill).height(Length::Fill).style(BrightContainer).into(),);
fn process_send_message_result(result: Result<send_message_event::Response, ClientError>,transaction_id: Uuid,room_id: RoomId,) -> super::Message {use ruma::{api::client::error::ErrorKind as ClientAPIErrorKind, api::error::*};use ruma_client::Error as InnerClientError;match result {Ok(_) => super::Message::Nothing,Err(err) => {if let ClientError::Internal(InnerClientError::FromHttpResponse(FromHttpResponseError::Http(ServerError::Known(err)),)) = &err{if let ClientAPIErrorKind::LimitExceeded {retry_after_ms: Some(retry_after),} = err.kind{return super::Message::MainScreen(Message::RetrySendMessage {retry_after,transaction_id,room_id,});}}super::Message::MatrixError(Box::new(err))}}}
Message::OpenContent(content_url, is_thumbnail) => {let process_path_result = |result| match result {Ok(path) => {open::that_in_background(path);super::Message::Nothing}Err(err) => super::Message::MatrixError(Box::new(err)),};
Message::OpenContent {content_url,is_thumbnail,} => {let thumbnail_exists = self.thumbnail_store.has_thumbnail(&content_url);
Command::perform(async move { Ok(content_path) }, process_path_result)
Command::perform(async move {let thumbnail = if is_thumbnail && !thumbnail_exists {tokio::fs::read(&content_path).await.map_or(None, |data| Some((data, content_url)))} else {None};(content_path, thumbnail)},|(content_path, thumbnail)| {open::that_in_background(content_path);if let Some((data, thumbnail_url)) = thumbnail {super::Message::MainScreen(Message::DownloadedThumbnail {thumbnail_url,thumbnail: ImageHandle::from_memory(data),})} else {super::Message::Nothing}},)
let mut commands = Vec::with_capacity(content.len());for content in content {if self.client.has_room(&room_id) {let inner = self.client.inner();let transaction_id = Uuid::new_v4();// This unwrap is safe since we check if the room exists beforehand// TODO: check if we actually need to check if a room exists beforehandself.client.get_room_mut(&room_id).unwrap().add_event(TimelineEvent::new_unacked_message(content.clone(), transaction_id),);let content = AnyMessageEventContent::RoomMessage(content);commands.push(Command::perform({let room_id = room_id.clone();async move {(Client::send_message(inner,content,room_id.clone(),transaction_id,).await,transaction_id,room_id,)}},|(result, transaction_id, room_id)| {process_send_message_result(result, transaction_id, room_id)},));
if let Some(room) = self.client.get_room_mut(&room_id) {for content in content {room.add_event(TimelineEvent::new_unacked_message(content, Uuid::new_v4()));
Message::RetrySendMessage {retry_after,transaction_id,room_id,} => {let inner = self.client.inner();let content = if let Some(Some(Some(content))) =self.client.get_room(&room_id).map(|room| {room.timeline().iter().find(|tevent| tevent.transaction_id() == Some(&transaction_id)).map(|tevent| tevent.message_content())}) {content} else {return Command::none();};
Message::SendMessageResult(errors) => {use ruma::{api::client::error::ErrorKind as ClientAPIErrorKind, api::error::*};use ruma_client::Error as InnerClientError;
return Command::perform(async move {tokio::time::delay_for(retry_after).await;(Client::send_message(inner, content, room_id.clone(), transaction_id).await,transaction_id,room_id,)},|(result, transaction_id, room_id)| {process_send_message_result(result, transaction_id, room_id)},);
for (room_id, errors) in errors {for (transaction_id, error) in errors {if let ClientError::Internal(InnerClientError::FromHttpResponse(FromHttpResponseError::Http(ServerError::Known(err)),)) = error{if let ClientAPIErrorKind::LimitExceeded { retry_after_ms } = err.kind {if let Some(retry_after) = retry_after_ms {if let Some(room) = self.client.get_room_mut(&room_id) {room.wait_for_duration(retry_after, transaction_id);}log::error!("Send message after: {}", retry_after.as_secs());}}} else {log::error!("Error while sendign message: {}", error);}}}
.map(|(id, room)| (id, room.displayable_events().len())).filter(|(id, disp)| {self.current_room_id.as_ref() != Some(id)&& if let Some(disp_at) = self.looking_at_event.get(id) {*disp_at == disp.saturating_sub(1)} else {false
.filter_map(|(id, room)| {let disp = room.displayable_events().count();if self.current_room_id.as_ref() != Some(id) {if let Some(disp_at) = self.looking_at_event.get(id) {if *disp_at == disp.saturating_sub(1) {return Some((id.clone(), disp));}
Subscription::from_recipe(SyncRecipe {client: self.client.inner(),since,}).map(|result| match result {Ok(response) => {super::Message::MainScreen(Message::MatrixSyncResponse(Box::from(response)))}Err(err) => super::Message::MatrixError(Box::new(err)),})} else {Subscription::none()
sub = Subscription::batch(vec![sub,Subscription::from_recipe(SyncRecipe {client: self.client.inner(),since,}).map(|result| match result {Ok(response) => {super::Message::MainScreen(Message::MatrixSyncResponse(Box::from(response)))}Err(err) => super::Message::MatrixError(Box::new(err)),}),]);
impl<H, I> iced_futures::subscription::Recipe<H, I> for RetrySendEventRecipewhereH: Hasher,{type Output = RetrySendEventResult;fn hash(&self, state: &mut H) {std::any::TypeId::of::<Self>().hash(state);for (id, events) in &self.rooms_queued_events {id.hash(state);for (transaction_id, _, retry_after) in events {transaction_id.hash(state);retry_after.hash(state);}}}fn stream(self: Box<Self>, _input: BoxStream<I>) -> BoxStream<Self::Output> {let future = async move {let mut room_errors = Vec::new();for (room_id, events) in self.rooms_queued_events {let mut transaction_errors = Vec::new();for (transaction_id, event, retry_after) in events {if let Some(dur) = retry_after {tokio::time::delay_for(dur).await;}let result = match event {AnySyncRoomEvent::Message(ev) => {Client::send_message(self.client.clone(),ev.content(),room_id.clone(),transaction_id,).await}_ => unimplemented!(),};if let Err(e) = result {transaction_errors.push((transaction_id, e));}}room_errors.push((room_id, transaction_errors));}room_errors};Box::pin(iced_futures::futures::stream::once(future))}}