Position and style the channel editor correctly
Fix a bug where some channel updates would be lost Add channel name sanitization before storing in the database
This commit is contained in:
parent
d00f6a490c
commit
b708824d37
6 changed files with 81 additions and 14 deletions
|
@ -4,8 +4,10 @@ use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
use futures::StreamExt;
|
||||||
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
||||||
use rpc::{proto, TypedEnvelope};
|
use rpc::{proto, TypedEnvelope};
|
||||||
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type ChannelId = u64;
|
pub type ChannelId = u64;
|
||||||
|
@ -19,6 +21,7 @@ pub struct ChannelStore {
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
user_store: ModelHandle<UserStore>,
|
user_store: ModelHandle<UserStore>,
|
||||||
_rpc_subscription: Subscription,
|
_rpc_subscription: Subscription,
|
||||||
|
_maintain_user: Task<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -55,6 +58,20 @@ impl ChannelStore {
|
||||||
let rpc_subscription =
|
let rpc_subscription =
|
||||||
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 maintain_user = cx.spawn(|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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Self {
|
Self {
|
||||||
channels: vec![],
|
channels: vec![],
|
||||||
channel_invitations: vec![],
|
channel_invitations: vec![],
|
||||||
|
@ -63,6 +80,7 @@ impl ChannelStore {
|
||||||
client,
|
client,
|
||||||
user_store,
|
user_store,
|
||||||
_rpc_subscription: rpc_subscription,
|
_rpc_subscription: rpc_subscription,
|
||||||
|
_maintain_user: maintain_user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,10 +319,10 @@ impl ChannelStore {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|c| c.id == channel.id)
|
.find(|c| c.id == channel.id)
|
||||||
{
|
{
|
||||||
let existing_channel = Arc::get_mut(existing_channel)
|
util::make_arc_mut(existing_channel, |new_existing_channel| {
|
||||||
.expect("channel is shared, update would have been lost");
|
new_existing_channel.name = channel.name;
|
||||||
existing_channel.name = channel.name;
|
new_existing_channel.user_is_admin = channel.user_is_admin;
|
||||||
existing_channel.user_is_admin = channel.user_is_admin;
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,10 +340,10 @@ impl ChannelStore {
|
||||||
|
|
||||||
for channel in payload.channels {
|
for channel in payload.channels {
|
||||||
if let Some(existing_channel) = self.channels.iter_mut().find(|c| c.id == channel.id) {
|
if let Some(existing_channel) = self.channels.iter_mut().find(|c| c.id == channel.id) {
|
||||||
let existing_channel = Arc::get_mut(existing_channel)
|
util::make_arc_mut(existing_channel, |new_existing_channel| {
|
||||||
.expect("channel is shared, update would have been lost");
|
new_existing_channel.name = channel.name;
|
||||||
existing_channel.name = channel.name;
|
new_existing_channel.user_is_admin = channel.user_is_admin;
|
||||||
existing_channel.user_is_admin = channel.user_is_admin;
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3155,6 +3155,7 @@ impl Database {
|
||||||
live_kit_room: &str,
|
live_kit_room: &str,
|
||||||
creator_id: UserId,
|
creator_id: UserId,
|
||||||
) -> Result<ChannelId> {
|
) -> Result<ChannelId> {
|
||||||
|
let name = name.trim().trim_start_matches('#');
|
||||||
self.transaction(move |tx| async move {
|
self.transaction(move |tx| async move {
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
self.check_user_is_channel_admin(parent, creator_id, &*tx)
|
self.check_user_is_channel_admin(parent, creator_id, &*tx)
|
||||||
|
|
|
@ -308,7 +308,7 @@ impl CollabPanel {
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
ListEntry::ChannelEditor { depth } => {
|
ListEntry::ChannelEditor { depth } => {
|
||||||
this.render_channel_editor(&theme.collab_panel, *depth, cx)
|
this.render_channel_editor(&theme, *depth, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1280,11 +1280,37 @@ impl CollabPanel {
|
||||||
|
|
||||||
fn render_channel_editor(
|
fn render_channel_editor(
|
||||||
&self,
|
&self,
|
||||||
_theme: &theme::CollabPanel,
|
theme: &theme::Theme,
|
||||||
_depth: usize,
|
depth: usize,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<Self> {
|
) -> AnyElement<Self> {
|
||||||
ChildView::new(&self.channel_name_editor, cx).into_any()
|
Flex::row()
|
||||||
|
.with_child(
|
||||||
|
Svg::new("icons/channel_hash.svg")
|
||||||
|
.with_color(theme.collab_panel.channel_hash.color)
|
||||||
|
.constrained()
|
||||||
|
.with_width(theme.collab_panel.channel_hash.width)
|
||||||
|
.aligned()
|
||||||
|
.left(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
ChildView::new(&self.channel_name_editor, cx)
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.collab_panel.channel_editor)
|
||||||
|
.flex(1.0, true),
|
||||||
|
)
|
||||||
|
.align_children_center()
|
||||||
|
.contained()
|
||||||
|
.with_padding_left(
|
||||||
|
theme.collab_panel.contact_row.default_style().padding.left
|
||||||
|
+ theme.collab_panel.channel_indent * depth as f32,
|
||||||
|
)
|
||||||
|
.contained()
|
||||||
|
.with_style(gpui::elements::ContainerStyle {
|
||||||
|
background_color: Some(theme.editor.background),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_channel(
|
fn render_channel(
|
||||||
|
@ -1331,7 +1357,7 @@ impl CollabPanel {
|
||||||
.constrained()
|
.constrained()
|
||||||
.with_height(theme.row_height)
|
.with_height(theme.row_height)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(*theme.contact_row.in_state(is_selected).style_for(state))
|
.with_style(*theme.contact_row.style_for(is_selected, state))
|
||||||
.with_padding_left(
|
.with_padding_left(
|
||||||
theme.contact_row.default_style().padding.left
|
theme.contact_row.default_style().padding.left
|
||||||
+ theme.channel_indent * channel.depth as f32,
|
+ theme.channel_indent * channel.depth as f32,
|
||||||
|
|
|
@ -221,6 +221,7 @@ pub struct CollabPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub log_in_button: Interactive<ContainedText>,
|
pub log_in_button: Interactive<ContainedText>,
|
||||||
|
pub channel_editor: ContainerStyle,
|
||||||
pub channel_hash: Icon,
|
pub channel_hash: Icon,
|
||||||
pub channel_modal: ChannelModal,
|
pub channel_modal: ChannelModal,
|
||||||
pub user_query_editor: FieldEditor,
|
pub user_query_editor: FieldEditor,
|
||||||
|
@ -885,6 +886,7 @@ impl<T> Toggleable<T> {
|
||||||
pub fn active_state(&self) -> &T {
|
pub fn active_state(&self) -> &T {
|
||||||
self.in_state(true)
|
self.in_state(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inactive_state(&self) -> &T {
|
pub fn inactive_state(&self) -> &T {
|
||||||
self.in_state(false)
|
self.in_state(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,11 @@ pub mod test;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
|
mem,
|
||||||
ops::{AddAssign, Range, RangeInclusive},
|
ops::{AddAssign, Range, RangeInclusive},
|
||||||
panic::Location,
|
panic::Location,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,6 +120,19 @@ pub fn merge_non_null_json_value_into(source: serde_json::Value, target: &mut se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mutates through the arc if no other references exist,
|
||||||
|
/// otherwise clones the value and swaps out the reference with a new Arc
|
||||||
|
/// Useful for mutating the elements of a list while using iter_mut()
|
||||||
|
pub fn make_arc_mut<T: Clone>(arc: &mut Arc<T>, mutate: impl FnOnce(&mut T)) {
|
||||||
|
if let Some(t) = Arc::get_mut(arc) {
|
||||||
|
mutate(t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut new_t = (**arc).clone();
|
||||||
|
mutate(&mut new_t);
|
||||||
|
mem::swap(&mut Arc::new(new_t), arc);
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ResultExt<E> {
|
pub trait ResultExt<E> {
|
||||||
type Ok;
|
type Ok;
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,11 @@ export default function contacts_panel(): any {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
face_overlap: 8
|
face_overlap: 8,
|
||||||
|
channel_editor: {
|
||||||
|
padding: {
|
||||||
|
left: 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue