onboarding_ui.rs

  1#![allow(unused, dead_code)]
  2use command_palette_hooks::CommandPaletteFilter;
  3use feature_flags::FeatureFlagAppExt as _;
  4use gpui::{Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, actions, prelude::*};
  5use settings_ui::SettingsUiFeatureFlag;
  6use ui::prelude::*;
  7use workspace::Workspace;
  8
  9actions!(
 10    onboarding,
 11    [
 12        ShowOnboarding,
 13        JumpToBasics,
 14        JumpToEditing,
 15        JumpToAiSetup,
 16        JumpToWelcome,
 17        NextPage,
 18        PreviousPage,
 19        ToggleFocus,
 20        ResetOnboarding,
 21    ]
 22);
 23
 24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 25pub enum OnboardingPage {
 26    Basics,
 27    Editing,
 28    AiSetup,
 29    Welcome,
 30}
 31
 32impl OnboardingPage {
 33    fn next(&self) -> Option<Self> {
 34        match self {
 35            Self::Basics => Some(Self::Editing),
 36            Self::Editing => Some(Self::AiSetup),
 37            Self::AiSetup => Some(Self::Welcome),
 38            Self::Welcome => None,
 39        }
 40    }
 41
 42    fn previous(&self) -> Option<Self> {
 43        match self {
 44            Self::Basics => None,
 45            Self::Editing => Some(Self::Basics),
 46            Self::AiSetup => Some(Self::Editing),
 47            Self::Welcome => Some(Self::AiSetup),
 48        }
 49    }
 50}
 51
 52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 53pub enum OnboardingFocus {
 54    Navigation,
 55    Page,
 56}
 57
 58pub struct OnboardingUI {
 59    focus_handle: FocusHandle,
 60    current_page: OnboardingPage,
 61    current_focus: OnboardingFocus,
 62    completed_pages: [bool; 4],
 63    basics_page: Entity<BasicsPage>,
 64    editing_page: Entity<EditingPage>,
 65    ai_setup_page: Entity<AiSetupPage>,
 66    welcome_page: Entity<WelcomePage>,
 67}
 68
 69pub struct BasicsPage {
 70    focus_handle: FocusHandle,
 71    parent: WeakEntity<OnboardingUI>,
 72}
 73
 74pub struct EditingPage {
 75    focus_handle: FocusHandle,
 76    parent: WeakEntity<OnboardingUI>,
 77}
 78
 79pub struct AiSetupPage {
 80    focus_handle: FocusHandle,
 81    parent: WeakEntity<OnboardingUI>,
 82}
 83
 84pub struct WelcomePage {
 85    focus_handle: FocusHandle,
 86    parent: WeakEntity<OnboardingUI>,
 87}
 88
 89// Event types for communication between pages and main UI
 90#[derive(Clone)]
 91pub enum OnboardingEvent {
 92    PageCompleted(OnboardingPage),
 93}
 94
 95// Implement EventEmitter for all entities
 96impl EventEmitter<OnboardingEvent> for OnboardingUI {}
 97impl EventEmitter<OnboardingEvent> for BasicsPage {}
 98impl EventEmitter<OnboardingEvent> for EditingPage {}
 99impl EventEmitter<OnboardingEvent> for AiSetupPage {}
100impl EventEmitter<OnboardingEvent> for WelcomePage {}
101
102// Implement Focusable for all entities
103impl Focusable for OnboardingUI {
104    fn focus_handle(&self, _cx: &App) -> FocusHandle {
105        self.focus_handle.clone()
106    }
107}
108
109impl Focusable for BasicsPage {
110    fn focus_handle(&self, _cx: &App) -> FocusHandle {
111        self.focus_handle.clone()
112    }
113}
114
115impl Focusable for EditingPage {
116    fn focus_handle(&self, _cx: &App) -> FocusHandle {
117        self.focus_handle.clone()
118    }
119}
120
121impl Focusable for AiSetupPage {
122    fn focus_handle(&self, _cx: &App) -> FocusHandle {
123        self.focus_handle.clone()
124    }
125}
126
127impl Focusable for WelcomePage {
128    fn focus_handle(&self, _cx: &App) -> FocusHandle {
129        self.focus_handle.clone()
130    }
131}
132
133// Placeholder Render implementations
134impl Render for OnboardingUI {
135    fn render(
136        &mut self,
137        _window: &mut gpui::Window,
138        _cx: &mut Context<Self>,
139    ) -> impl gpui::IntoElement {
140        h_flex()
141            .w(px(904.))
142            .h(px(500.))
143            .gap(px(48.))
144            .child(v_flex().h_full().w(px(256.)).child("nav"))
145    }
146}
147
148impl Render for BasicsPage {
149    fn render(
150        &mut self,
151        _window: &mut gpui::Window,
152        _cx: &mut Context<Self>,
153    ) -> impl gpui::IntoElement {
154        gpui::div()
155    }
156}
157
158impl Render for EditingPage {
159    fn render(
160        &mut self,
161        _window: &mut gpui::Window,
162        _cx: &mut Context<Self>,
163    ) -> impl gpui::IntoElement {
164        gpui::div()
165    }
166}
167
168impl Render for AiSetupPage {
169    fn render(
170        &mut self,
171        _window: &mut gpui::Window,
172        _cx: &mut Context<Self>,
173    ) -> impl gpui::IntoElement {
174        gpui::div()
175    }
176}
177
178impl Render for WelcomePage {
179    fn render(
180        &mut self,
181        _window: &mut gpui::Window,
182        _cx: &mut Context<Self>,
183    ) -> impl gpui::IntoElement {
184        gpui::div()
185    }
186}
187
188impl OnboardingUI {
189    pub fn new(cx: &mut Context<Self>) -> Self {
190        let parent_handle = cx.entity().downgrade();
191
192        let basics_page = cx.new(|cx| BasicsPage {
193            focus_handle: cx.focus_handle(),
194            parent: parent_handle.clone(),
195        });
196
197        let editing_page = cx.new(|cx| EditingPage {
198            focus_handle: cx.focus_handle(),
199            parent: parent_handle.clone(),
200        });
201
202        let ai_setup_page = cx.new(|cx| AiSetupPage {
203            focus_handle: cx.focus_handle(),
204            parent: parent_handle.clone(),
205        });
206
207        let welcome_page = cx.new(|cx| WelcomePage {
208            focus_handle: cx.focus_handle(),
209            parent: parent_handle.clone(),
210        });
211
212        Self {
213            focus_handle: cx.focus_handle(),
214            current_page: OnboardingPage::Basics,
215            current_focus: OnboardingFocus::Page,
216            completed_pages: [false; 4],
217            basics_page,
218            editing_page,
219            ai_setup_page,
220            welcome_page,
221        }
222    }
223
224    fn jump_to_page(
225        &mut self,
226        page: OnboardingPage,
227        _window: &mut gpui::Window,
228        cx: &mut Context<Self>,
229    ) {
230        self.current_page = page;
231        cx.notify();
232    }
233
234    fn next_page(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) {
235        if let Some(next) = self.current_page.next() {
236            self.current_page = next;
237            cx.notify();
238        }
239    }
240
241    fn previous_page(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) {
242        if let Some(prev) = self.current_page.previous() {
243            self.current_page = prev;
244            cx.notify();
245        }
246    }
247
248    fn toggle_focus(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) {
249        self.current_focus = match self.current_focus {
250            OnboardingFocus::Navigation => OnboardingFocus::Page,
251            OnboardingFocus::Page => OnboardingFocus::Navigation,
252        };
253        cx.notify();
254    }
255
256    fn reset(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) {
257        self.current_page = OnboardingPage::Basics;
258        self.current_focus = OnboardingFocus::Page;
259        self.completed_pages = [false; 4];
260        cx.notify();
261    }
262
263    fn mark_page_completed(
264        &mut self,
265        page: OnboardingPage,
266        _window: &mut gpui::Window,
267        cx: &mut Context<Self>,
268    ) {
269        let index = match page {
270            OnboardingPage::Basics => 0,
271            OnboardingPage::Editing => 1,
272            OnboardingPage::AiSetup => 2,
273            OnboardingPage::Welcome => 3,
274        };
275        self.completed_pages[index] = true;
276        cx.notify();
277    }
278}
279
280pub fn init(cx: &mut App) {
281    cx.observe_new(|workspace: &mut Workspace, _, _cx| {
282        workspace.register_action(|_workspace, _: &ShowOnboarding, _window, _cx| {
283            // Show onboarding implementation will go here
284        });
285
286        workspace.register_action(|_workspace, _: &JumpToBasics, _window, _cx| {
287            // Jump to basics implementation
288        });
289
290        workspace.register_action(|_workspace, _: &JumpToEditing, _window, _cx| {
291            // Jump to editing implementation
292        });
293
294        workspace.register_action(|_workspace, _: &JumpToAiSetup, _window, _cx| {
295            // Jump to AI setup implementation
296        });
297
298        workspace.register_action(|_workspace, _: &JumpToWelcome, _window, _cx| {
299            // Jump to welcome implementation
300        });
301
302        workspace.register_action(|_workspace, _: &NextPage, _window, _cx| {
303            // Next page implementation
304        });
305
306        workspace.register_action(|_workspace, _: &PreviousPage, _window, _cx| {
307            // Previous page implementation
308        });
309
310        workspace.register_action(|_workspace, _: &ToggleFocus, _window, _cx| {
311            // Toggle focus implementation
312        });
313
314        workspace.register_action(|_workspace, _: &ResetOnboarding, _window, _cx| {
315            // Reset onboarding implementation
316        });
317    })
318    .detach();
319
320    feature_gate_onboarding_ui_actions(cx);
321}
322
323fn feature_gate_onboarding_ui_actions(cx: &mut App) {
324    const ONBOARDING_ACTION_NAMESPACE: &str = "onboarding";
325
326    CommandPaletteFilter::update_global(cx, |filter, _cx| {
327        filter.hide_namespace(ONBOARDING_ACTION_NAMESPACE);
328    });
329
330    cx.observe_flag::<SettingsUiFeatureFlag, _>({
331        move |is_enabled, cx| {
332            CommandPaletteFilter::update_global(cx, |filter, _cx| {
333                if is_enabled {
334                    filter.show_namespace(ONBOARDING_ACTION_NAMESPACE);
335                } else {
336                    filter.hide_namespace(ONBOARDING_ACTION_NAMESPACE);
337                }
338            });
339        }
340    })
341    .detach();
342}