use coop_ui::{ button::{Button, ButtonCustomVariant, ButtonRounded, ButtonVariants}, dock::{DockArea, DockItem, DockPlacement}, theme::{ActiveTheme, Theme, ThemeMode}, ContextModal, IconName, Root, Sizable, TitleBar, }; use gpui::*; use prelude::FluentBuilder; use serde::Deserialize; use std::sync::Arc; use super::{ account::Account, dock::{chat::ChatPanel, left_dock::LeftDock, welcome::WelcomePanel}, onboarding::Onboarding, }; use crate::states::{account::AccountRegistry, chat::Room}; #[derive(Clone, PartialEq, Eq, Deserialize)] pub struct AddPanel { pub room: Arc, pub position: DockPlacement, } impl_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)); cx.observe_global::(move |view, cx| { if let Some(public_key) = cx.global::().get() { Self::init_layout(view.dock.downgrade(), cx); // TODO: save dock state and load previous state on startup let view = cx.new_view(|cx| Account::new(public_key, cx)); cx.update_model(&async_account, |model, cx| { *model = Some(view); cx.notify(); }); } }) .detach(); AppView { account, onboarding, dock, } } fn change_theme_mode(&mut self, _: &ClickEvent, cx: &mut ViewContext) { let mode = match cx.theme().mode.is_dark() { true => ThemeMode::Light, false => ThemeMode::Dark, }; // Change theme Theme::change(mode, cx); // Rerender cx.refresh(); } fn init_layout(dock_area: WeakView, cx: &mut WindowContext) { let left = DockItem::panel(Arc::new(LeftDock::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(260.)), 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) { let chat_panel = Arc::new(ChatPanel::new(&action.room, cx)); self.dock.update(cx, |dock_area, cx| { dock_area.add_panel(chat_panel, action.position, cx); }); } } 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_user_logged_in() { content = content .child( TitleBar::new() // Left side .child( div() .flex() .items_center() .gap_2() .child( div().when_some( self.account.read(cx).as_ref(), |this, account| this.child(account.clone()), ), ) .child( Button::new("new") .custom( ButtonCustomVariant::new(cx) .shadow(false) .color(cx.theme().primary) .border(cx.theme().primary) .foreground(cx.theme().primary_foreground) .active(cx.theme().primary_active) .hover(cx.theme().primary_hover), ) .xsmall() .rounded(ButtonRounded::Size(px(24.))) .label("Compose") .on_click(move |_, cx| { cx.open_modal(move |modal, _| { modal.title("Compose").child("TODO").min_h(px(300.)) }); }), ), ) // Right side .child( div() .flex() .items_center() .justify_end() .px_2() .gap_2() .child( Button::new("theme-mode") .map(|this| { if cx.theme().mode.is_dark() { this.icon(IconName::Sun) } else { this.icon(IconName::Moon) } }) .small() .ghost() .on_click(cx.listener(Self::change_theme_mode)), ), ), ) .child(self.dock.clone()) .on_action(cx.listener(Self::on_action_add_panel)) } else { content = content .child(TitleBar::new()) .child(self.onboarding.clone()) } div() .bg(cx.theme().background) .text_color(cx.theme().foreground) .size_full() .child(content) .child(div().absolute().top_8().children(notification_layer)) .children(modal_layer) } }