Merge branch 'collab-panel' of github.com:zed-industries/zed into collab-panel

This commit is contained in:
Mikayla 2023-08-09 10:44:50 -07:00
commit 707e41ce1f
No known key found for this signature in database
8 changed files with 112 additions and 108 deletions

View file

@ -58,9 +58,10 @@ impl ChannelStore {
client.add_message_handler(cx.handle(), Self::handle_update_channels); client.add_message_handler(cx.handle(), Self::handle_update_channels);
let mut current_user = user_store.read(cx).watch_current_user(); let mut current_user = user_store.read(cx).watch_current_user();
let maintain_user = cx.spawn(|this, mut cx| async move { let maintain_user = cx.spawn_weak(|this, mut cx| async move {
while let Some(current_user) = current_user.next().await { while let Some(current_user) = current_user.next().await {
if current_user.is_none() { if current_user.is_none() {
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.channels.clear(); this.channels.clear();
this.channel_invitations.clear(); this.channel_invitations.clear();
@ -68,6 +69,9 @@ impl ChannelStore {
this.outgoing_invites.clear(); this.outgoing_invites.clear();
cx.notify(); cx.notify();
}); });
} else {
break;
}
} }
} }
}); });

View file

@ -192,11 +192,11 @@ CREATE TABLE "channels" (
"created_at" TIMESTAMP NOT NULL DEFAULT now "created_at" TIMESTAMP NOT NULL DEFAULT now
); );
CREATE TABLE "channel_parents" ( CREATE TABLE "channel_paths" (
"child_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "id_path" TEXT NOT NULL PRIMARY KEY,
"parent_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE
PRIMARY KEY(child_id, parent_id)
); );
CREATE INDEX "index_channel_paths_on_channel_id" ON "channel_paths" ("channel_id");
CREATE TABLE "channel_members" ( CREATE TABLE "channel_members" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT, "id" INTEGER PRIMARY KEY AUTOINCREMENT,

View file

@ -10,11 +10,11 @@ CREATE TABLE "channels" (
"created_at" TIMESTAMP NOT NULL DEFAULT now() "created_at" TIMESTAMP NOT NULL DEFAULT now()
); );
CREATE TABLE "channel_parents" ( CREATE TABLE "channel_paths" (
"child_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "id_path" VARCHAR NOT NULL PRIMARY KEY,
"parent_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE
PRIMARY KEY(child_id, parent_id)
); );
CREATE INDEX "index_channel_paths_on_channel_id" ON "channel_paths" ("channel_id");
CREATE TABLE "channel_members" ( CREATE TABLE "channel_members" (
"id" SERIAL PRIMARY KEY, "id" SERIAL PRIMARY KEY,

View file

@ -1,7 +1,7 @@
mod access_token; mod access_token;
mod channel; mod channel;
mod channel_member; mod channel_member;
mod channel_parent; mod channel_path;
mod contact; mod contact;
mod follower; mod follower;
mod language_server; mod language_server;
@ -3169,12 +3169,34 @@ impl Database {
.insert(&*tx) .insert(&*tx)
.await?; .await?;
let channel_paths_stmt;
if let Some(parent) = parent { if let Some(parent) = parent {
channel_parent::ActiveModel { let sql = r#"
child_id: ActiveValue::Set(channel.id), INSERT INTO channel_paths
parent_id: ActiveValue::Set(parent), (id_path, channel_id)
} SELECT
.insert(&*tx) id_path || $1 || '/', $2
FROM
channel_paths
WHERE
channel_id = $3
"#;
channel_paths_stmt = Statement::from_sql_and_values(
self.pool.get_database_backend(),
sql,
[
channel.id.to_proto().into(),
channel.id.to_proto().into(),
parent.to_proto().into(),
],
);
tx.execute(channel_paths_stmt).await?;
} else {
channel_path::Entity::insert(channel_path::ActiveModel {
channel_id: ActiveValue::Set(channel.id),
id_path: ActiveValue::Set(format!("/{}/", channel.id)),
})
.exec(&*tx)
.await?; .await?;
} }
@ -3213,9 +3235,9 @@ impl Database {
// Don't remove descendant channels that have additional parents. // Don't remove descendant channels that have additional parents.
let mut channels_to_remove = self.get_channel_descendants([channel_id], &*tx).await?; let mut channels_to_remove = self.get_channel_descendants([channel_id], &*tx).await?;
{ {
let mut channels_to_keep = channel_parent::Entity::find() let mut channels_to_keep = channel_path::Entity::find()
.filter( .filter(
channel_parent::Column::ChildId channel_path::Column::ChannelId
.is_in( .is_in(
channels_to_remove channels_to_remove
.keys() .keys()
@ -3223,15 +3245,15 @@ impl Database {
.filter(|&id| id != channel_id), .filter(|&id| id != channel_id),
) )
.and( .and(
channel_parent::Column::ParentId channel_path::Column::IdPath
.is_not_in(channels_to_remove.keys().copied()), .not_like(&format!("%/{}/%", channel_id)),
), ),
) )
.stream(&*tx) .stream(&*tx)
.await?; .await?;
while let Some(row) = channels_to_keep.next().await { while let Some(row) = channels_to_keep.next().await {
let row = row?; let row = row?;
channels_to_remove.remove(&row.child_id); channels_to_remove.remove(&row.channel_id);
} }
} }
@ -3631,40 +3653,21 @@ impl Database {
channel_id: ChannelId, channel_id: ChannelId,
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
) -> Result<Vec<ChannelId>> { ) -> Result<Vec<ChannelId>> {
let sql = format!( let paths = channel_path::Entity::find()
r#" .filter(channel_path::Column::ChannelId.eq(channel_id))
WITH RECURSIVE channel_tree(child_id, parent_id) AS ( .all(tx)
SELECT CAST(NULL as INTEGER) as child_id, root_ids.column1 as parent_id
FROM (VALUES ({})) as root_ids
UNION
SELECT channel_parents.child_id, channel_parents.parent_id
FROM channel_parents, channel_tree
WHERE channel_parents.child_id = channel_tree.parent_id
)
SELECT DISTINCT channel_tree.parent_id
FROM channel_tree
"#,
channel_id
);
#[derive(FromQueryResult, Debug, PartialEq)]
pub struct ChannelParent {
pub parent_id: ChannelId,
}
let stmt = Statement::from_string(self.pool.get_database_backend(), sql);
let mut channel_ids_stream = channel_parent::Entity::find()
.from_raw_sql(stmt)
.into_model::<ChannelParent>()
.stream(&*tx)
.await?; .await?;
let mut channel_ids = Vec::new();
let mut channel_ids = vec![]; for path in paths {
while let Some(channel_id) = channel_ids_stream.next().await { for id in path.id_path.trim_matches('/').split('/') {
channel_ids.push(channel_id?.parent_id); if let Ok(id) = id.parse() {
let id = ChannelId::from_proto(id);
if let Err(ix) = channel_ids.binary_search(&id) {
channel_ids.insert(ix, id);
}
}
}
} }
Ok(channel_ids) Ok(channel_ids)
} }
@ -3687,38 +3690,38 @@ impl Database {
let sql = format!( let sql = format!(
r#" r#"
WITH RECURSIVE channel_tree(child_id, parent_id) AS ( SELECT
SELECT root_ids.column1 as child_id, CAST(NULL as INTEGER) as parent_id descendant_paths.*
FROM (VALUES {values}) as root_ids FROM
UNION channel_paths parent_paths, channel_paths descendant_paths
SELECT channel_parents.child_id, channel_parents.parent_id WHERE
FROM channel_parents, channel_tree parent_paths.channel_id IN ({values}) AND
WHERE channel_parents.parent_id = channel_tree.child_id descendant_paths.id_path LIKE (parent_paths.id_path || '%')
) "#
SELECT channel_tree.child_id, channel_tree.parent_id
FROM channel_tree
ORDER BY child_id, parent_id IS NOT NULL
"#,
); );
#[derive(FromQueryResult, Debug, PartialEq)]
pub struct ChannelParent {
pub child_id: ChannelId,
pub parent_id: Option<ChannelId>,
}
let stmt = Statement::from_string(self.pool.get_database_backend(), sql); let stmt = Statement::from_string(self.pool.get_database_backend(), sql);
let mut parents_by_child_id = HashMap::default(); let mut parents_by_child_id = HashMap::default();
let mut parents = channel_parent::Entity::find() let mut paths = channel_path::Entity::find()
.from_raw_sql(stmt) .from_raw_sql(stmt)
.into_model::<ChannelParent>()
.stream(tx) .stream(tx)
.await?; .await?;
while let Some(parent) = parents.next().await { while let Some(path) = paths.next().await {
let parent = parent?; let path = path?;
parents_by_child_id.insert(parent.child_id, parent.parent_id); let ids = path.id_path.trim_matches('/').split('/');
let mut parent_id = None;
for id in ids {
if let Ok(id) = id.parse() {
let id = ChannelId::from_proto(id);
if id == path.channel_id {
break;
}
parent_id = Some(id);
}
}
parents_by_child_id.insert(path.channel_id, parent_id);
} }
Ok(parents_by_child_id) Ok(parents_by_child_id)

View file

@ -2,12 +2,11 @@ use super::ChannelId;
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "channel_parents")] #[sea_orm(table_name = "channel_paths")]
pub struct Model { pub struct Model {
#[sea_orm(primary_key)] #[sea_orm(primary_key)]
pub child_id: ChannelId, pub id_path: String,
#[sea_orm(primary_key)] pub channel_id: ChannelId,
pub parent_id: ChannelId,
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View file

@ -1103,9 +1103,12 @@ impl CollabPanel {
enum AddContact {} enum AddContact {}
let button = match section { let button = match section {
Section::ActiveCall => Some( Section::ActiveCall => Some(
MouseEventHandler::<AddContact, Self>::new(0, cx, |_, _| { MouseEventHandler::<AddContact, Self>::new(0, cx, |state, _| {
render_icon_button( render_icon_button(
theme.collab_panel.leave_call_button.in_state(is_selected), theme
.collab_panel
.leave_call_button
.style_for(is_selected, state),
"icons/radix/exit.svg", "icons/radix/exit.svg",
) )
}) })
@ -1122,9 +1125,12 @@ impl CollabPanel {
), ),
), ),
Section::Contacts => Some( Section::Contacts => Some(
MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |_, _| { MouseEventHandler::<LeaveCallContactList, Self>::new(0, cx, |state, _| {
render_icon_button( render_icon_button(
theme.collab_panel.add_contact_button.in_state(is_selected), theme
.collab_panel
.add_contact_button
.style_for(is_selected, state),
"icons/plus_16.svg", "icons/plus_16.svg",
) )
}) })
@ -1141,9 +1147,12 @@ impl CollabPanel {
), ),
), ),
Section::Channels => Some( Section::Channels => Some(
MouseEventHandler::<AddChannel, Self>::new(0, cx, |_, _| { MouseEventHandler::<AddChannel, Self>::new(0, cx, |state, _| {
render_icon_button( render_icon_button(
theme.collab_panel.add_contact_button.in_state(is_selected), theme
.collab_panel
.add_contact_button
.style_for(is_selected, state),
"icons/plus_16.svg", "icons/plus_16.svg",
) )
}) })

View file

@ -226,9 +226,9 @@ pub struct CollabPanel {
pub channel_modal: ChannelModal, pub channel_modal: ChannelModal,
pub user_query_editor: FieldEditor, pub user_query_editor: FieldEditor,
pub user_query_editor_height: f32, pub user_query_editor_height: f32,
pub leave_call_button: Toggleable<IconButton>, pub leave_call_button: Toggleable<Interactive<IconButton>>,
pub add_contact_button: Toggleable<IconButton>, pub add_contact_button: Toggleable<Interactive<IconButton>>,
pub add_channel_button: Toggleable<IconButton>, pub add_channel_button: Toggleable<Interactive<IconButton>>,
pub header_row: ContainedText, pub header_row: ContainedText,
pub subheader_row: Toggleable<Interactive<ContainedText>>, pub subheader_row: Toggleable<Interactive<ContainedText>>,
pub leave_call: Interactive<ContainedText>, pub leave_call: Interactive<ContainedText>,

View file

@ -8,6 +8,7 @@ import {
import { interactive, toggleable } from "../element" import { interactive, toggleable } from "../element"
import { useTheme } from "../theme" import { useTheme } from "../theme"
import channel_modal from "./channel_modal" import channel_modal from "./channel_modal"
import { icon_button, toggleable_icon_button } from "../component/icon_button"
export default function contacts_panel(): any { export default function contacts_panel(): any {
@ -51,19 +52,7 @@ export default function contacts_panel(): any {
}, },
} }
const headerButton = toggleable({ const headerButton = toggleable_icon_button(theme, {})
base: {
color: foreground(layer, "on"),
button_width: 28,
icon_width: 16,
},
state: {
active: {
background: background(layer, "active"),
corner_radius: 8,
}
}
})
return { return {
channel_modal: channel_modal(), channel_modal: channel_modal(),