project_shared_notification.rs

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