diff --git a/apps/mtxchat/locales/i18n.json b/apps/mtxchat/locales/i18n.json index eb368f85e..53ba2621a 100644 --- a/apps/mtxchat/locales/i18n.json +++ b/apps/mtxchat/locales/i18n.json @@ -18,6 +18,10 @@ "ja": "WARNING: clock not set *EN*", "zh": "WARNING: clock not set *EN*" }, + "mtxchat.status.default" : { + "en": "Use ← for login menu", + "en-tts": "Use ← for login menu" + }, "mtxchat.filter.failed": { "en": "error: could not create filter", "en-tts": "error: could not create filter", diff --git a/apps/mtxchat/src/lib.rs b/apps/mtxchat/src/lib.rs index d78b66569..35e18b256 100644 --- a/apps/mtxchat/src/lib.rs +++ b/apps/mtxchat/src/lib.rs @@ -83,6 +83,7 @@ pub struct MtxChat<'a> { modals: Modals, new_username: bool, new_room: bool, + status: String, } impl<'a> MtxChat<'a> { pub fn new(chat: &Chat) -> MtxChat { @@ -91,6 +92,8 @@ impl<'a> MtxChat<'a> { let trng = Trng::new(&xns).unwrap(); let pddb = pddb::Pddb::new(); pddb.try_mount(); + let status = t!("mtxchat.status.default", locales::LANG).to_owned(); + chat.set_status_text(&status); MtxChat { chat: chat, trng: trng, @@ -113,6 +116,7 @@ impl<'a> MtxChat<'a> { modals: modals, new_username: false, new_room: false, + status, } } @@ -241,7 +245,6 @@ impl<'a> MtxChat<'a> { pub fn connect(&mut self) -> bool { log::info!("Attempting connect to Matrix server"); - self.chat.set_busy_state(Some(t!("mtxchat.busy.connecting", locales::LANG).to_owned())).expect("couldn't set busy state"); if self.wifi() { if self.login() { if let Some(_room_id) = self.get_room_id() { @@ -253,7 +256,6 @@ impl<'a> MtxChat<'a> { .show_notification(t!("mtxchat.listen.patience", locales::LANG), None) .expect("notification failed"); } - self.chat.set_busy_state(None).expect("couldn't clear busy state"); return true; } else { self.modals @@ -275,11 +277,12 @@ impl<'a> MtxChat<'a> { .expect("notification failed"); } self.dialogue_set(None); - self.chat.set_busy_state(None).expect("couldn't clear busy state"); false } pub fn login(&mut self) -> bool { + self.chat.set_status_text(t!("mtxchat.busy.connecting", locales::LANG)); + self.chat.set_busy_state(true); self.token = self.get(TOKEN_KEY).unwrap_or(None); self.logged_in = false; @@ -338,6 +341,8 @@ impl<'a> MtxChat<'a> { self.unset(USER_DOMAIN_KEY) .expect("failed to unset user domain"); } + self.chat.set_status_text(&self.status); + self.chat.set_busy_state(false); self.logged_in } @@ -629,7 +634,8 @@ impl<'a> MtxChat<'a> { &self.room_id, ) { (true, Some(token), Some(user_domain), Some(room_id)) => { - self.chat.set_busy_state(Some(t!("mtxchat.busy.sending", locales::LANG).to_owned())).expect("couldn't set busy state"); + self.chat.set_status_text(t!("mtxchat.busy.sending", locales::LANG)); + self.chat.set_busy_state(true); log::info!("txn_id = {}", txn_id); let mut url = Url::parse("https://matrix.org").unwrap(); url.set_host(Some(user_domain)).expect("failed to set host"); @@ -638,7 +644,8 @@ impl<'a> MtxChat<'a> { } else { "FAILED TO SEND" }; - self.chat.set_busy_state(None).expect("couldn't clear busy state"); + self.chat.set_status_text(&self.status); + self.chat.set_busy_state(false); r } (false, _, _, _) => "Not logged in", diff --git a/libs/chat/locales/i18n.json b/libs/chat/locales/i18n.json index d8a49f26c..ede28f36f 100644 --- a/libs/chat/locales/i18n.json +++ b/libs/chat/locales/i18n.json @@ -20,8 +20,8 @@ "en-tts": "no dialogues available to read" }, "chat.status.initial" : { - "en": "use ← for login menu", - "en-tts": "use ← for login menu" + "en": "Idle", + "en-tts": "Idle" }, "chat.help.navigation": { "en": "use ↑ & ↓ to scroll thru old posts\nuse ← & → to show menus\nF1-F4 dont do anything yet.", diff --git a/libs/chat/src/api.rs b/libs/chat/src/api.rs index 41b29fab9..091e04870 100644 --- a/libs/chat/src/api.rs +++ b/libs/chat/src/api.rs @@ -36,6 +36,8 @@ pub enum ChatOp { /// Find a Post by timestamp and Author PostFind, PostFlag, + /// Set status bar text + SetStatusText, /// Run or stop the busy animation. SetBusyAnimationState, /// Update just the state of the busy animation, if any. Internal opcode. @@ -133,8 +135,5 @@ pub enum AuthorFlag { #[derive(Archive, Serialize, Deserialize, Debug)] pub struct BusyMessage { - /// If None, restores the last non-busy status bar message, and stops the animation. - /// If Some, updates the status bar to contain the busy message and - /// starts the animation running. - pub busy_msg: Option>, + pub busy_msg: xous_ipc::String<128>, } diff --git a/libs/chat/src/lib.rs b/libs/chat/src/lib.rs index 5b7b3dd36..a95272bab 100644 --- a/libs/chat/src/lib.rs +++ b/libs/chat/src/lib.rs @@ -12,7 +12,7 @@ use std::thread; use std::time::{SystemTime, UNIX_EPOCH}; use std::sync::{Arc, atomic::AtomicBool, atomic::Ordering}; -use xous::{Error, CID, SID}; +use xous::{Error, CID, SID, msg_scalar_unpack}; use xous_ipc::Buffer; pub struct Chat { @@ -239,32 +239,31 @@ impl Chat { .expect("failed to Redraw Chat UI"); } - /// Run or stop the busy/waiting animation. + /// Set the status bar text. /// /// # Arguments /// - /// `msg` - If `Some()`, updates the status bar to show the `msg` and start the animation running - /// If `None`, restores the last status message before being busy and stops the animation. - pub fn set_busy_state(&self, msg: Option) -> Result<(), Error> { + /// `msg` - the text to show + /// + /// This method implements the latest recommendation of panicing on internal errors. + pub fn set_status_text(&self, msg: &str) { let bm = BusyMessage { - busy_msg: match msg { - None => None, - Some(m) => { - Some( - xous_ipc::String::from_str(&m) - ) - } - } + busy_msg: xous_ipc::String::from_str(msg) }; - match Buffer::into_buf(bm) { - Ok(buf) => match buf.send(self.cid, ChatOp::SetBusyAnimationState as u32) { - Ok(..) => { - Ok(()) - } - Err(_) => Err(xous::Error::InternalError), - }, - Err(_) => Err(xous::Error::InternalError), - } + Buffer::into_buf(bm).expect("internal error").send( + self.cid, ChatOp::SetStatusText as u32 + ).expect("internal error"); + } + + pub fn set_busy_state(&self, run: bool) { + xous::send_message( + self.cid, + xous::Message::new_scalar(ChatOp::SetBusyAnimationState as usize, + if run { 1 } else { 0 }, + 0, 0, 0), + ) + .map(|_| ()) + .expect("internal error"); } } @@ -299,6 +298,7 @@ pub fn busy_animator( ChatOp::UpdateBusy as usize, 0, 0, 0, 0) ).ok(); tt.sleep_ms(crate::BUSY_ANIMATION_RATE_MS).unwrap(); + log::info!("busy!"); xous::try_send_message(busy_bumper_cid, xous::Message::new_scalar( BusyAnimOp::Pump as usize, 0, 0, 0, 0) @@ -346,37 +346,32 @@ pub fn server( loop { let msg = xous::receive_message(sid).unwrap(); log::debug!("got message {:?}", msg); - if ui.is_busy() { - if !run_busy_animation.swap(true, Ordering::SeqCst) { - // only send off the Pump request on the transition from false->true; this causes the machine to run - xous::try_send_message(busy_bumper_cid, - xous::Message::new_scalar( - BusyAnimOp::Pump as usize, 0, 0, 0, 0) - ).ok(); - } - } else { - // make sure the animation stops running. Not sure if this is the most efficient way to handle this, - // but I think an atomic bool set is just a couple dozen CPU cycles...? - run_busy_animation.store(false, Ordering::SeqCst); - } match FromPrimitive::from_usize(msg.body.id()) { Some(ChatOp::UpdateBusy) => { ui.redraw_busy().expect("CHAT couldn't redraw"); } - Some(ChatOp::SetBusyAnimationState) => { + Some(ChatOp::SetStatusText) => { let buffer = unsafe { Buffer::from_memory_message(msg.body.memory_message().unwrap()) }; let s = buffer.to_original::().unwrap(); - match s.busy_msg { - Some(msg) => { - ui.set_busy(msg.as_str().unwrap()); - } - None => { - ui.clear_busy(); - } - } + ui.set_status_text(s.busy_msg.as_str().unwrap()); } + Some(ChatOp::SetBusyAnimationState) => msg_scalar_unpack!(msg, state, _, _, _, { + if state != 0 { + ui.set_busy_state(true); + if !run_busy_animation.swap(true, Ordering::SeqCst) { + // only send off the Pump request on the transition from false->true; this causes the machine to run + xous::try_send_message(busy_bumper_cid, + xous::Message::new_scalar( + BusyAnimOp::Pump as usize, 0, 0, 0, 0) + ).ok(); + } + } else { + ui.set_busy_state(false); + run_busy_animation.store(false, Ordering::SeqCst) + } + }), Some(ChatOp::DialogueSave) => { log::info!("ChatOp::DialogueSave"); ui.dialogue_save().expect("failed to save Dialogue"); diff --git a/libs/chat/src/ui.rs b/libs/chat/src/ui.rs index 9e37365aa..4cd883036 100644 --- a/libs/chat/src/ui.rs +++ b/libs/chat/src/ui.rs @@ -64,8 +64,6 @@ pub(crate) struct Ui { status_tv: TextView, /// track the last time we update the status bar; use this avoid double-updating busy animations status_last_update_ms: u64, - /// track the last non-busy message so we can restore it when the busy state stops - status_last_msg: Option, bubble_width: u16, margin: Point, // margin to edge of canvas bubble_margin: Point, // margin of text in bubbles @@ -163,7 +161,6 @@ impl Ui { status_height, status_tv, status_last_update_ms, - status_last_msg: None, post_selected: None, post_anchor: None, post_topdown: false, @@ -642,16 +639,8 @@ impl Ui { self.status_tv.busy_animation_state.is_some() } - /// If not currently busy, save the current status bar message and kick off the animation. - /// Updates the text of the status bar in all cases. - pub(crate) fn set_busy(&mut self, msg: &str) { - if self.status_tv.busy_animation_state.is_none() { - // stash a copy of the last message that wasn't a busy message - self.status_last_msg = Some( - self.status_tv.to_str().to_owned() - ); - self.status_tv.busy_animation_state = Some(0); - } + /// Set the status bar text + pub(crate) fn set_status_text(&mut self, msg: &str) { self.status_tv.clear_str(); write!(self.status_tv, "{}", msg).ok(); xous::send_message(self.app_cid.unwrap(), @@ -659,13 +648,12 @@ impl Ui { ChatOp::UpdateBusy as usize, 0, 0, 0, 0) ).ok(); } - - /// Stops the busy animation state - pub(crate) fn clear_busy(&mut self) { - self.status_tv.busy_animation_state = None; - if let Some(prev_msg) = self.status_last_msg.take() { - self.status_tv.clear_str(); - write!(self.status_tv, "{}", prev_msg).ok(); + /// Sets the status bar to animate the busy animation + pub(crate) fn set_busy_state(&mut self, run: bool) { + if run { + self.status_tv.busy_animation_state = Some(0); // the "glitch" to 0 is intentional, gives an indicator that a new op has started + } else { + self.status_tv.busy_animation_state = None; } }