session.rs

  1use db::kvp::KEY_VALUE_STORE;
  2use gpui::{App, AppContext as _, Context, Subscription, Task, WindowId};
  3use util::ResultExt;
  4
  5pub struct Session {
  6    session_id: String,
  7    old_session_id: Option<String>,
  8    old_window_ids: Option<Vec<WindowId>>,
  9}
 10
 11const SESSION_ID_KEY: &str = "session_id";
 12const SESSION_WINDOW_STACK_KEY: &str = "session_window_stack";
 13
 14impl Session {
 15    pub async fn new(session_id: String) -> Self {
 16        let old_session_id = KEY_VALUE_STORE.read_kvp(SESSION_ID_KEY).ok().flatten();
 17
 18        KEY_VALUE_STORE
 19            .write_kvp(SESSION_ID_KEY.to_string(), session_id.clone())
 20            .await
 21            .log_err();
 22
 23        let old_window_ids = KEY_VALUE_STORE
 24            .read_kvp(SESSION_WINDOW_STACK_KEY)
 25            .ok()
 26            .flatten()
 27            .and_then(|json| serde_json::from_str::<Vec<u64>>(&json).ok())
 28            .map(|vec| {
 29                vec.into_iter()
 30                    .map(WindowId::from)
 31                    .collect::<Vec<WindowId>>()
 32            });
 33
 34        Self {
 35            session_id,
 36            old_session_id,
 37            old_window_ids,
 38        }
 39    }
 40
 41    #[cfg(any(test, feature = "test-support"))]
 42    pub fn test() -> Self {
 43        Self {
 44            session_id: uuid::Uuid::new_v4().to_string(),
 45            old_session_id: None,
 46            old_window_ids: None,
 47        }
 48    }
 49
 50    pub fn id(&self) -> &str {
 51        &self.session_id
 52    }
 53}
 54
 55pub struct AppSession {
 56    session: Session,
 57    _serialization_task: Task<()>,
 58    _subscriptions: Vec<Subscription>,
 59}
 60
 61impl AppSession {
 62    pub fn new(session: Session, cx: &Context<Self>) -> Self {
 63        let _subscriptions = vec![cx.on_app_quit(Self::app_will_quit)];
 64
 65        #[cfg(not(any(test, feature = "test-support")))]
 66        let _serialization_task = cx.spawn(async move |_, cx| {
 67            // Disabled in tests: the infinite loop bypasses "parking forbidden" checks,
 68            // causing tests to hang instead of panicking.
 69            {
 70                let mut current_window_stack = Vec::new();
 71                loop {
 72                    if let Some(windows) = cx.update(|cx| window_stack(cx))
 73                        && windows != current_window_stack
 74                    {
 75                        store_window_stack(&windows).await;
 76                        current_window_stack = windows;
 77                    }
 78
 79                    cx.background_executor()
 80                        .timer(std::time::Duration::from_millis(500))
 81                        .await;
 82                }
 83            }
 84        });
 85
 86        #[cfg(any(test, feature = "test-support"))]
 87        let _serialization_task = Task::ready(());
 88
 89        Self {
 90            session,
 91            _subscriptions,
 92            _serialization_task,
 93        }
 94    }
 95
 96    fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
 97        if let Some(window_stack) = window_stack(cx) {
 98            cx.background_spawn(async move { store_window_stack(&window_stack).await })
 99        } else {
100            Task::ready(())
101        }
102    }
103
104    pub fn id(&self) -> &str {
105        self.session.id()
106    }
107
108    pub fn last_session_id(&self) -> Option<&str> {
109        self.session.old_session_id.as_deref()
110    }
111
112    pub fn last_session_window_stack(&self) -> Option<Vec<WindowId>> {
113        self.session.old_window_ids.clone()
114    }
115}
116
117fn window_stack(cx: &App) -> Option<Vec<u64>> {
118    Some(
119        cx.window_stack()?
120            .into_iter()
121            .map(|window| window.window_id().as_u64())
122            .collect(),
123    )
124}
125
126async fn store_window_stack(windows: &[u64]) {
127    if let Ok(window_ids_json) = serde_json::to_string(windows) {
128        KEY_VALUE_STORE
129            .write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json)
130            .await
131            .log_err();
132    }
133}