Detailed changes
@@ -453,6 +453,10 @@ impl Client {
self.state.read().status.1.clone()
}
+ pub fn is_connected(&self) -> bool {
+ matches!(&*self.status().borrow(), Status::Connected { .. })
+ }
+
fn set_status(self: &Arc<Self>, status: Status, cx: &AsyncAppContext) {
log::info!("set status on client {}: {:?}", self.id, status);
let mut state = self.state.write();
@@ -595,6 +595,10 @@ impl UserStore {
self.load_users(proto::FuzzySearchUsers { query }, cx)
}
+ pub fn get_cached_user(&self, user_id: u64) -> Option<Arc<User>> {
+ self.users.get(&user_id).cloned()
+ }
+
pub fn get_user(
&mut self,
user_id: u64,
@@ -215,7 +215,13 @@ impl CollabTitlebarItem {
let git_style = theme.titlebar.git_menu_button.clone();
let item_spacing = theme.titlebar.item_spacing;
- let mut ret = Flex::row().with_child(
+ let mut ret = Flex::row();
+
+ if let Some(project_host) = self.collect_project_host(theme.clone(), cx) {
+ ret = ret.with_child(project_host)
+ }
+
+ ret = ret.with_child(
Stack::new()
.with_child(
MouseEventHandler::new::<ToggleProjectMenu, _>(0, cx, |mouse_state, cx| {
@@ -283,6 +289,71 @@ impl CollabTitlebarItem {
ret.into_any()
}
+ fn collect_project_host(
+ &self,
+ theme: Arc<Theme>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<AnyElement<Self>> {
+ if ActiveCall::global(cx).read(cx).room().is_none() {
+ return None;
+ }
+ let project = self.project.read(cx);
+ let user_store = self.user_store.read(cx);
+
+ if project.is_local() {
+ return None;
+ }
+
+ let Some(host) = project.host() else {
+ return None;
+ };
+ let (Some(host_user), Some(participant_index)) = (
+ user_store.get_cached_user(host.user_id),
+ user_store.participant_indices().get(&host.user_id),
+ ) else {
+ return None;
+ };
+
+ enum ProjectHost {}
+ enum ProjectHostTooltip {}
+
+ let host_style = theme.titlebar.project_host.clone();
+ let selection_style = theme
+ .editor
+ .selection_style_for_room_participant(participant_index.0);
+ let peer_id = host.peer_id.clone();
+
+ Some(
+ MouseEventHandler::new::<ProjectHost, _>(0, cx, |mouse_state, _| {
+ let mut host_style = host_style.style_for(mouse_state).clone();
+ host_style.text.color = selection_style.cursor;
+ Label::new(host_user.github_login.clone(), host_style.text)
+ .contained()
+ .with_style(host_style.container)
+ .aligned()
+ .left()
+ })
+ .with_cursor_style(CursorStyle::PointingHand)
+ .on_click(MouseButton::Left, move |_, this, cx| {
+ if let Some(workspace) = this.workspace.upgrade(cx) {
+ if let Some(task) =
+ workspace.update(cx, |workspace, cx| workspace.follow(peer_id, cx))
+ {
+ task.detach_and_log_err(cx);
+ }
+ }
+ })
+ .with_tooltip::<ProjectHostTooltip>(
+ 0,
+ host_user.github_login.clone() + " is sharing this project. Click to follow.",
+ None,
+ theme.tooltip.clone(),
+ cx,
+ )
+ .into_any_named("project-host"),
+ )
+ }
+
fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
let project = if active {
Some(self.project.clone())
@@ -975,6 +975,10 @@ impl Project {
&self.collaborators
}
+ pub fn host(&self) -> Option<&Collaborator> {
+ self.collaborators.values().find(|c| c.replica_id == 0)
+ }
+
/// Collect all worktrees, including ones that don't appear in the project panel
pub fn worktrees<'a>(
&'a self,
@@ -131,6 +131,7 @@ pub struct Titlebar {
pub menu: TitlebarMenu,
pub project_menu_button: Toggleable<Interactive<ContainedText>>,
pub git_menu_button: Toggleable<Interactive<ContainedText>>,
+ pub project_host: Interactive<ContainedText>,
pub item_spacing: f32,
pub face_pile_spacing: f32,
pub avatar_ribbon: AvatarRibbon,
@@ -2608,6 +2608,24 @@ impl Workspace {
.and_then(|leader_id| self.toggle_follow(leader_id, cx))
}
+ pub fn follow(
+ &mut self,
+ leader_id: PeerId,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<Task<Result<()>>> {
+ for (existing_leader_id, states_by_pane) in &mut self.follower_states_by_leader {
+ if leader_id == *existing_leader_id {
+ for (pane, _) in states_by_pane {
+ cx.focus(pane);
+ return None;
+ }
+ }
+ }
+
+ // not currently following, so follow.
+ self.toggle_follow(leader_id, cx)
+ }
+
pub fn unfollow(
&mut self,
pane: &ViewHandle<Pane>,
@@ -1,4 +1,4 @@
-import { icon_button, toggleable_icon_button, toggleable_text_button } from "../component"
+import { icon_button, text_button, toggleable_icon_button, toggleable_text_button } from "../component"
import { interactive, toggleable } from "../element"
import { useTheme, with_opacity } from "../theme"
import { background, border, foreground, text } from "./components"
@@ -191,6 +191,12 @@ export function titlebar(): any {
color: "variant",
}),
+ project_host: text_button({
+ text_properties: {
+ weight: "bold"
+ }
+ }),
+
// Collaborators
leader_avatar: {
width: avatar_width,