Unified · Split
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#26232a", | 92 | "background": "#26232a", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#e2dfe7", | 92 | "background": "#e2dfe7", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#1c1c1c", | 92 | "background": "#1c1c1c", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#f8f8f8", | 92 | "background": "#f8f8f8", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#073642", | 92 | "background": "#073642", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#eee8d5", | 92 | "background": "#eee8d5", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#293256", | 92 | "background": "#293256", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -90,6 +90,10 @@ · 89 unmodified lines | |||
| 90 | }, | 90 | }, |
| 91 | "workspace": { | 91 | "workspace": { |
| 92 | "background": "#dfe2f1", | 92 | "background": "#dfe2f1", |
| 93 | "joining_project_avatar": { | ||
| 94 | "corner_radius": 40, | ||
| 95 | "width": 80 | ||
| 96 | }, | ||
| 93 | "joining_project_message": { | 97 | "joining_project_message": { |
| 94 | "padding": 12, | 98 | "padding": 12, |
| 95 | "family": "Zed Sans", | 99 | "family": "Zed Sans", |
| @@ -336,14 +336,14 @@ impl ContactsPanel { · 335 unmodified lines | |||
| 336 | fn render_contact_project( | 336 | fn render_contact_project( |
| 337 | contact: Arc<Contact>, | 337 | contact: Arc<Contact>, |
| 338 | current_user_id: Option<u64>, | 338 | current_user_id: Option<u64>, |
| 339 | project_ix: usize, | 339 | project_index: usize, |
| 340 | app_state: Arc<AppState>, | 340 | app_state: Arc<AppState>, |
| 341 | theme: &theme::ContactsPanel, | 341 | theme: &theme::ContactsPanel, |
| 342 | is_last_project: bool, | 342 | is_last_project: bool, |
| 343 | is_selected: bool, | 343 | is_selected: bool, |
| 344 | cx: &mut LayoutContext, | 344 | cx: &mut LayoutContext, |
| 345 | ) -> ElementBox { | 345 | ) -> ElementBox { |
| 346 | let project = &contact.projects[project_ix]; | 346 | let project = &contact.projects[project_index]; |
| 347 | let project_id = project.id; | 347 | let project_id = project.id; |
| 348 | let is_host = Some(contact.user.id) == current_user_id; | 348 | let is_host = Some(contact.user.id) == current_user_id; |
| 349 | let is_guest = !is_host | 349 | let is_guest = !is_host |
| @@ -445,7 +445,8 @@ impl ContactsPanel { · 95 unmodified lines | |||
| 445 | .on_click(move |_, cx| { | 445 | .on_click(move |_, cx| { |
| 446 | if !is_host && !is_guest { | 446 | if !is_host && !is_guest { |
| 447 | cx.dispatch_global_action(JoinProject { | 447 | cx.dispatch_global_action(JoinProject { |
| 448 | project_id, | 448 | contact: contact.clone(), |
| 449 | project_index, | ||
| 449 | app_state: app_state.clone(), | 450 | app_state: app_state.clone(), |
| 450 | }); | 451 | }); |
| 451 | } | 452 | } |
| @@ -768,12 +769,12 @@ impl ContactsPanel { · 316 unmodified lines | |||
| 768 | let section = *section; | 769 | let section = *section; |
| 769 | self.toggle_expanded(&ToggleExpanded(section), cx); | 770 | self.toggle_expanded(&ToggleExpanded(section), cx); |
| 770 | } | 771 | } |
| 771 | ContactEntry::ContactProject(contact, project_ix) => { | 772 | ContactEntry::ContactProject(contact, project_index) => cx |
| 772 | cx.dispatch_global_action(JoinProject { | 773 | .dispatch_global_action(JoinProject { |
| 773 | project_id: contact.projects[*project_ix].id, | 774 | contact: contact.clone(), |
| 775 | project_index: *project_index, | ||
| 774 | app_state: self.app_state.clone(), | 776 | app_state: self.app_state.clone(), |
| 775 | }) | 777 | }), |
| 776 | } | ||
| 777 | _ => {} | 778 | _ => {} |
| 778 | } | 779 | } |
| 779 | } | 780 | } |
| @@ -48,6 +48,7 @@ pub struct Workspace { · 47 unmodified lines | |||
| 48 | pub modal: ContainerStyle, | 48 | pub modal: ContainerStyle, |
| 49 | pub notification: ContainerStyle, | 49 | pub notification: ContainerStyle, |
| 50 | pub notifications: Notifications, | 50 | pub notifications: Notifications, |
| 51 | pub joining_project_avatar: ImageStyle, | ||
| 51 | pub joining_project_message: ContainedText, | 52 | pub joining_project_message: ContainedText, |
| 52 | } | 53 | } |
| 53 | 54 | ||
| @@ -8,7 +8,8 @@ mod toolbar; · 7 unmodified lines | |||
| 8 | 8 | ||
| 9 | use anyhow::{anyhow, Context, Result}; | 9 | use anyhow::{anyhow, Context, Result}; |
| 10 | use client::{ | 10 | use client::{ |
| 11 | proto, Authenticate, ChannelList, Client, PeerId, Subscription, TypedEnvelope, User, UserStore, | 11 | proto, Authenticate, ChannelList, Client, Contact, PeerId, Subscription, TypedEnvelope, User, |
| 12 | UserStore, | ||
| 12 | }; | 13 | }; |
| 13 | use clock::ReplicaId; | 14 | use clock::ReplicaId; |
| 14 | use collections::{hash_map, HashMap, HashSet}; | 15 | use collections::{hash_map, HashMap, HashSet}; |
| @@ -97,7 +98,8 @@ pub struct ToggleFollow(pub PeerId); · 82 unmodified lines | |||
| 97 | 98 | ||
| 98 | #[derive(Clone)] | 99 | #[derive(Clone)] |
| 99 | pub struct JoinProject { | 100 | pub struct JoinProject { |
| 100 | pub project_id: u64, | 101 | pub contact: Arc<Contact>, |
| 102 | pub project_index: usize, | ||
| 101 | pub app_state: Arc<AppState>, | 103 | pub app_state: Arc<AppState>, |
| 102 | } | 104 | } |
| 103 | 105 | ||
| @@ -117,7 +119,13 @@ pub fn init(client: &Arc<Client>, cx: &mut MutableAppContext) { · 13 unmodified lines | |||
| 117 | open_new(&action.0, cx) | 119 | open_new(&action.0, cx) |
| 118 | }); | 120 | }); |
| 119 | cx.add_global_action(move |action: &JoinProject, cx: &mut MutableAppContext| { | 121 | cx.add_global_action(move |action: &JoinProject, cx: &mut MutableAppContext| { |
| 120 | join_project(action.project_id, &action.app_state, cx).detach(); | 122 | join_project( |
| 123 | action.contact.clone(), | ||
| 124 | action.project_index, | ||
| 125 | &action.app_state, | ||
| 126 | cx, | ||
| 127 | ) | ||
| 128 | .detach(); | ||
| 121 | }); | 129 | }); |
| 122 | 130 | ||
| 123 | cx.add_async_action(Workspace::toggle_follow); | 131 | cx.add_async_action(Workspace::toggle_follow); |
| @@ -2268,12 +2276,16 @@ pub fn open_paths( · 2144 unmodified lines | |||
| 2268 | } | 2276 | } |
| 2269 | 2277 | ||
| 2270 | pub fn join_project( | 2278 | pub fn join_project( |
| 2271 | project_id: u64, | 2279 | contact: Arc<Contact>, |
| 2280 | project_index: usize, | ||
| 2272 | app_state: &Arc<AppState>, | 2281 | app_state: &Arc<AppState>, |
| 2273 | cx: &mut MutableAppContext, | 2282 | cx: &mut MutableAppContext, |
| 2274 | ) -> Task<Result<ViewHandle<Workspace>>> { | 2283 | ) -> Task<Result<ViewHandle<Workspace>>> { |
| 2284 | let project_id = contact.projects[project_index].id; | ||
| 2285 | |||
| 2275 | struct JoiningNotice { | 2286 | struct JoiningNotice { |
| 2276 | message: &'static str, | 2287 | avatar: Option<Arc<ImageData>>, |
| 2288 | message: String, | ||
| 2277 | } | 2289 | } |
| 2278 | 2290 | ||
| 2279 | impl Entity for JoiningNotice { | 2291 | impl Entity for JoiningNotice { |
| @@ -2287,16 +2299,28 @@ pub fn join_project( · 7 unmodified lines | |||
| 2287 | 2299 | ||
| 2288 | fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox { | 2300 | fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox { |
| 2289 | let theme = &cx.global::<Settings>().theme.workspace; | 2301 | let theme = &cx.global::<Settings>().theme.workspace; |
| 2290 | Text::new( | 2302 | |
| 2291 | self.message.to_string(), | 2303 | Flex::column() |
| 2292 | theme.joining_project_message.text.clone(), | 2304 | .with_children(self.avatar.clone().map(|avatar| { |
| 2293 | ) | 2305 | Image::new(avatar) |
| 2294 | .contained() | 2306 | .with_style(theme.joining_project_avatar) |
| 2295 | .with_style(theme.joining_project_message.container) | 2307 | .aligned() |
| 2296 | .aligned() | 2308 | .boxed() |
| 2297 | .contained() | 2309 | })) |
| 2298 | .with_background_color(theme.background) | 2310 | .with_child( |
| 2299 | .boxed() | 2311 | Text::new( |
| 2312 | self.message.clone(), | ||
| 2313 | theme.joining_project_message.text.clone(), | ||
| 2314 | ) | ||
| 2315 | .contained() | ||
| 2316 | .with_style(theme.joining_project_message.container) | ||
| 2317 | .aligned() | ||
| 2318 | .boxed(), | ||
| 2319 | ) | ||
| 2320 | .aligned() | ||
| 2321 | .contained() | ||
| 2322 | .with_background_color(theme.background) | ||
| 2323 | .boxed() | ||
| 2300 | } | 2324 | } |
| 2301 | } | 2325 | } |
| 2302 | 2326 | ||
| @@ -2312,7 +2336,12 @@ pub fn join_project( · 9 unmodified lines | |||
| 2312 | cx.spawn(|mut cx| async move { | 2336 | cx.spawn(|mut cx| async move { |
| 2313 | let (window, joining_notice) = cx.update(|cx| { | 2337 | let (window, joining_notice) = cx.update(|cx| { |
| 2314 | cx.add_window((app_state.build_window_options)(), |_| JoiningNotice { | 2338 | cx.add_window((app_state.build_window_options)(), |_| JoiningNotice { |
| 2315 | message: "Loading remote project...", | 2339 | avatar: contact.user.avatar.clone(), |
| 2340 | message: format!( | ||
| 2341 | "Asking to join @{}'s copy of {}...", | ||
| 2342 | contact.user.github_login, | ||
| 2343 | humanize_list(&contact.projects[project_index].worktree_root_names) | ||
| 2344 | ), | ||
| 2316 | }) | 2345 | }) |
| 2317 | }); | 2346 | }); |
| 2318 | let project = Project::remote( | 2347 | let project = Project::remote( |
| @@ -2338,15 +2367,22 @@ pub fn join_project( · 19 unmodified lines | |||
| 2338 | workspace | 2367 | workspace |
| 2339 | })), | 2368 | })), |
| 2340 | Err(error @ _) => { | 2369 | Err(error @ _) => { |
| 2370 | let login = &contact.user.github_login; | ||
| 2341 | let message = match error { | 2371 | let message = match error { |
| 2342 | project::JoinProjectError::HostDeclined => { | 2372 | project::JoinProjectError::HostDeclined => { |
| 2343 | "The host declined your request to join." | 2373 | format!("@{} declined your request.", login) |
| 2374 | } | ||
| 2375 | project::JoinProjectError::HostClosedProject => { | ||
| 2376 | format!( | ||
| 2377 | "@{} closed their copy of {}.", | ||
| 2378 | login, | ||
| 2379 | humanize_list(&contact.projects[project_index].worktree_root_names) | ||
| 2380 | ) | ||
| 2344 | } | 2381 | } |
| 2345 | project::JoinProjectError::HostClosedProject => "The host closed the project.", | 2382 | project::JoinProjectError::HostWentOffline => { |
| 2346 | project::JoinProjectError::HostWentOffline => "The host went offline.", | 2383 | format!("@{} went offline.", login) |
| 2347 | project::JoinProjectError::Other(_) => { | ||
| 2348 | "An error occurred when attempting to join the project." | ||
| 2349 | } | 2384 | } |
| 2385 | project::JoinProjectError::Other(_) => "An error occurred.".to_string(), | ||
| 2350 | }; | 2386 | }; |
| 2351 | joining_notice.update(cx, |notice, cx| { | 2387 | joining_notice.update(cx, |notice, cx| { |
| 2352 | notice.message = message; | 2388 | notice.message = message; |
| @@ -2372,3 +2408,18 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) { · 19 unmodified lines | |||
| 2372 | }); | 2408 | }); |
| 2373 | cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone())); | 2409 | cx.dispatch_action(window_id, vec![workspace.id()], &OpenNew(app_state.clone())); |
| 2374 | } | 2410 | } |
| 2411 | |||
| 2412 | fn humanize_list<'a>(items: impl IntoIterator<Item = &'a String>) -> String { | ||
| 2413 | let mut list = String::new(); | ||
| 2414 | let mut items = items.into_iter().enumerate().peekable(); | ||
| 2415 | while let Some((ix, item)) = items.next() { | ||
| 2416 | if ix > 0 { | ||
| 2417 | list.push_str(", "); | ||
| 2418 | } | ||
| 2419 | if items.peek().is_none() { | ||
| 2420 | list.push_str("and "); | ||
| 2421 | } | ||
| 2422 | list.push_str(item); | ||
| 2423 | } | ||
| 2424 | list | ||
| 2425 | } | ||
| @@ -0,0 +1,7 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | set -e | ||
| 4 | |||
| 5 | cd styles | ||
| 6 | npm install | ||
| 7 | npm run watch | ||
| @@ -41,6 +41,10 @@ export default function workspace(theme: Theme) { · 40 unmodified lines | |||
| 41 | 41 | ||
| 42 | return { | 42 | return { |
| 43 | background: backgroundColor(theme, 300), | 43 | background: backgroundColor(theme, 300), |
| 44 | joiningProjectAvatar: { | ||
| 45 | cornerRadius: 40, | ||
| 46 | width: 80, | ||
| 47 | }, | ||
| 44 | joiningProjectMessage: { | 48 | joiningProjectMessage: { |
| 45 | padding: 12, | 49 | padding: 12, |
| 46 | ...text(theme, "sans", "primary", { size: "lg" }) | 50 | ...text(theme, "sans", "primary", { size: "lg" }) |