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 store_window_stack(&windows).await;
76 current_window_stack = windows;
77 }
78
79 cx.background_executor()
80 .timer(Duration::from_millis(500))
81 .await;
82 }
83 });
84
85 Self {
86 session,
87 _subscriptions,
88 _serialization_task,
89 }
90 }
91
92 fn app_will_quit(&mut self, cx: &mut Context<Self>) -> Task<()> {
93 if let Some(window_stack) = window_stack(cx) {
94 cx.background_spawn(async move { store_window_stack(&window_stack).await })
95 } else {
96 Task::ready(())
97 }
98 }
99
100 pub fn id(&self) -> &str {
101 self.session.id()
102 }
103
104 pub fn last_session_id(&self) -> Option<&str> {
105 self.session.old_session_id.as_deref()
106 }
107
108 pub fn last_session_window_stack(&self) -> Option<Vec<WindowId>> {
109 self.session.old_window_ids.clone()
110 }
111}
112
113fn window_stack(cx: &App) -> Option<Vec<u64>> {
114 Some(
115 cx.window_stack()?
116 .into_iter()
117 .map(|window| window.window_id().as_u64())
118 .collect(),
119 )
120}
121
122async fn store_window_stack(windows: &[u64]) {
123 if let Ok(window_ids_json) = serde_json::to_string(windows) {
124 KEY_VALUE_STORE
125 .write_kvp(SESSION_WINDOW_STACK_KEY.to_string(), window_ids_json)
126 .await
127 .log_err();
128 }
129}