From debedaf004be2c14af5714afd3a7ef8ef8dc8489 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 16:55:41 +0200 Subject: [PATCH] Show notification when a new project is shared and allow joining it --- crates/collab_ui/src/collab_ui.rs | 11 +- .../src/project_shared_notification.rs | 174 ++++++++++++++++++ crates/theme/src/theme.rs | 9 + crates/zed/src/main.rs | 2 +- styles/src/styleTree/app.ts | 2 + .../styleTree/projectSharedNotification.ts | 22 +++ 6 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 crates/collab_ui/src/project_shared_notification.rs create mode 100644 styles/src/styleTree/projectSharedNotification.ts diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 4bb08607047ed43507d86da53765579e5646faf6..ccf17974a4512c2d7778cc73fa119fcf14970160 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,13 +1,16 @@ mod collab_titlebar_item; mod contacts_popover; mod incoming_call_notification; +mod project_shared_notification; -use client::UserStore; pub use collab_titlebar_item::CollabTitlebarItem; -use gpui::{ModelHandle, MutableAppContext}; +use gpui::MutableAppContext; +use std::sync::Arc; +use workspace::AppState; -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(user_store, cx); + incoming_call_notification::init(app_state.user_store.clone(), cx); + project_shared_notification::init(app_state, cx); } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs new file mode 100644 index 0000000000000000000000000000000000000000..879c3ca28cbcf2dae621d031b08ce5568bc88248 --- /dev/null +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -0,0 +1,174 @@ +use call::{room, ActiveCall}; +use client::User; +use gpui::{ + actions, + elements::*, + geometry::{rect::RectF, vector::vec2f}, + Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds, + WindowKind, WindowOptions, +}; +use project::Project; +use settings::Settings; +use std::sync::Arc; +use workspace::{AppState, Workspace}; + +actions!(project_shared_notification, [JoinProject, DismissProject]); + +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + cx.add_action(ProjectSharedNotification::join); + cx.add_action(ProjectSharedNotification::dismiss); + + let active_call = ActiveCall::global(cx); + let mut _room_subscription = None; + cx.observe(&active_call, move |active_call, cx| { + if let Some(room) = active_call.read(cx).room().cloned() { + let app_state = app_state.clone(); + _room_subscription = Some(cx.subscribe(&room, move |_, event, cx| match event { + room::Event::RemoteProjectShared { owner, project_id } => { + cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new( + vec2f(0., 0.), + vec2f(300., 400.), + )), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| { + ProjectSharedNotification::new( + *project_id, + owner.clone(), + app_state.clone(), + ) + }, + ); + } + })); + } else { + _room_subscription = None; + } + }) + .detach(); +} + +pub struct ProjectSharedNotification { + project_id: u64, + owner: Arc, + app_state: Arc, +} + +impl ProjectSharedNotification { + fn new(project_id: u64, owner: Arc, app_state: Arc) -> Self { + Self { + project_id, + owner, + app_state, + } + } + + fn join(&mut self, _: &JoinProject, cx: &mut ViewContext) { + let project_id = self.project_id; + let app_state = self.app_state.clone(); + cx.spawn_weak(|_, mut cx| async move { + let project = Project::remote( + project_id, + app_state.client.clone(), + app_state.user_store.clone(), + app_state.project_store.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx.clone(), + ) + .await?; + + cx.add_window((app_state.build_window_options)(), |cx| { + let mut workspace = Workspace::new(project, app_state.default_item_factory, cx); + (app_state.initialize_workspace)(&mut workspace, &app_state, cx); + workspace + }); + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext) { + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn render_owner(&self, cx: &mut RenderContext) -> ElementBox { + let theme = &cx.global::().theme.project_shared_notification; + Flex::row() + .with_children( + self.owner + .avatar + .clone() + .map(|avatar| Image::new(avatar).with_style(theme.owner_avatar).boxed()), + ) + .with_child( + Label::new( + format!("{} has shared a new project", self.owner.github_login), + theme.message.text.clone(), + ) + .boxed(), + ) + .boxed() + } + + fn render_buttons(&self, cx: &mut RenderContext) -> ElementBox { + enum Join {} + enum Dismiss {} + + Flex::row() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.project_shared_notification; + Label::new("Join".to_string(), theme.join_button.text.clone()) + .contained() + .with_style(theme.join_button.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(JoinProject); + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.project_shared_notification; + Label::new("Dismiss".to_string(), theme.dismiss_button.text.clone()) + .contained() + .with_style(theme.dismiss_button.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(DismissProject); + }) + .boxed(), + ) + .boxed() + } +} + +impl Entity for ProjectSharedNotification { + type Event = (); +} + +impl View for ProjectSharedNotification { + fn ui_name() -> &'static str { + "ProjectSharedNotification" + } + + fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + Flex::row() + .with_child(self.render_owner(cx)) + .with_child(self.render_buttons(cx)) + .boxed() + } +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 52b34462aa992b11431ccd944f51e14e0d634e65..6253b6dabbc129442097fbbb63c7fd3607f9a8aa 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -30,6 +30,7 @@ pub struct Theme { pub breadcrumbs: ContainedText, pub contact_notification: ContactNotification, pub update_notification: UpdateNotification, + pub project_shared_notification: ProjectSharedNotification, pub tooltip: TooltipStyle, pub terminal: TerminalStyle, } @@ -481,6 +482,14 @@ pub struct UpdateNotification { pub dismiss_button: Interactive, } +#[derive(Deserialize, Default)] +pub struct ProjectSharedNotification { + pub owner_avatar: ImageStyle, + pub message: ContainedText, + pub join_button: ContainedText, + pub dismiss_button: ContainedText, +} + #[derive(Clone, Deserialize, Default)] pub struct Editor { pub text_color: Color, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index ea42c61dfba1fd51c0646a0be5f671bbcf16b679..580493f6d0f321c934c3c71b538fb579304f701e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,6 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); @@ -157,6 +156,7 @@ fn main() { journal::init(app_state.clone(), cx); theme_selector::init(app_state.clone(), cx); zed::init(&app_state, cx); + collab_ui::init(app_state.clone(), cx); cx.set_menus(menus::menus()); diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index a3ab4b654c6f6d2835804c834dab6a7435cd75f3..1c0c81cfde950a7e89f3e9546955d2ea8cb3f6eb 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -14,6 +14,7 @@ import contextMenu from "./contextMenu"; import projectDiagnostics from "./projectDiagnostics"; import contactNotification from "./contactNotification"; import updateNotification from "./updateNotification"; +import projectSharedNotification from "./projectSharedNotification"; import tooltip from "./tooltip"; import terminal from "./terminal"; @@ -47,6 +48,7 @@ export default function app(theme: Theme): Object { }, contactNotification: contactNotification(theme), updateNotification: updateNotification(theme), + projectSharedNotification: projectSharedNotification(theme), tooltip: tooltip(theme), terminal: terminal(theme), }; diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc342651358fac5627ec71f718e5322f3b03d891 --- /dev/null +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -0,0 +1,22 @@ +import Theme from "../themes/common/theme"; +import { text } from "./components"; + +export default function projectSharedNotification(theme: Theme): Object { + const avatarSize = 12; + return { + ownerAvatar: { + height: avatarSize, + width: avatarSize, + cornerRadius: 6, + }, + message: { + ...text(theme, "sans", "primary", { size: "xs" }), + }, + joinButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + dismissButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + }; +}