project_shared_notification.rs

  1use crate::notification_window_options;
  2use crate::notifications::collab_notification::CollabNotification;
  3use call::{room, ActiveCall};
  4use client::User;
  5use collections::HashMap;
  6use gpui::{AppContext, Size};
  7use settings::Settings;
  8use std::sync::{Arc, Weak};
  9use theme::ThemeSettings;
 10use ui::{prelude::*, Button, Label};
 11use util::ResultExt;
 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(72.),
 27            };
 28
 29            for screen in cx.displays() {
 30                let options = notification_window_options(screen, window_size, cx);
 31                let Some(window) = cx
 32                    .open_window(options, |cx| {
 33                        cx.new_view(|_| {
 34                            ProjectSharedNotification::new(
 35                                owner.clone(),
 36                                *project_id,
 37                                worktree_root_names.clone(),
 38                                app_state.clone(),
 39                            )
 40                        })
 41                    })
 42                    .log_err()
 43                else {
 44                    continue;
 45                };
 46                notification_windows
 47                    .entry(*project_id)
 48                    .or_insert(Vec::new())
 49                    .push(window);
 50            }
 51        }
 52
 53        room::Event::RemoteProjectUnshared { project_id }
 54        | room::Event::RemoteProjectJoined { project_id }
 55        | room::Event::RemoteProjectInvitationDiscarded { project_id } => {
 56            if let Some(windows) = notification_windows.remove(&project_id) {
 57                for window in windows {
 58                    window
 59                        .update(cx, |_, cx| {
 60                            cx.remove_window();
 61                        })
 62                        .ok();
 63                }
 64            }
 65        }
 66
 67        room::Event::RoomLeft { .. } => {
 68            for (_, windows) in notification_windows.drain() {
 69                for window in windows {
 70                    window
 71                        .update(cx, |_, cx| {
 72                            cx.remove_window();
 73                        })
 74                        .ok();
 75                }
 76            }
 77        }
 78        _ => {}
 79    })
 80    .detach();
 81}
 82
 83pub struct ProjectSharedNotification {
 84    project_id: u64,
 85    worktree_root_names: Vec<String>,
 86    owner: Arc<User>,
 87    app_state: Weak<AppState>,
 88}
 89
 90impl ProjectSharedNotification {
 91    fn new(
 92        owner: Arc<User>,
 93        project_id: u64,
 94        worktree_root_names: Vec<String>,
 95        app_state: Weak<AppState>,
 96    ) -> Self {
 97        Self {
 98            project_id,
 99            worktree_root_names,
100            owner,
101            app_state,
102        }
103    }
104
105    fn join(&mut self, cx: &mut ViewContext<Self>) {
106        if let Some(app_state) = self.app_state.upgrade() {
107            workspace::join_in_room_project(self.project_id, self.owner.id, app_state, cx)
108                .detach_and_log_err(cx);
109        }
110    }
111
112    fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
113        if let Some(active_room) =
114            ActiveCall::global(cx).read_with(cx, |call, _| call.room().cloned())
115        {
116            active_room.update(cx, |_, cx| {
117                cx.emit(room::Event::RemoteProjectInvitationDiscarded {
118                    project_id: self.project_id,
119                });
120            });
121        }
122    }
123}
124
125impl Render for ProjectSharedNotification {
126    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
127        // TODO: Is there a better place for us to initialize the font?
128        let (ui_font, ui_font_size) = {
129            let theme_settings = ThemeSettings::get_global(cx);
130            (theme_settings.ui_font.clone(), theme_settings.ui_font_size)
131        };
132
133        cx.set_rem_size(ui_font_size);
134
135        div().size_full().font(ui_font).child(
136            CollabNotification::new(
137                self.owner.avatar_uri.clone(),
138                Button::new("open", "Open").on_click(cx.listener(move |this, _event, cx| {
139                    this.join(cx);
140                })),
141                Button::new("dismiss", "Dismiss").on_click(cx.listener(move |this, _event, cx| {
142                    this.dismiss(cx);
143                })),
144            )
145            .child(Label::new(self.owner.github_login.clone()))
146            .child(Label::new(format!(
147                "is sharing a project in Zed{}",
148                if self.worktree_root_names.is_empty() {
149                    ""
150                } else {
151                    ":"
152                }
153            )))
154            .children(if self.worktree_root_names.is_empty() {
155                None
156            } else {
157                Some(Label::new(self.worktree_root_names.join(", ")))
158            }),
159        )
160    }
161}