use rosace_state::GlobalAtom;
use rosace_trace::event::AtomId;
use crate::context::Context;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LifecycleState {
#[default]
Active,
Inactive,
Background,
Suspended,
}
const APP_LIFECYCLE_ATOM_ID: AtomId = AtomId(0xFFF9);
static APP_LIFECYCLE: GlobalAtom<LifecycleState> =
GlobalAtom::new(APP_LIFECYCLE_ATOM_ID, || LifecycleState::Active);
pub fn use_app_lifecycle(ctx: &Context) -> LifecycleState {
APP_LIFECYCLE.get_or_init().subscribe(ctx.component_id());
APP_LIFECYCLE.get()
}
pub fn app_lifecycle() -> LifecycleState {
APP_LIFECYCLE.get()
}
pub fn set_app_lifecycle(state: LifecycleState) {
APP_LIFECYCLE.set(state);
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static TEST_LOCK: Mutex<()> = Mutex::new(());
#[test]
fn defaults_to_active() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
set_app_lifecycle(LifecycleState::Active); assert_eq!(app_lifecycle(), LifecycleState::Active);
}
#[test]
fn set_then_read_round_trips_every_state() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
for state in [
LifecycleState::Inactive,
LifecycleState::Background,
LifecycleState::Suspended,
LifecycleState::Active,
] {
set_app_lifecycle(state);
assert_eq!(app_lifecycle(), state);
}
}
#[test]
fn use_app_lifecycle_subscribes_the_calling_component_for_re_render() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
set_app_lifecycle(LifecycleState::Active);
let component = rosace_trace::event::ComponentId(4242);
let ctx = Context::new(component);
assert_eq!(use_app_lifecycle(&ctx), LifecycleState::Active);
let _ = rosace_state::dirty_set::take_dirty_components();
set_app_lifecycle(LifecycleState::Background);
assert!(
rosace_state::dirty_set::take_dirty_components().contains(&component),
"a lifecycle transition must mark the subscribed component dirty"
);
APP_LIFECYCLE.get_or_init().unsubscribe(component);
set_app_lifecycle(LifecycleState::Active); }
}