aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/app')
-rw-r--r--src/app/input/mod.rs1
-rw-r--r--src/app/input/modal.rs45
-rw-r--r--src/app/input/navigate.rs3
-rw-r--r--src/app/input/terminal.rs1
-rw-r--r--src/app/mod.rs105
-rw-r--r--src/app/runtime.rs2
6 files changed, 124 insertions, 33 deletions
diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs
index ae8fb12b..ad68f9da 100644
--- a/src/app/input/mod.rs
+++ b/src/app/input/mod.rs
@@ -295,7 +295,6 @@ fn app_for_mouse_test() -> App {
&crate::config::Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
);
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs
index 954f97de..ff5e8582 100644
--- a/src/app/input/modal.rs
+++ b/src/app/input/modal.rs
@@ -627,6 +627,23 @@ mod tests {
use super::super::{capture_snapshot, state_with_workspaces};
use super::*;
+ fn config_env_lock() -> &'static std::sync::Mutex<()> {
+ static LOCK: std::sync::OnceLock<std::sync::Mutex<()>> = std::sync::OnceLock::new();
+ LOCK.get_or_init(|| std::sync::Mutex::new(()))
+ }
+
+ fn temp_config_path(name: &str) -> std::path::PathBuf {
+ let unique = format!(
+ "herdr-modal-{name}-{}-{}",
+ std::process::id(),
+ std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap()
+ .as_nanos()
+ );
+ std::env::temp_dir().join(unique).join("config.toml")
+ }
+
#[test]
fn custom_resize_key_exits_resize_mode() {
let mut state = state_with_workspaces(&["test"]);
@@ -643,6 +660,34 @@ mod tests {
}
#[test]
+ fn global_menu_whats_new_opens_saved_release_notes() {
+ let _guard = config_env_lock().lock().unwrap();
+ let path = temp_config_path("whats-new-saved-release-notes");
+ std::env::set_var(crate::config::CONFIG_PATH_ENV_VAR, &path);
+ crate::release_notes::save_pending(env!("CARGO_PKG_VERSION"), "### Changed\n- Menu")
+ .unwrap();
+
+ let mut state = state_with_workspaces(&["test"]);
+ state.latest_release_notes_available = true;
+
+ assert!(global_menu_actions(&state).contains(&GlobalMenuAction::WhatsNew));
+
+ apply_global_menu_action(&mut state, GlobalMenuAction::WhatsNew);
+
+ assert_eq!(state.mode, Mode::ReleaseNotes);
+ assert_eq!(
+ state
+ .release_notes
+ .as_ref()
+ .map(|notes| notes.body.as_str()),
+ Some("### Changed\n- Menu")
+ );
+
+ std::env::remove_var(crate::config::CONFIG_PATH_ENV_VAR);
+ let _ = std::fs::remove_dir_all(path.parent().unwrap());
+ }
+
+ #[test]
fn rename_modal_keyboard_and_mouse_share_actions() {
let mut state = state_with_workspaces(&["test"]);
state.mode = Mode::RenameWorkspace;
diff --git a/src/app/input/navigate.rs b/src/app/input/navigate.rs
index e2e0993e..34d0a342 100644
--- a/src/app/input/navigate.rs
+++ b/src/app/input/navigate.rs
@@ -1035,7 +1035,6 @@ mod tests {
&Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
);
@@ -1084,7 +1083,6 @@ mod tests {
&Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
);
@@ -1153,7 +1151,6 @@ mod tests {
&Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
);
diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs
index b5ceda12..12fb67b2 100644
--- a/src/app/input/terminal.rs
+++ b/src/app/input/terminal.rs
@@ -461,7 +461,6 @@ mod tests {
&Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
);
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 39cf0c13..3e8a8b83 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -198,7 +198,6 @@ impl App {
config: &Config,
no_session: bool,
config_diagnostic: Option<String>,
- startup_release_notes: Option<crate::release_notes::ReleaseNotes>,
api_rx: tokio::sync::mpsc::UnboundedReceiver<crate::api::ApiRequestMessage>,
event_hub: crate::api::EventHub,
) -> Self {
@@ -321,8 +320,6 @@ impl App {
let mode = if config.should_show_onboarding() {
state::Mode::Onboarding
- } else if startup_release_notes.is_some() {
- state::Mode::ReleaseNotes
} else if startup_product_announcement.is_some() {
state::Mode::ProductAnnouncement
} else if active.is_some() {
@@ -353,12 +350,7 @@ impl App {
request_complete_onboarding: false,
name_input: String::new(),
name_input_replace_on_type: false,
- release_notes: startup_release_notes.map(|notes| state::ReleaseNotesState {
- version: notes.version,
- body: notes.body,
- scroll: 0,
- preview: notes.preview,
- }),
+ release_notes: None,
product_announcement: startup_product_announcement.map(|announcement| {
state::ProductAnnouncementState {
version: announcement.version,
@@ -1131,7 +1123,6 @@ mod tests {
&Config::default(),
true,
None,
- None,
api_rx,
crate::api::EventHub::default(),
)
@@ -1154,6 +1145,14 @@ mod tests {
std::env::temp_dir().join(unique).join("config.toml")
}
+ fn restore_xdg_state_home(original: Option<std::ffi::OsString>) {
+ if let Some(value) = original {
+ std::env::set_var("XDG_STATE_HOME", value);
+ } else {
+ std::env::remove_var("XDG_STATE_HOME");
+ }
+ }
+
#[test]
fn git_refresh_deadline_is_suppressed_while_in_flight() {
let mut app = test_app();
@@ -1204,14 +1203,7 @@ mod tests {
config.ui.agent_panel_scope = crate::config::AgentPanelScopeConfig::Current;
let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel();
- let app = App::new(
- &config,
- true,
- None,
- None,
- api_rx,
- crate::api::EventHub::default(),
- );
+ let app = App::new(&config, true, None, api_rx, crate::api::EventHub::default());
assert_eq!(
app.state.agent_panel_scope,
@@ -1255,6 +1247,74 @@ mod tests {
}
#[test]
+ fn startup_keeps_pending_release_notes_available_without_auto_opening() {
+ let _guard = config_env_lock().lock().unwrap();
+ let path = temp_config_path("startup-pending-release-notes-no-auto-open");
+ std::env::set_var(crate::config::CONFIG_PATH_ENV_VAR, &path);
+
+ crate::release_notes::save_pending(env!("CARGO_PKG_VERSION"), "### Changed\n- One")
+ .unwrap();
+ let config = Config {
+ onboarding: Some(false),
+ ..Default::default()
+ };
+ let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel();
+
+ let app = App::new(&config, true, None, api_rx, crate::api::EventHub::default());
+
+ assert_eq!(app.state.mode, Mode::Navigate);
+ assert!(app.state.release_notes.is_none());
+ assert!(app.state.latest_release_notes_available);
+
+ std::env::remove_var(crate::config::CONFIG_PATH_ENV_VAR);
+ let _ = std::fs::remove_dir_all(path.parent().unwrap());
+ }
+
+ #[test]
+ fn startup_still_auto_opens_unseen_product_announcement() {
+ let _guard = config_env_lock().lock().unwrap();
+ let path = temp_config_path("startup-product-announcement-auto-open");
+ let state_home = path.parent().unwrap().join("state");
+ let original_xdg_state_home = std::env::var_os("XDG_STATE_HOME");
+ std::env::set_var(crate::config::CONFIG_PATH_ENV_VAR, &path);
+ std::env::set_var("XDG_STATE_HOME", &state_home);
+
+ crate::release_notes::save_pending(env!("CARGO_PKG_VERSION"), "### Changed\n- One")
+ .unwrap();
+ crate::product_announcements::save_manifest_announcement(
+ env!("CARGO_PKG_VERSION"),
+ Some(&crate::product_announcements::ManifestAnnouncement {
+ id: "startup-announcement".into(),
+ title: Some("Startup announcement".into()),
+ body: "### Announcement\n- One".into(),
+ }),
+ )
+ .unwrap();
+
+ let config = Config {
+ onboarding: Some(false),
+ ..Default::default()
+ };
+ let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel();
+
+ let app = App::new(&config, true, None, api_rx, crate::api::EventHub::default());
+
+ assert_eq!(app.state.mode, Mode::ProductAnnouncement);
+ assert_eq!(
+ app.state
+ .product_announcement
+ .as_ref()
+ .map(|announcement| announcement.id.as_str()),
+ Some("startup-announcement")
+ );
+ assert!(app.state.release_notes.is_none());
+
+ std::env::remove_var(crate::config::CONFIG_PATH_ENV_VAR);
+ restore_xdg_state_home(original_xdg_state_home);
+ let _ = std::fs::remove_dir_all(path.parent().unwrap());
+ }
+
+ #[test]
fn reload_config_updates_live_state() {
let _guard = config_env_lock().lock().unwrap();
let path = temp_config_path("reload-config-success");
@@ -1382,14 +1442,7 @@ mod tests {
config.ui.sidebar_max_width = 30;
let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel();
- let app = App::new(
- &config,
- true,
- None,
- None,
- api_rx,
- crate::api::EventHub::default(),
- );
+ let app = App::new(&config, true, None, api_rx, crate::api::EventHub::default());
assert_eq!(
app.state.sidebar_min_width, 18,
diff --git a/src/app/runtime.rs b/src/app/runtime.rs
index 739d3548..5671bf38 100644
--- a/src/app/runtime.rs
+++ b/src/app/runtime.rs
@@ -419,7 +419,6 @@ mod tests {
&crate::config::Config::default(),
true,
None,
- None,
tokio::sync::mpsc::unbounded_channel().1,
crate::api::EventHub::default(),
);
@@ -528,7 +527,6 @@ mod tests {
&crate::config::Config::default(),
true,
None,
- None,
tokio::sync::mpsc::unbounded_channel().1,
crate::api::EventHub::default(),
);