1use crate::notification_window_options;
2use call::{room, ActiveCall};
3use client::User;
4use collections::HashMap;
5use gpui::{
6 img, px, AppContext, Div, ParentElement, Render, Size, Styled, ViewContext, VisualContext,
7};
8use settings::Settings;
9use std::sync::{Arc, Weak};
10use theme::ThemeSettings;
11use ui::{h_stack, prelude::*, v_stack, Button, Label};
12use workspace::AppState;
13
14pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
15 let app_state = Arc::downgrade(app_state);
16 let active_call = ActiveCall::global(cx);
17 let mut notification_windows = HashMap::default();
18 cx.subscribe(&active_call, move |_, event, cx| match event {
19 room::Event::RemoteProjectShared {
20 owner,
21 project_id,
22 worktree_root_names,
23 } => {
24 let window_size = Size {
25 width: px(400.),
26 height: px(96.),
27 };
28
29 for screen in cx.displays() {
30 let options = notification_window_options(screen, window_size);
31 let window = cx.open_window(options, |cx| {
32 cx.build_view(|_| {
33 ProjectSharedNotification::new(
34 owner.clone(),
35 *project_id,
36 worktree_root_names.clone(),
37 app_state.clone(),
38 )
39 })
40 });
41 notification_windows
42 .entry(*project_id)
43 .or_insert(Vec::new())
44 .push(window);
45 }
46 }
47
48 room::Event::RemoteProjectUnshared { project_id }
49 | room::Event::RemoteProjectJoined { project_id }
50 | room::Event::RemoteProjectInvitationDiscarded { project_id } => {
51 if let Some(windows) = notification_windows.remove(&project_id) {
52 for window in windows {
53 window
54 .update(cx, |_, cx| {
55 // todo!()
56 cx.remove_window();
57 })
58 .ok();
59 }
60 }
61 }
62
63 room::Event::Left => {
64 for (_, windows) in notification_windows.drain() {
65 for window in windows {
66 window
67 .update(cx, |_, cx| {
68 // todo!()
69 cx.remove_window();
70 })
71 .ok();
72 }
73 }
74 }
75 _ => {}
76 })
77 .detach();
78}
79
80pub struct ProjectSharedNotification {
81 project_id: u64,
82 worktree_root_names: Vec<String>,
83 owner: Arc<User>,
84 app_state: Weak<AppState>,
85}
86
87impl ProjectSharedNotification {
88 fn new(
89 owner: Arc<User>,
90 project_id: u64,
91 worktree_root_names: Vec<String>,
92 app_state: Weak<AppState>,
93 ) -> Self {
94 Self {
95 project_id,
96 worktree_root_names,
97 owner,
98 app_state,
99 }
100 }
101
102 fn join(&mut self, cx: &mut ViewContext<Self>) {
103 if let Some(app_state) = self.app_state.upgrade() {
104 workspace::join_remote_project(self.project_id, self.owner.id, app_state, cx)
105 .detach_and_log_err(cx);
106 }
107 }
108
109 fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
110 if let Some(active_room) =
111 ActiveCall::global(cx).read_with(cx, |call, _| call.room().cloned())
112 {
113 active_room.update(cx, |_, cx| {
114 cx.emit(room::Event::RemoteProjectInvitationDiscarded {
115 project_id: self.project_id,
116 });
117 });
118 }
119 }
120}
121
122impl Render for ProjectSharedNotification {
123 type Element = Div;
124
125 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
126 // TODO: Is there a better place for us to initialize the font?
127 let (ui_font, ui_font_size) = {
128 let theme_settings = ThemeSettings::get_global(cx);
129 (
130 theme_settings.ui_font.family.clone(),
131 theme_settings.ui_font_size.clone(),
132 )
133 };
134
135 cx.set_rem_size(ui_font_size);
136
137 h_stack()
138 .font(ui_font)
139 .text_ui()
140 .justify_between()
141 .size_full()
142 .elevation_3(cx)
143 .p_2()
144 .gap_2()
145 .child(
146 h_stack()
147 .gap_2()
148 .child(
149 img(self.owner.avatar_uri.clone())
150 .w_16()
151 .h_16()
152 .rounded_full(),
153 )
154 .child(
155 v_stack()
156 .child(Label::new(self.owner.github_login.clone()))
157 .child(Label::new(format!(
158 "is sharing a project in Zed{}",
159 if self.worktree_root_names.is_empty() {
160 ""
161 } else {
162 ":"
163 }
164 )))
165 .children(if self.worktree_root_names.is_empty() {
166 None
167 } else {
168 Some(Label::new(self.worktree_root_names.join(", ")))
169 }),
170 ),
171 )
172 .child(
173 v_stack()
174 .child(Button::new("open", "Open").on_click(cx.listener(
175 move |this, _event, cx| {
176 this.join(cx);
177 },
178 )))
179 .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
180 move |this, _event, cx| {
181 this.dismiss(cx);
182 },
183 ))),
184 )
185 }
186}