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