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