1use call::{room, ActiveCall};
2use client::User;
3use gpui::{
4 actions,
5 elements::*,
6 geometry::{rect::RectF, vector::vec2f},
7 Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds,
8 WindowKind, WindowOptions,
9};
10use project::Project;
11use settings::Settings;
12use std::sync::Arc;
13use workspace::{AppState, Workspace};
14
15actions!(project_shared_notification, [JoinProject, DismissProject]);
16
17pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
18 cx.add_action(ProjectSharedNotification::join);
19 cx.add_action(ProjectSharedNotification::dismiss);
20
21 let active_call = ActiveCall::global(cx);
22 let mut _room_subscription = None;
23 cx.observe(&active_call, move |active_call, cx| {
24 if let Some(room) = active_call.read(cx).room().cloned() {
25 let app_state = app_state.clone();
26 _room_subscription = Some(cx.subscribe(&room, move |_, event, cx| match event {
27 room::Event::RemoteProjectShared { owner, project_id } => {
28 cx.add_window(
29 WindowOptions {
30 bounds: WindowBounds::Fixed(RectF::new(
31 vec2f(0., 0.),
32 vec2f(300., 400.),
33 )),
34 titlebar: None,
35 center: true,
36 kind: WindowKind::PopUp,
37 is_movable: false,
38 },
39 |_| {
40 ProjectSharedNotification::new(
41 *project_id,
42 owner.clone(),
43 app_state.clone(),
44 )
45 },
46 );
47 }
48 }));
49 } else {
50 _room_subscription = None;
51 }
52 })
53 .detach();
54}
55
56pub struct ProjectSharedNotification {
57 project_id: u64,
58 owner: Arc<User>,
59 app_state: Arc<AppState>,
60}
61
62impl ProjectSharedNotification {
63 fn new(project_id: u64, owner: Arc<User>, app_state: Arc<AppState>) -> Self {
64 Self {
65 project_id,
66 owner,
67 app_state,
68 }
69 }
70
71 fn join(&mut self, _: &JoinProject, cx: &mut ViewContext<Self>) {
72 let project_id = self.project_id;
73 let app_state = self.app_state.clone();
74 cx.spawn_weak(|_, mut cx| async move {
75 let project = Project::remote(
76 project_id,
77 app_state.client.clone(),
78 app_state.user_store.clone(),
79 app_state.project_store.clone(),
80 app_state.languages.clone(),
81 app_state.fs.clone(),
82 cx.clone(),
83 )
84 .await?;
85
86 cx.add_window((app_state.build_window_options)(), |cx| {
87 let mut workspace = Workspace::new(project, app_state.default_item_factory, cx);
88 (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
89 workspace
90 });
91
92 anyhow::Ok(())
93 })
94 .detach_and_log_err(cx);
95
96 let window_id = cx.window_id();
97 cx.remove_window(window_id);
98 }
99
100 fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext<Self>) {
101 let window_id = cx.window_id();
102 cx.remove_window(window_id);
103 }
104
105 fn render_owner(&self, cx: &mut RenderContext<Self>) -> ElementBox {
106 let theme = &cx.global::<Settings>().theme.project_shared_notification;
107 Flex::row()
108 .with_children(
109 self.owner
110 .avatar
111 .clone()
112 .map(|avatar| Image::new(avatar).with_style(theme.owner_avatar).boxed()),
113 )
114 .with_child(
115 Label::new(
116 format!("{} has shared a new project", self.owner.github_login),
117 theme.message.text.clone(),
118 )
119 .boxed(),
120 )
121 .boxed()
122 }
123
124 fn render_buttons(&self, cx: &mut RenderContext<Self>) -> ElementBox {
125 enum Join {}
126 enum Dismiss {}
127
128 Flex::row()
129 .with_child(
130 MouseEventHandler::<Join>::new(0, cx, |_, cx| {
131 let theme = &cx.global::<Settings>().theme.project_shared_notification;
132 Label::new("Join".to_string(), theme.join_button.text.clone())
133 .contained()
134 .with_style(theme.join_button.container)
135 .boxed()
136 })
137 .on_click(MouseButton::Left, |_, cx| {
138 cx.dispatch_action(JoinProject);
139 })
140 .boxed(),
141 )
142 .with_child(
143 MouseEventHandler::<Dismiss>::new(0, cx, |_, cx| {
144 let theme = &cx.global::<Settings>().theme.project_shared_notification;
145 Label::new("Dismiss".to_string(), theme.dismiss_button.text.clone())
146 .contained()
147 .with_style(theme.dismiss_button.container)
148 .boxed()
149 })
150 .on_click(MouseButton::Left, |_, cx| {
151 cx.dispatch_action(DismissProject);
152 })
153 .boxed(),
154 )
155 .boxed()
156 }
157}
158
159impl Entity for ProjectSharedNotification {
160 type Event = ();
161}
162
163impl View for ProjectSharedNotification {
164 fn ui_name() -> &'static str {
165 "ProjectSharedNotification"
166 }
167
168 fn render(&mut self, cx: &mut RenderContext<Self>) -> gpui::ElementBox {
169 Flex::row()
170 .with_child(self.render_owner(cx))
171 .with_child(self.render_buttons(cx))
172 .boxed()
173 }
174}