use db::kvp::KEY_VALUE_STORE;use gpui::{App, AppContext as _, Context, Subscription, Task, WindowId};use util::ResultExt; pub struct Session { session_id: String, old_session_id: Option<String>, old_window_ids: Option<Vec<WindowId>>,} const SESSION_ID_KEY: &str = "session_id";const SESSION_WINDOW_STACK_KEY: &str = "session_window_stack"; impl Session { pub async fn new(session_id: String) -> Self { let old_session_id = KEY_VALUE_STORE.read_kvp(SESSION_ID_KEY).ok().flatten(); KEY_VALUE_STORE .write_kvp(SESSION_ID_KEY.to_string(), session_id.clone()) .await .log_err(); let old_window_ids = KEY_VALUE_STORE .read_kvp(SESSION_WINDOW_STACK_KEY) .ok() .flatten() .and_then(|json| serde_json::from_str::<Vec<u64>>(&json).ok()) .map(|vec| { vec.into_iter() .map(WindowId::from) .collect::<Vec<WindowId>>() }); Self { session_id, old_session_id, old_window_ids, } } #[cfg(any(test, feature = "test-support"))] pub fn test() -> Self { Self { session_id: uuid::Uuid::new_v4().to_string(), old_session_id: None, old_window_ids: None, } } #[cfg(any(test, feature = "test-support"))] pub fn test_with_old_session(old_session_id: String) -> Self { Self { session_id: uuid::Uuid::new_v4().to_string(), old_session_id: Some(old_session_id), old_window_ids: None, } } pub fn id(&self) -> &str { &self.session_id }} pub struct AppSession { session: Session, _serialization_task: Task<()>, _subscriptions: Vec<Subscription>,} impl AppSession { pub fn new(session: Session, cx: &Context<Self>) -> Self { let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)]; #[cfg(not(any(test, feature = "test-support")))] let _serialization_task = cx.spawn(async move |_, cx| { // Disabled in tests: the infinite loop bypasses "parking forbidden" checks, // causing tests to hang instead of panicking. { let mut current_window_stack = Vec::new(); loop { if let Some(windows) = cx.update(|cx| window_stack(cx)) && windows != current_window_stack { store_window_stack(&windows).await; current_window_stack = windows; } cx.background_executor() .timer(std::time::Duration::from_millis(500)) .await; } } }); #[cfg(any(test, feature = "test-support"))] let _serialization_task = Task::ready(()); Self { session, _subscriptions, _serialization_task, } } fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> { if let Some(window_stack) = window_stack(cx) { cx.background_spawn(async move { store_window_stack(&window_stack).await }) } else { Task::ready(()) } } pub fn id(&self) -> &str { self.session.id() } pub fn last_session_id(&self) -> Option<&str> { self.session.old_session_id.as_deref() } #[cfg(any(test, feature = "test-support"))] pub fn replace_session_for_test(&mut self, session: Session) { self.session = session; } pub fn last_session_window_stack(&self) -> Option<Vec<WindowId>> { self.session.old_window_ids.clone() }} fn window_stack(cx: &App) -> Option<Vec<u64>> { Some( cx.window_stack()? .into_iter() .map(|window| window.window_id().as_u64()) .collect(), )} async fn store_window_stack(windows: &[u64]) { if let Ok(window_ids_json) = serde_json::to_string(windows) { KEY_VALUE_STORE .write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json) .await .log_err(); }}
use db::kvp::KEY_VALUE_STORE;
use gpui::{App, AppContext as _, Context, Subscription, Task, WindowId};
use util::ResultExt;
pub struct Session {
session_id: String,
old_session_id: Option<String>,
old_window_ids: Option<Vec<WindowId>>,
}
const SESSION_ID_KEY: &str = "session_id";
const SESSION_WINDOW_STACK_KEY: &str = "session_window_stack";
impl Session {
pub async fn new(session_id: String) -> Self {
let old_session_id = KEY_VALUE_STORE.read_kvp(SESSION_ID_KEY).ok().flatten();
KEY_VALUE_STORE
.write_kvp(SESSION_ID_KEY.to_string(), session_id.clone())
.await
.log_err();
let old_window_ids = KEY_VALUE_STORE
.read_kvp(SESSION_WINDOW_STACK_KEY)
.ok()
.flatten()
.and_then(|json| serde_json::from_str::<Vec<u64>>(&json).ok())
.map(|vec| {
vec.into_iter()
.map(WindowId::from)
.collect::<Vec<WindowId>>()
});
Self {
session_id,
old_session_id,
old_window_ids,
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn test() -> Self {
Self {
session_id: uuid::Uuid::new_v4().to_string(),
old_session_id: None,
old_window_ids: None,
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn test_with_old_session(old_session_id: String) -> Self {
Self {
session_id: uuid::Uuid::new_v4().to_string(),
old_session_id: Some(old_session_id),
old_window_ids: None,
}
}
pub fn id(&self) -> &str {
&self.session_id
}
}
pub struct AppSession {
session: Session,
_serialization_task: Task<()>,
_subscriptions: Vec<Subscription>,
}
impl AppSession {
pub fn new(session: Session, cx: &Context<Self>) -> Self {
let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)];
#[cfg(not(any(test, feature = "test-support")))]
let _serialization_task = cx.spawn(async move |_, cx| {
// Disabled in tests: the infinite loop bypasses "parking forbidden" checks,
// causing tests to hang instead of panicking.
{
let mut current_window_stack = Vec::new();
loop {
if let Some(windows) = cx.update(|cx| window_stack(cx))
&& windows != current_window_stack
{
store_window_stack(&windows).await;
current_window_stack = windows;
}
cx.background_executor()
.timer(std::time::Duration::from_millis(500))
.await;
}
}
});
#[cfg(any(test, feature = "test-support"))]
let _serialization_task = Task::ready(());
Self {
session,
_subscriptions,
_serialization_task,
}
}
fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
if let Some(window_stack) = window_stack(cx) {
cx.background_spawn(async move { store_window_stack(&window_stack).await })
} else {
Task::ready(())
}
}
pub fn id(&self) -> &str {
self.session.id()
}
pub fn last_session_id(&self) -> Option<&str> {
self.session.old_session_id.as_deref()
}
#[cfg(any(test, feature = "test-support"))]
pub fn replace_session_for_test(&mut self, session: Session) {
self.session = session;
}
pub fn last_session_window_stack(&self) -> Option<Vec<WindowId>> {
self.session.old_window_ids.clone()
}
}
fn window_stack(cx: &App) -> Option<Vec<u64>> {
Some(
cx.window_stack()?
.into_iter()
.map(|window| window.window_id().as_u64())
.collect(),
)
}
async fn store_window_stack(windows: &[u64]) {
if let Ok(window_ids_json) = serde_json::to_string(windows) {
KEY_VALUE_STORE
.write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json)
.await
.log_err();
}
}