project_shared_notification.rs

  1use crate::notification_window_options;
  2use call::{room, ActiveCall};
  3use client::User;
  4use collections::HashMap;
  5use gpui::{
  6    px, AppContext, Div, Element, ParentElement, Render, RenderOnce, Size, Styled, ViewContext,
  7    VisualContext,
  8};
  9use std::sync::{Arc, Weak};
 10use ui::{h_stack, v_stack, Avatar, Button, Clickable, 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(380.),
 25                height: px(64.),
 26            };
 27
 28            for screen in cx.displays() {
 29                let options = notification_window_options(screen, window_size);
 30                let window = cx.open_window(options, |cx| {
 31                    cx.build_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                            // todo!()
 55                            cx.remove_window();
 56                        })
 57                        .ok();
 58                }
 59            }
 60        }
 61
 62        room::Event::Left => {
 63            for (_, windows) in notification_windows.drain() {
 64                for window in windows {
 65                    window
 66                        .update(cx, |_, cx| {
 67                            // todo!()
 68                            cx.remove_window();
 69                        })
 70                        .ok();
 71                }
 72            }
 73        }
 74        _ => {}
 75    })
 76    .detach();
 77}
 78
 79pub struct ProjectSharedNotification {
 80    project_id: u64,
 81    worktree_root_names: Vec<String>,
 82    owner: Arc<User>,
 83    app_state: Weak<AppState>,
 84}
 85
 86impl ProjectSharedNotification {
 87    fn new(
 88        owner: Arc<User>,
 89        project_id: u64,
 90        worktree_root_names: Vec<String>,
 91        app_state: Weak<AppState>,
 92    ) -> Self {
 93        Self {
 94            project_id,
 95            worktree_root_names,
 96            owner,
 97            app_state,
 98        }
 99    }
100
101    fn join(&mut self, cx: &mut ViewContext<Self>) {
102        if let Some(app_state) = self.app_state.upgrade() {
103            workspace::join_remote_project(self.project_id, self.owner.id, app_state, cx)
104                .detach_and_log_err(cx);
105        }
106    }
107
108    fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
109        if let Some(active_room) =
110            ActiveCall::global(cx).read_with(cx, |call, _| call.room().cloned())
111        {
112            active_room.update(cx, |_, cx| {
113                cx.emit(room::Event::RemoteProjectInvitationDiscarded {
114                    project_id: self.project_id,
115                });
116            });
117        }
118    }
119
120    fn render_owner(&self) -> impl Element {
121        h_stack()
122            .child(Avatar::new(self.owner.avatar_uri.clone()))
123            .child(
124                v_stack()
125                    .child(Label::new(self.owner.github_login.clone()))
126                    .child(Label::new(format!(
127                        "is sharing a project in Zed{}",
128                        if self.worktree_root_names.is_empty() {
129                            ""
130                        } else {
131                            ":"
132                        }
133                    )))
134                    .children(if self.worktree_root_names.is_empty() {
135                        None
136                    } else {
137                        Some(Label::new(self.worktree_root_names.join(", ")))
138                    }),
139            )
140    }
141
142    fn render_buttons(&self, cx: &mut ViewContext<Self>) -> impl Element {
143        let this = cx.view().clone();
144        v_stack()
145            .child(Button::new("open", "Open").render(cx).on_click({
146                let this = this.clone();
147                move |_, cx| {
148                    this.update(cx, |this, cx| this.join(cx));
149                }
150            }))
151            .child(
152                Button::new("dismiss", "Dismiss")
153                    .render(cx)
154                    .on_click(move |_, cx| {
155                        this.update(cx, |this, cx| this.dismiss(cx));
156                    }),
157            )
158    }
159}
160
161impl Render for ProjectSharedNotification {
162    type Element = Div;
163
164    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
165        h_stack()
166            .size_full()
167            .bg(gpui::red())
168            .child(self.render_owner())
169            .child(self.render_buttons(cx))
170    }
171}