project_shared_notification.rs

  1use crate::notification_window_options;
  2use call::{room, ActiveCall};
  3use client::User;
  4use collections::HashMap;
  5use gpui::{
  6    img, px, AppContext, Div, ParentElement, Render, Size, Styled, ViewContext, VisualContext,
  7};
  8use settings::Settings;
  9use std::sync::{Arc, Weak};
 10use theme::ThemeSettings;
 11use ui::{h_stack, prelude::*, v_stack, Button, Label};
 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(96.),
 27            };
 28
 29            for screen in cx.displays() {
 30                let options = notification_window_options(screen, window_size);
 31                let window = cx.open_window(options, |cx| {
 32                    cx.build_view(|_| {
 33                        ProjectSharedNotification::new(
 34                            owner.clone(),
 35                            *project_id,
 36                            worktree_root_names.clone(),
 37                            app_state.clone(),
 38                        )
 39                    })
 40                });
 41                notification_windows
 42                    .entry(*project_id)
 43                    .or_insert(Vec::new())
 44                    .push(window);
 45            }
 46        }
 47
 48        room::Event::RemoteProjectUnshared { project_id }
 49        | room::Event::RemoteProjectJoined { project_id }
 50        | room::Event::RemoteProjectInvitationDiscarded { project_id } => {
 51            if let Some(windows) = notification_windows.remove(&project_id) {
 52                for window in windows {
 53                    window
 54                        .update(cx, |_, cx| {
 55                            // todo!()
 56                            cx.remove_window();
 57                        })
 58                        .ok();
 59                }
 60            }
 61        }
 62
 63        room::Event::Left => {
 64            for (_, windows) in notification_windows.drain() {
 65                for window in windows {
 66                    window
 67                        .update(cx, |_, cx| {
 68                            // todo!()
 69                            cx.remove_window();
 70                        })
 71                        .ok();
 72                }
 73            }
 74        }
 75        _ => {}
 76    })
 77    .detach();
 78}
 79
 80pub struct ProjectSharedNotification {
 81    project_id: u64,
 82    worktree_root_names: Vec<String>,
 83    owner: Arc<User>,
 84    app_state: Weak<AppState>,
 85}
 86
 87impl ProjectSharedNotification {
 88    fn new(
 89        owner: Arc<User>,
 90        project_id: u64,
 91        worktree_root_names: Vec<String>,
 92        app_state: Weak<AppState>,
 93    ) -> Self {
 94        Self {
 95            project_id,
 96            worktree_root_names,
 97            owner,
 98            app_state,
 99        }
100    }
101
102    fn join(&mut self, cx: &mut ViewContext<Self>) {
103        if let Some(app_state) = self.app_state.upgrade() {
104            workspace::join_remote_project(self.project_id, self.owner.id, app_state, cx)
105                .detach_and_log_err(cx);
106        }
107    }
108
109    fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
110        if let Some(active_room) =
111            ActiveCall::global(cx).read_with(cx, |call, _| call.room().cloned())
112        {
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    type Element = Div;
124
125    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
126        // TODO: Is there a better place for us to initialize the font?
127        let (ui_font, ui_font_size) = {
128            let theme_settings = ThemeSettings::get_global(cx);
129            (
130                theme_settings.ui_font.family.clone(),
131                theme_settings.ui_font_size.clone(),
132            )
133        };
134
135        cx.set_rem_size(ui_font_size);
136
137        h_stack()
138            .font(ui_font)
139            .text_ui()
140            .justify_between()
141            .size_full()
142            .elevation_3(cx)
143            .p_2()
144            .gap_2()
145            .child(
146                h_stack()
147                    .gap_2()
148                    .child(
149                        img(self.owner.avatar_uri.clone())
150                            .w_16()
151                            .h_16()
152                            .rounded_full(),
153                    )
154                    .child(
155                        v_stack()
156                            .child(Label::new(self.owner.github_login.clone()))
157                            .child(Label::new(format!(
158                                "is sharing a project in Zed{}",
159                                if self.worktree_root_names.is_empty() {
160                                    ""
161                                } else {
162                                    ":"
163                                }
164                            )))
165                            .children(if self.worktree_root_names.is_empty() {
166                                None
167                            } else {
168                                Some(Label::new(self.worktree_root_names.join(", ")))
169                            }),
170                    ),
171            )
172            .child(
173                v_stack()
174                    .child(Button::new("open", "Open").on_click(cx.listener(
175                        move |this, _event, cx| {
176                            this.join(cx);
177                        },
178                    )))
179                    .child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
180                        move |this, _event, cx| {
181                            this.dismiss(cx);
182                        },
183                    ))),
184            )
185    }
186}