use super::{ account::Account, chat::ChatPanel, onboarding::Onboarding, sidebar::Sidebar, welcome::WelcomePanel, }; use crate::states::{app::AppRegistry, chat::ChatRegistry}; use gpui::{ div, impl_internal_actions, px, svg, Axis, Context, Edges, InteractiveElement, IntoElement, Model, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView, WindowContext, }; use serde::Deserialize; use std::sync::Arc; use ui::{ dock_area::{dock::DockPlacement, DockArea, DockItem}, notification::NotificationType, theme::{scale::ColorScaleStep, ActiveTheme, Theme}, ContextModal, Root, TitleBar, }; #[derive(Clone, PartialEq, Eq, Deserialize)] pub enum PanelKind { Room(u64), } #[derive(Clone, PartialEq, Eq, Deserialize)] pub struct AddPanel { pub panel: PanelKind, pub position: DockPlacement, } impl_internal_actions!(dock, [AddPanel]); pub struct DockAreaTab { id: &'static str, version: usize, } pub const DOCK_AREA: DockAreaTab = DockAreaTab { id: "dock", version: 1, }; pub struct AppView { account: Model>>, onboarding: View, dock: View, } impl AppView { pub fn new(cx: &mut ViewContext<'_, Self>) -> AppView { // Sync theme with system cx.observe_window_appearance(|_, cx| { Theme::sync_system_appearance(cx); }) .detach(); // Account let account = cx.new_model(|_| None); let async_account = account.clone(); // Onboarding let onboarding = cx.new_view(Onboarding::new); // Dock let dock = cx.new_view(|cx| DockArea::new(DOCK_AREA.id, Some(DOCK_AREA.version), cx)); // Get current user from app state let current_user = cx.global::().current_user(); if let Some(current_user) = current_user.upgrade() { cx.observe(¤t_user, move |view, model, cx| { if let Some(public_key) = model.read(cx).clone().as_ref() { Self::init_layout(view.dock.downgrade(), cx); // TODO: save dock state and load previous state on startup let view = cx.new_view(|cx| { let view = Account::new(*public_key, cx); // Initial load metadata view.load_metadata(cx); view }); cx.update_model(&async_account, |model, cx| { *model = Some(view); cx.notify(); }); } }) .detach(); } AppView { account, onboarding, dock, } } fn init_layout(dock_area: WeakView, cx: &mut WindowContext) { let left = DockItem::panel(Arc::new(Sidebar::new(cx))); let center = Self::init_dock_items(&dock_area, cx); _ = dock_area.update(cx, |view, cx| { view.set_version(DOCK_AREA.version, cx); view.set_left_dock(left, Some(px(240.)), true, cx); view.set_center(center, cx); view.set_dock_collapsible( Edges { left: false, ..Default::default() }, cx, ); // TODO: support right dock? // TODO: support bottom dock? }); } fn init_dock_items(dock_area: &WeakView, cx: &mut WindowContext) -> DockItem { DockItem::split_with_sizes( Axis::Vertical, vec![DockItem::tabs( vec![Arc::new(WelcomePanel::new(cx))], None, dock_area, cx, )], vec![None], dock_area, cx, ) } fn on_action_add_panel(&mut self, action: &AddPanel, cx: &mut ViewContext) { match &action.panel { PanelKind::Room(id) => { if let Some(weak_room) = cx.global::().room(id, cx) { if let Some(room) = weak_room.upgrade() { let panel = Arc::new(ChatPanel::new(room, cx)); self.dock.update(cx, |dock_area, cx| { dock_area.add_panel(panel, action.position, cx); }); } else { cx.push_notification(( NotificationType::Error, "System error. Cannot open this chat room.", )); } } } }; } } impl Render for AppView { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let modal_layer = Root::render_modal_layer(cx); let notification_layer = Root::render_notification_layer(cx); let mut content = div().size_full().flex().flex_col(); if cx.global::().is_loading { content = content.child(div()).child( div().flex_1().flex().items_center().justify_center().child( svg() .path("brand/coop.svg") .size_12() .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), ), ) } else if let Some(account) = self.account.read(cx).as_ref() { content = content .child( TitleBar::new() // Left side .child(div()) // Right side .child( div() .flex() .items_center() .justify_end() .gap_1() .px_2() .child(account.clone()), ), ) .child(self.dock.clone()) .on_action(cx.listener(Self::on_action_add_panel)) } else { content = content .child(TitleBar::new()) .child(self.onboarding.clone()) } div() .size_full() .child(content) .child(div().absolute().top_8().children(notification_layer)) .children(modal_layer) } }