diff --git a/crates/client/src/channel_store.rs b/crates/client/src/channel_store.rs index ec945ce0367c1da14543f41b4fe9733ef1fec90d..8fb005a26289072871b684df9268ede85333b461 100644 --- a/crates/client/src/channel_store.rs +++ b/crates/client/src/channel_store.rs @@ -58,16 +58,20 @@ impl ChannelStore { client.add_message_handler(cx.handle(), Self::handle_update_channels); 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 { if current_user.is_none() { - this.update(&mut cx, |this, cx| { - this.channels.clear(); - this.channel_invitations.clear(); - this.channel_participants.clear(); - this.outgoing_invites.clear(); - cx.notify(); - }); + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.channels.clear(); + this.channel_invitations.clear(); + this.channel_participants.clear(); + this.outgoing_invites.clear(); + cx.notify(); + }); + } else { + break; + } } } }); diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index 6703f98df26acb9f3755c6667cef0c374a12bcfb..3dceaecef4e15a3fcbc221102110ee441b876832 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -192,11 +192,11 @@ CREATE TABLE "channels" ( "created_at" TIMESTAMP NOT NULL DEFAULT now ); -CREATE TABLE "channel_parents" ( - "child_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, - "parent_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, - PRIMARY KEY(child_id, parent_id) +CREATE TABLE "channel_paths" ( + "id_path" TEXT NOT NULL PRIMARY KEY, + "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE ); +CREATE INDEX "index_channel_paths_on_channel_id" ON "channel_paths" ("channel_id"); CREATE TABLE "channel_members" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/crates/collab/migrations/20230727150500_add_channels.sql b/crates/collab/migrations/20230727150500_add_channels.sql index 2d94cb6d9769f9165382ede3074bd58e90f313a6..df981838bf72d7ef7392ed6f4e302ffdc57631db 100644 --- a/crates/collab/migrations/20230727150500_add_channels.sql +++ b/crates/collab/migrations/20230727150500_add_channels.sql @@ -10,11 +10,11 @@ CREATE TABLE "channels" ( "created_at" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE TABLE "channel_parents" ( - "child_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, - "parent_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE, - PRIMARY KEY(child_id, parent_id) +CREATE TABLE "channel_paths" ( + "id_path" VARCHAR NOT NULL PRIMARY KEY, + "channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE ); +CREATE INDEX "index_channel_paths_on_channel_id" ON "channel_paths" ("channel_id"); CREATE TABLE "channel_members" ( "id" SERIAL PRIMARY KEY, diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index ed5e7e8e3d5a4ba0e06bf2847bc6877ec97f60bf..d830938497dd517e8f9ee92ffeb907df4fad9563 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1,7 +1,7 @@ mod access_token; mod channel; mod channel_member; -mod channel_parent; +mod channel_path; mod contact; mod follower; mod language_server; @@ -3169,12 +3169,34 @@ impl Database { .insert(&*tx) .await?; + let channel_paths_stmt; if let Some(parent) = parent { - channel_parent::ActiveModel { - child_id: ActiveValue::Set(channel.id), - parent_id: ActiveValue::Set(parent), - } - .insert(&*tx) + let sql = r#" + INSERT INTO channel_paths + (id_path, channel_id) + SELECT + 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?; } @@ -3213,9 +3235,9 @@ impl Database { // 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_keep = channel_parent::Entity::find() + let mut channels_to_keep = channel_path::Entity::find() .filter( - channel_parent::Column::ChildId + channel_path::Column::ChannelId .is_in( channels_to_remove .keys() @@ -3223,15 +3245,15 @@ impl Database { .filter(|&id| id != channel_id), ) .and( - channel_parent::Column::ParentId - .is_not_in(channels_to_remove.keys().copied()), + channel_path::Column::IdPath + .not_like(&format!("%/{}/%", channel_id)), ), ) .stream(&*tx) .await?; while let Some(row) = channels_to_keep.next().await { 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, tx: &DatabaseTransaction, ) -> Result> { - let sql = format!( - r#" - WITH RECURSIVE channel_tree(child_id, parent_id) AS ( - 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::() - .stream(&*tx) + let paths = channel_path::Entity::find() + .filter(channel_path::Column::ChannelId.eq(channel_id)) + .all(tx) .await?; - - let mut channel_ids = vec![]; - while let Some(channel_id) = channel_ids_stream.next().await { - channel_ids.push(channel_id?.parent_id); + let mut channel_ids = Vec::new(); + for path in paths { + for id in path.id_path.trim_matches('/').split('/') { + 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) } @@ -3687,38 +3690,38 @@ impl Database { let sql = format!( r#" - WITH RECURSIVE channel_tree(child_id, parent_id) AS ( - SELECT root_ids.column1 as child_id, CAST(NULL as INTEGER) as parent_id - FROM (VALUES {values}) as root_ids - UNION - SELECT channel_parents.child_id, channel_parents.parent_id - FROM channel_parents, channel_tree - WHERE channel_parents.parent_id = channel_tree.child_id - ) - SELECT channel_tree.child_id, channel_tree.parent_id - FROM channel_tree - ORDER BY child_id, parent_id IS NOT NULL - "#, + SELECT + descendant_paths.* + FROM + channel_paths parent_paths, channel_paths descendant_paths + WHERE + parent_paths.channel_id IN ({values}) AND + descendant_paths.id_path LIKE (parent_paths.id_path || '%') + "# ); - #[derive(FromQueryResult, Debug, PartialEq)] - pub struct ChannelParent { - pub child_id: ChannelId, - pub parent_id: Option, - } - let stmt = Statement::from_string(self.pool.get_database_backend(), sql); 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) - .into_model::() .stream(tx) .await?; - while let Some(parent) = parents.next().await { - let parent = parent?; - parents_by_child_id.insert(parent.child_id, parent.parent_id); + while let Some(path) = paths.next().await { + let path = path?; + 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) diff --git a/crates/collab/src/db/channel_parent.rs b/crates/collab/src/db/channel_path.rs similarity index 69% rename from crates/collab/src/db/channel_parent.rs rename to crates/collab/src/db/channel_path.rs index b0072155a3ce666f26124cc0372657d7c9512d85..08ecbddb56ec49cb0ab2c5d24ddec2e2e2bff129 100644 --- a/crates/collab/src/db/channel_parent.rs +++ b/crates/collab/src/db/channel_path.rs @@ -2,12 +2,11 @@ use super::ChannelId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "channel_parents")] +#[sea_orm(table_name = "channel_paths")] pub struct Model { #[sea_orm(primary_key)] - pub child_id: ChannelId, - #[sea_orm(primary_key)] - pub parent_id: ChannelId, + pub id_path: String, + pub channel_id: ChannelId, } impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index c65ef29237aacf4cbd97b9c8a67cf607d426db66..dd2a0db24332e840d90839d95a7fb9c46d6a7433 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1103,9 +1103,12 @@ impl CollabPanel { enum AddContact {} let button = match section { Section::ActiveCall => Some( - MouseEventHandler::::new(0, cx, |_, _| { + MouseEventHandler::::new(0, cx, |state, _| { 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", ) }) @@ -1122,9 +1125,12 @@ impl CollabPanel { ), ), Section::Contacts => Some( - MouseEventHandler::::new(0, cx, |_, _| { + MouseEventHandler::::new(0, cx, |state, _| { 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", ) }) @@ -1141,9 +1147,12 @@ impl CollabPanel { ), ), Section::Channels => Some( - MouseEventHandler::::new(0, cx, |_, _| { + MouseEventHandler::::new(0, cx, |state, _| { 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", ) }) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index cf8da6233a4e4950d106d637275b3e59f50ef436..1756f91fb8e4cc96ec22d6f80a90b67b9c7505f2 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -226,9 +226,9 @@ pub struct CollabPanel { pub channel_modal: ChannelModal, pub user_query_editor: FieldEditor, pub user_query_editor_height: f32, - pub leave_call_button: Toggleable, - pub add_contact_button: Toggleable, - pub add_channel_button: Toggleable, + pub leave_call_button: Toggleable>, + pub add_contact_button: Toggleable>, + pub add_channel_button: Toggleable>, pub header_row: ContainedText, pub subheader_row: Toggleable>, pub leave_call: Interactive, diff --git a/styles/src/style_tree/collab_panel.ts b/styles/src/style_tree/collab_panel.ts index a859f6d6700a772a2ba6f51311eacba9e6b2d6f1..fd6e75d9ec0b120d02501245fc172e10d30f7882 100644 --- a/styles/src/style_tree/collab_panel.ts +++ b/styles/src/style_tree/collab_panel.ts @@ -8,6 +8,7 @@ import { import { interactive, toggleable } from "../element" import { useTheme } from "../theme" import channel_modal from "./channel_modal" +import { icon_button, toggleable_icon_button } from "../component/icon_button" export default function contacts_panel(): any { @@ -51,19 +52,7 @@ export default function contacts_panel(): any { }, } - const headerButton = toggleable({ - base: { - color: foreground(layer, "on"), - button_width: 28, - icon_width: 16, - }, - state: { - active: { - background: background(layer, "active"), - corner_radius: 8, - } - } - }) + const headerButton = toggleable_icon_button(theme, {}) return { channel_modal: channel_modal(),