project_shared_notification.rs

  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    const 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}