1use std::time::Duration;
2
3use db::kvp::KEY_VALUE_STORE;
4use gpui::{AnyWindowHandle, 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: Option<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 = Some(cx.spawn(async move |_, cx| {
71 loop {
72 if let Some(windows) = cx.update(|cx| cx.window_stack()).ok().flatten() {
73 store_window_stack(windows).await;
74 }
75
76 cx.background_executor()
77 .timer(Duration::from_millis(100))
78 .await;
79 }
80 }));
81
82 Self {
83 session,
84 _subscriptions,
85 _serialization_task,
86 }
87 }
88
89 fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
90 if let Some(windows) = cx.window_stack() {
91 cx.background_spawn(store_window_stack(windows))
92 } else {
93 Task::ready(())
94 }
95 }
96
97 pub fn id(&self) -> &str {
98 self.session.id()
99 }
100
101 pub fn last_session_id(&self) -> Option<&str> {
102 self.session.old_session_id.as_deref()
103 }
104
105 pub fn last_session_window_stack(&self) -> Option<Vec<WindowId>> {
106 self.session.old_window_ids.clone()
107 }
108}
109
110async fn store_window_stack(windows: Vec<AnyWindowHandle>) {
111 let window_ids = windows
112 .into_iter()
113 .map(|window| window.window_id().as_u64())
114 .collect::<Vec<_>>();
115
116 if let Ok(window_ids_json) = serde_json::to_string(&window_ids) {
117 KEY_VALUE_STORE
118 .write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json)
119 .await
120 .log_err();
121 }
122}