session.rs

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