project_shared_notification.rs

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