diff --git a/crates/onboarding/src/onboarding.rs b/crates/onboarding/src/onboarding.rs index 2e6025285c465793a994b6bb0896c86401909ca3..f7e76f2f3468ead00959f319071d1d0e363cb864 100644 --- a/crates/onboarding/src/onboarding.rs +++ b/crates/onboarding/src/onboarding.rs @@ -22,7 +22,7 @@ use workspace::{ dock::DockPosition, item::{Item, ItemEvent}, notifications::NotifyResultExt as _, - open_new, with_active_or_new_workspace, + open_new, register_serializable_item, with_active_or_new_workspace, }; mod ai_setup_page; @@ -197,6 +197,7 @@ pub fn init(cx: &mut App) { .detach(); }) .detach(); + register_serializable_item::(cx); } pub fn show_onboarding_view(app_state: Arc, cx: &mut App) -> Task> { @@ -247,6 +248,12 @@ impl Onboarding { }) } + fn set_page(&mut self, page: SelectedPage, cx: &mut Context) { + self.selected_page = page; + cx.notify(); + cx.emit(ItemEvent::UpdateTab); + } + fn render_nav_buttons( &mut self, window: &mut Window, @@ -306,8 +313,7 @@ impl Onboarding { IntoElement::into_any_element, )) .on_click(cx.listener(move |this, _, _, cx| { - this.selected_page = page; - cx.notify(); + this.set_page(page, cx); })) }) } @@ -470,16 +476,13 @@ impl Render for Onboarding { .size_full() .bg(cx.theme().colors().editor_background) .on_action(cx.listener(|this, _: &ActivateBasicsPage, _, cx| { - this.selected_page = SelectedPage::Basics; - cx.notify(); + this.set_page(SelectedPage::Basics, cx); })) .on_action(cx.listener(|this, _: &ActivateEditingPage, _, cx| { - this.selected_page = SelectedPage::Editing; - cx.notify(); + this.set_page(SelectedPage::Editing, cx); })) .on_action(cx.listener(|this, _: &ActivateAISetupPage, _, cx| { - this.selected_page = SelectedPage::AiSetup; - cx.notify(); + this.set_page(SelectedPage::AiSetup, cx); })) .child( h_flex() @@ -594,3 +597,125 @@ pub async fn handle_import_vscode_settings( }) .ok(); } + +impl workspace::SerializableItem for Onboarding { + fn serialized_item_kind() -> &'static str { + "OnboardingPage" + } + + fn cleanup( + workspace_id: workspace::WorkspaceId, + alive_items: Vec, + _window: &mut Window, + cx: &mut App, + ) -> gpui::Task> { + workspace::delete_unloaded_items( + alive_items, + workspace_id, + "onboarding_pages", + &persistence::ONBOARDING_PAGES, + cx, + ) + } + + fn deserialize( + _project: Entity, + workspace: WeakEntity, + workspace_id: workspace::WorkspaceId, + item_id: workspace::ItemId, + window: &mut Window, + cx: &mut App, + ) -> gpui::Task>> { + window.spawn(cx, async move |cx| { + if let Some(page_number) = + persistence::ONBOARDING_PAGES.get_onboarding_page(item_id, workspace_id)? + { + let page = match page_number { + 0 => Some(SelectedPage::Basics), + 1 => Some(SelectedPage::Editing), + 2 => Some(SelectedPage::AiSetup), + _ => None, + }; + workspace.update(cx, |workspace, cx| { + let onboarding_page = Onboarding::new(workspace, cx); + if let Some(page) = page { + zlog::info!("Onboarding page {page:?} loaded"); + onboarding_page.update(cx, |onboarding_page, cx| { + onboarding_page.set_page(page, cx); + }) + } + onboarding_page + }) + } else { + Err(anyhow::anyhow!("No onboarding page to deserialize")) + } + }) + } + + fn serialize( + &mut self, + workspace: &mut Workspace, + item_id: workspace::ItemId, + _closing: bool, + _window: &mut Window, + cx: &mut ui::Context, + ) -> Option>> { + let workspace_id = workspace.database_id()?; + let page_number = self.selected_page as u16; + Some(cx.background_spawn(async move { + persistence::ONBOARDING_PAGES + .save_onboarding_page(item_id, workspace_id, page_number) + .await + })) + } + + fn should_serialize(&self, event: &Self::Event) -> bool { + event == &ItemEvent::UpdateTab + } +} + +mod persistence { + use db::{define_connection, query, sqlez_macros::sql}; + use workspace::WorkspaceDb; + + define_connection! { + pub static ref ONBOARDING_PAGES: OnboardingPagesDb = + &[ + sql!( + CREATE TABLE onboarding_pages ( + workspace_id INTEGER, + item_id INTEGER UNIQUE, + page_number INTEGER, + + PRIMARY KEY(workspace_id, item_id), + FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) + ON DELETE CASCADE + ) STRICT; + ), + ]; + } + + impl OnboardingPagesDb { + query! { + pub async fn save_onboarding_page( + item_id: workspace::ItemId, + workspace_id: workspace::WorkspaceId, + page_number: u16 + ) -> Result<()> { + INSERT OR REPLACE INTO onboarding_pages(item_id, workspace_id, page_number) + VALUES (?, ?, ?) + } + } + + query! { + pub fn get_onboarding_page( + item_id: workspace::ItemId, + workspace_id: workspace::WorkspaceId + ) -> Result> { + SELECT page_number + FROM onboarding_pages + WHERE item_id = ? AND workspace_id = ? + } + } + } +}