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