1use crate::notification_window_options;
2use crate::notifications::collab_notification::CollabNotification;
3use call::{ActiveCall, room};
4use client::User;
5use collections::HashMap;
6use gpui::{App, Size};
7use std::sync::{Arc, Weak};
8
9use ui::{Button, Label, prelude::*};
10use util::ResultExt;
11use workspace::AppState;
12
13pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
14 let app_state = Arc::downgrade(app_state);
15 let active_call = ActiveCall::global(cx);
16 let mut notification_windows = HashMap::default();
17 cx.subscribe(&active_call, move |_, event, cx| match event {
18 room::Event::RemoteProjectShared {
19 owner,
20 project_id,
21 worktree_root_names,
22 } => {
23 let window_size = Size {
24 width: px(400.),
25 height: px(72.),
26 };
27
28 for screen in cx.displays() {
29 let options = notification_window_options(screen, window_size, cx);
30 let Some(window) = cx
31 .open_window(options, |_, cx| {
32 cx.new(|_| {
33 ProjectSharedNotification::new(
34 owner.clone(),
35 *project_id,
36 worktree_root_names.clone(),
37 app_state.clone(),
38 )
39 })
40 })
41 .log_err()
42 else {
43 continue;
44 };
45 notification_windows
46 .entry(*project_id)
47 .or_insert(Vec::new())
48 .push(window);
49 }
50 }
51
52 room::Event::RemoteProjectUnshared { project_id }
53 | room::Event::RemoteProjectJoined { project_id }
54 | room::Event::RemoteProjectInvitationDiscarded { project_id } => {
55 if let Some(windows) = notification_windows.remove(project_id) {
56 for window in windows {
57 window
58 .update(cx, |_, window, _| {
59 window.remove_window();
60 })
61 .ok();
62 }
63 }
64 }
65
66 room::Event::RoomLeft { .. } => {
67 for (_, windows) in notification_windows.drain() {
68 for window in windows {
69 window
70 .update(cx, |_, window, _| {
71 window.remove_window();
72 })
73 .ok();
74 }
75 }
76 }
77 _ => {}
78 })
79 .detach();
80}
81
82pub struct ProjectSharedNotification {
83 project_id: u64,
84 worktree_root_names: Vec<String>,
85 owner: Arc<User>,
86 app_state: Weak<AppState>,
87}
88
89impl ProjectSharedNotification {
90 fn new(
91 owner: Arc<User>,
92 project_id: u64,
93 worktree_root_names: Vec<String>,
94 app_state: Weak<AppState>,
95 ) -> Self {
96 Self {
97 project_id,
98 worktree_root_names,
99 owner,
100 app_state,
101 }
102 }
103
104 fn join(&mut self, cx: &mut Context<Self>) {
105 if let Some(app_state) = self.app_state.upgrade() {
106 workspace::join_in_room_project(self.project_id, self.owner.id, app_state, cx)
107 .detach_and_log_err(cx);
108 }
109 }
110
111 fn dismiss(&mut self, cx: &mut Context<Self>) {
112 if let Some(active_room) = ActiveCall::global(cx).read(cx).room().cloned() {
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 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
124 let ui_font = theme::setup_ui_font(window, cx);
125
126 div().size_full().font(ui_font).child(
127 CollabNotification::new(
128 self.owner.avatar_uri.clone(),
129 Button::new("open", "Open").on_click(cx.listener(move |this, _event, _, cx| {
130 this.join(cx);
131 })),
132 Button::new("dismiss", "Dismiss").on_click(cx.listener(
133 move |this, _event, _, cx| {
134 this.dismiss(cx);
135 },
136 )),
137 )
138 .child(Label::new(self.owner.github_login.clone()))
139 .child(Label::new(format!(
140 "is sharing a project in Zed{}",
141 if self.worktree_root_names.is_empty() {
142 ""
143 } else {
144 ":"
145 }
146 )))
147 .children(if self.worktree_root_names.is_empty() {
148 None
149 } else {
150 Some(Label::new(self.worktree_root_names.join(", ")))
151 }),
152 )
153 }
154}