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}