collab_ui.rs

  1pub mod channel_view;
  2pub mod chat_panel;
  3pub mod collab_panel;
  4mod collab_titlebar_item;
  5mod face_pile;
  6pub mod notification_panel;
  7pub mod notifications;
  8mod panel_settings;
  9
 10use call::{report_call_event_for_room, ActiveCall, Room};
 11use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
 12use gpui::{
 13    actions,
 14    elements::{ContainerStyle, Empty, Image},
 15    geometry::{
 16        rect::RectF,
 17        vector::{vec2f, Vector2F},
 18    },
 19    platform::{Screen, WindowBounds, WindowKind, WindowOptions},
 20    AnyElement, AppContext, Element, ImageData, Task,
 21};
 22use std::{rc::Rc, sync::Arc};
 23use theme::AvatarStyle;
 24use util::ResultExt;
 25use workspace::AppState;
 26
 27pub use collab_titlebar_item::CollabTitlebarItem;
 28pub use panel_settings::{
 29    ChatPanelSettings, CollaborationPanelSettings, NotificationPanelSettings,
 30};
 31
 32actions!(
 33    collab,
 34    [ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
 35);
 36
 37pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
 38    settings::register::<CollaborationPanelSettings>(cx);
 39    settings::register::<ChatPanelSettings>(cx);
 40    settings::register::<NotificationPanelSettings>(cx);
 41
 42    vcs_menu::init(cx);
 43    collab_titlebar_item::init(cx);
 44    collab_panel::init(cx);
 45    chat_panel::init(cx);
 46    notifications::init(&app_state, cx);
 47
 48    cx.add_global_action(toggle_screen_sharing);
 49    cx.add_global_action(toggle_mute);
 50    cx.add_global_action(toggle_deafen);
 51}
 52
 53pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
 54    let call = ActiveCall::global(cx).read(cx);
 55    if let Some(room) = call.room().cloned() {
 56        let client = call.client();
 57        let toggle_screen_sharing = room.update(cx, |room, cx| {
 58            if room.is_screen_sharing() {
 59                report_call_event_for_room(
 60                    "disable screen share",
 61                    room.id(),
 62                    room.channel_id(),
 63                    &client,
 64                    cx,
 65                );
 66                Task::ready(room.unshare_screen(cx))
 67            } else {
 68                report_call_event_for_room(
 69                    "enable screen share",
 70                    room.id(),
 71                    room.channel_id(),
 72                    &client,
 73                    cx,
 74                );
 75                room.share_screen(cx)
 76            }
 77        });
 78        toggle_screen_sharing.detach_and_log_err(cx);
 79    }
 80}
 81
 82pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
 83    let call = ActiveCall::global(cx).read(cx);
 84    if let Some(room) = call.room().cloned() {
 85        let client = call.client();
 86        room.update(cx, |room, cx| {
 87            let operation = if room.is_muted(cx) {
 88                "enable microphone"
 89            } else {
 90                "disable microphone"
 91            };
 92            report_call_event_for_room(operation, room.id(), room.channel_id(), &client, cx);
 93
 94            room.toggle_mute(cx)
 95        })
 96        .map(|task| task.detach_and_log_err(cx))
 97        .log_err();
 98    }
 99}
100
101pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
102    if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
103        room.update(cx, Room::toggle_deafen)
104            .map(|task| task.detach_and_log_err(cx))
105            .log_err();
106    }
107}
108
109fn notification_window_options(
110    screen: Rc<dyn Screen>,
111    window_size: Vector2F,
112) -> WindowOptions<'static> {
113    const NOTIFICATION_PADDING: f32 = 16.;
114
115    let screen_bounds = screen.content_bounds();
116    WindowOptions {
117        bounds: WindowBounds::Fixed(RectF::new(
118            screen_bounds.upper_right()
119                + vec2f(
120                    -NOTIFICATION_PADDING - window_size.x(),
121                    NOTIFICATION_PADDING,
122                ),
123            window_size,
124        )),
125        titlebar: None,
126        center: false,
127        focus: false,
128        show: true,
129        kind: WindowKind::PopUp,
130        is_movable: false,
131        screen: Some(screen),
132    }
133}
134
135fn render_avatar<T: 'static>(
136    avatar: Option<Arc<ImageData>>,
137    avatar_style: &AvatarStyle,
138    container: ContainerStyle,
139) -> AnyElement<T> {
140    avatar
141        .map(|avatar| {
142            Image::from_data(avatar)
143                .with_style(avatar_style.image)
144                .aligned()
145                .contained()
146                .with_corner_radius(avatar_style.outer_corner_radius)
147                .constrained()
148                .with_width(avatar_style.outer_width)
149                .with_height(avatar_style.outer_width)
150                .into_any()
151        })
152        .unwrap_or_else(|| {
153            Empty::new()
154                .constrained()
155                .with_width(avatar_style.outer_width)
156                .into_any()
157        })
158        .contained()
159        .with_style(container)
160        .into_any()
161}
162
163fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
164    cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
165}