Guest roles (#3140)
Release Notes: - Added a "guest" role to channels, and made that the default when a new user joins a public channel.
This commit is contained in:
commit
cc9e92857b
33 changed files with 2320 additions and 1711 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1575,6 +1575,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"theme",
|
||||
"theme_selector",
|
||||
"time",
|
||||
|
|
|
@ -370,42 +370,15 @@
|
|||
{
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"ctrl-1": [
|
||||
"pane::ActivateItem",
|
||||
0
|
||||
],
|
||||
"ctrl-2": [
|
||||
"pane::ActivateItem",
|
||||
1
|
||||
],
|
||||
"ctrl-3": [
|
||||
"pane::ActivateItem",
|
||||
2
|
||||
],
|
||||
"ctrl-4": [
|
||||
"pane::ActivateItem",
|
||||
3
|
||||
],
|
||||
"ctrl-5": [
|
||||
"pane::ActivateItem",
|
||||
4
|
||||
],
|
||||
"ctrl-6": [
|
||||
"pane::ActivateItem",
|
||||
5
|
||||
],
|
||||
"ctrl-7": [
|
||||
"pane::ActivateItem",
|
||||
6
|
||||
],
|
||||
"ctrl-8": [
|
||||
"pane::ActivateItem",
|
||||
7
|
||||
],
|
||||
"ctrl-9": [
|
||||
"pane::ActivateItem",
|
||||
8
|
||||
],
|
||||
"ctrl-1": ["pane::ActivateItem", 0],
|
||||
"ctrl-2": ["pane::ActivateItem", 1],
|
||||
"ctrl-3": ["pane::ActivateItem", 2],
|
||||
"ctrl-4": ["pane::ActivateItem", 3],
|
||||
"ctrl-5": ["pane::ActivateItem", 4],
|
||||
"ctrl-6": ["pane::ActivateItem", 5],
|
||||
"ctrl-7": ["pane::ActivateItem", 6],
|
||||
"ctrl-8": ["pane::ActivateItem", 7],
|
||||
"ctrl-9": ["pane::ActivateItem", 8],
|
||||
"ctrl-0": "pane::ActivateLastItem",
|
||||
"ctrl--": "pane::GoBack",
|
||||
"ctrl-_": "pane::GoForward",
|
||||
|
@ -416,42 +389,15 @@
|
|||
{
|
||||
"context": "Workspace",
|
||||
"bindings": {
|
||||
"cmd-1": [
|
||||
"workspace::ActivatePane",
|
||||
0
|
||||
],
|
||||
"cmd-2": [
|
||||
"workspace::ActivatePane",
|
||||
1
|
||||
],
|
||||
"cmd-3": [
|
||||
"workspace::ActivatePane",
|
||||
2
|
||||
],
|
||||
"cmd-4": [
|
||||
"workspace::ActivatePane",
|
||||
3
|
||||
],
|
||||
"cmd-5": [
|
||||
"workspace::ActivatePane",
|
||||
4
|
||||
],
|
||||
"cmd-6": [
|
||||
"workspace::ActivatePane",
|
||||
5
|
||||
],
|
||||
"cmd-7": [
|
||||
"workspace::ActivatePane",
|
||||
6
|
||||
],
|
||||
"cmd-8": [
|
||||
"workspace::ActivatePane",
|
||||
7
|
||||
],
|
||||
"cmd-9": [
|
||||
"workspace::ActivatePane",
|
||||
8
|
||||
],
|
||||
"cmd-1": ["workspace::ActivatePane", 0],
|
||||
"cmd-2": ["workspace::ActivatePane", 1],
|
||||
"cmd-3": ["workspace::ActivatePane", 2],
|
||||
"cmd-4": ["workspace::ActivatePane", 3],
|
||||
"cmd-5": ["workspace::ActivatePane", 4],
|
||||
"cmd-6": ["workspace::ActivatePane", 5],
|
||||
"cmd-7": ["workspace::ActivatePane", 6],
|
||||
"cmd-8": ["workspace::ActivatePane", 7],
|
||||
"cmd-9": ["workspace::ActivatePane", 8],
|
||||
"cmd-b": "workspace::ToggleLeftDock",
|
||||
"cmd-r": "workspace::ToggleRightDock",
|
||||
"cmd-j": "workspace::ToggleBottomDock",
|
||||
|
@ -494,38 +440,14 @@
|
|||
},
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-k cmd-left": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Left"
|
||||
],
|
||||
"cmd-k cmd-right": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Right"
|
||||
],
|
||||
"cmd-k cmd-up": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Up"
|
||||
],
|
||||
"cmd-k cmd-down": [
|
||||
"workspace::ActivatePaneInDirection",
|
||||
"Down"
|
||||
],
|
||||
"cmd-k shift-left": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Left"
|
||||
],
|
||||
"cmd-k shift-right": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Right"
|
||||
],
|
||||
"cmd-k shift-up": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Up"
|
||||
],
|
||||
"cmd-k shift-down": [
|
||||
"workspace::SwapPaneInDirection",
|
||||
"Down"
|
||||
]
|
||||
"cmd-k cmd-left": ["workspace::ActivatePaneInDirection", "Left"],
|
||||
"cmd-k cmd-right": ["workspace::ActivatePaneInDirection", "Right"],
|
||||
"cmd-k cmd-up": ["workspace::ActivatePaneInDirection", "Up"],
|
||||
"cmd-k cmd-down": ["workspace::ActivatePaneInDirection", "Down"],
|
||||
"cmd-k shift-left": ["workspace::SwapPaneInDirection", "Left"],
|
||||
"cmd-k shift-right": ["workspace::SwapPaneInDirection", "Right"],
|
||||
"cmd-k shift-up": ["workspace::SwapPaneInDirection", "Up"],
|
||||
"cmd-k shift-down": ["workspace::SwapPaneInDirection", "Down"]
|
||||
}
|
||||
},
|
||||
// Bindings from Atom
|
||||
|
@ -627,14 +549,6 @@
|
|||
"space": "collab_panel::InsertSpace"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "(CollabPanel && not_editing) > Editor",
|
||||
"bindings": {
|
||||
"cmd-c": "collab_panel::StartLinkChannel",
|
||||
"cmd-x": "collab_panel::StartMoveChannel",
|
||||
"cmd-v": "collab_panel::MoveOrLinkToSelected"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ChannelModal",
|
||||
"bindings": {
|
||||
|
@ -655,57 +569,21 @@
|
|||
"cmd-v": "terminal::Paste",
|
||||
"cmd-k": "terminal::Clear",
|
||||
// Some nice conveniences
|
||||
"cmd-backspace": [
|
||||
"terminal::SendText",
|
||||
"\u0015"
|
||||
],
|
||||
"cmd-right": [
|
||||
"terminal::SendText",
|
||||
"\u0005"
|
||||
],
|
||||
"cmd-left": [
|
||||
"terminal::SendText",
|
||||
"\u0001"
|
||||
],
|
||||
"cmd-backspace": ["terminal::SendText", "\u0015"],
|
||||
"cmd-right": ["terminal::SendText", "\u0005"],
|
||||
"cmd-left": ["terminal::SendText", "\u0001"],
|
||||
// Terminal.app compatibility
|
||||
"alt-left": [
|
||||
"terminal::SendText",
|
||||
"\u001bb"
|
||||
],
|
||||
"alt-right": [
|
||||
"terminal::SendText",
|
||||
"\u001bf"
|
||||
],
|
||||
"alt-left": ["terminal::SendText", "\u001bb"],
|
||||
"alt-right": ["terminal::SendText", "\u001bf"],
|
||||
// There are conflicting bindings for these keys in the global context.
|
||||
// these bindings override them, remove at your own risk:
|
||||
"up": [
|
||||
"terminal::SendKeystroke",
|
||||
"up"
|
||||
],
|
||||
"pageup": [
|
||||
"terminal::SendKeystroke",
|
||||
"pageup"
|
||||
],
|
||||
"down": [
|
||||
"terminal::SendKeystroke",
|
||||
"down"
|
||||
],
|
||||
"pagedown": [
|
||||
"terminal::SendKeystroke",
|
||||
"pagedown"
|
||||
],
|
||||
"escape": [
|
||||
"terminal::SendKeystroke",
|
||||
"escape"
|
||||
],
|
||||
"enter": [
|
||||
"terminal::SendKeystroke",
|
||||
"enter"
|
||||
],
|
||||
"ctrl-c": [
|
||||
"terminal::SendKeystroke",
|
||||
"ctrl-c"
|
||||
]
|
||||
"up": ["terminal::SendKeystroke", "up"],
|
||||
"pageup": ["terminal::SendKeystroke", "pageup"],
|
||||
"down": ["terminal::SendKeystroke", "down"],
|
||||
"pagedown": ["terminal::SendKeystroke", "pagedown"],
|
||||
"escape": ["terminal::SendKeystroke", "escape"],
|
||||
"enter": ["terminal::SendKeystroke", "enter"],
|
||||
"ctrl-c": ["terminal::SendKeystroke", "ctrl-c"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -55,7 +55,7 @@ pub enum Event {
|
|||
|
||||
pub struct Room {
|
||||
id: u64,
|
||||
channel_id: Option<u64>,
|
||||
pub channel_id: Option<u64>,
|
||||
live_kit: Option<LiveKitRoom>,
|
||||
status: RoomStatus,
|
||||
shared_projects: HashSet<WeakModelHandle<Project>>,
|
||||
|
@ -122,6 +122,10 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn can_publish(&self) -> bool {
|
||||
self.live_kit.as_ref().is_some_and(|room| room.can_publish)
|
||||
}
|
||||
|
||||
fn new(
|
||||
id: u64,
|
||||
channel_id: Option<u64>,
|
||||
|
@ -181,20 +185,23 @@ impl Room {
|
|||
});
|
||||
|
||||
let connect = room.connect(&connection_info.server_url, &connection_info.token);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
connect.await?;
|
||||
if connection_info.can_publish {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
connect.await?;
|
||||
|
||||
if !cx.read(Self::mute_on_join) {
|
||||
this.update(&mut cx, |this, cx| this.share_microphone(cx))
|
||||
.await?;
|
||||
}
|
||||
if !cx.read(Self::mute_on_join) {
|
||||
this.update(&mut cx, |this, cx| this.share_microphone(cx))
|
||||
.await?;
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
Some(LiveKitRoom {
|
||||
room,
|
||||
can_publish: connection_info.can_publish,
|
||||
screen_track: LocalTrack::None,
|
||||
microphone_track: LocalTrack::None,
|
||||
next_publish_id: 0,
|
||||
|
@ -1498,6 +1505,7 @@ struct LiveKitRoom {
|
|||
deafened: bool,
|
||||
speaking: bool,
|
||||
next_publish_id: usize,
|
||||
can_publish: bool,
|
||||
_maintain_room: Task<()>,
|
||||
_maintain_tracks: [Task<()>; 2],
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Channel;
|
||||
use crate::{Channel, ChannelId, ChannelStore};
|
||||
use anyhow::Result;
|
||||
use client::{Client, Collaborator, UserStore};
|
||||
use collections::HashMap;
|
||||
|
@ -19,10 +19,11 @@ pub(crate) fn init(client: &Arc<Client>) {
|
|||
}
|
||||
|
||||
pub struct ChannelBuffer {
|
||||
pub(crate) channel: Arc<Channel>,
|
||||
pub channel_id: ChannelId,
|
||||
connected: bool,
|
||||
collaborators: HashMap<PeerId, Collaborator>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
buffer: ModelHandle<language::Buffer>,
|
||||
buffer_epoch: u64,
|
||||
client: Arc<Client>,
|
||||
|
@ -34,6 +35,7 @@ pub enum ChannelBufferEvent {
|
|||
CollaboratorsChanged,
|
||||
Disconnected,
|
||||
BufferEdited,
|
||||
ChannelChanged,
|
||||
}
|
||||
|
||||
impl Entity for ChannelBuffer {
|
||||
|
@ -46,7 +48,7 @@ impl Entity for ChannelBuffer {
|
|||
}
|
||||
self.client
|
||||
.send(proto::LeaveChannelBuffer {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -58,6 +60,7 @@ impl ChannelBuffer {
|
|||
channel: Arc<Channel>,
|
||||
client: Arc<Client>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<ModelHandle<Self>> {
|
||||
let response = client
|
||||
|
@ -90,9 +93,10 @@ impl ChannelBuffer {
|
|||
connected: true,
|
||||
collaborators: Default::default(),
|
||||
acknowledge_task: None,
|
||||
channel,
|
||||
channel_id: channel.id,
|
||||
subscription: Some(subscription.set_model(&cx.handle(), &mut cx.to_async())),
|
||||
user_store,
|
||||
channel_store,
|
||||
};
|
||||
this.replace_collaborators(response.collaborators, cx);
|
||||
this
|
||||
|
@ -179,7 +183,7 @@ impl ChannelBuffer {
|
|||
let operation = language::proto::serialize_operation(operation);
|
||||
self.client
|
||||
.send(proto::UpdateChannelBuffer {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
operations: vec![operation],
|
||||
})
|
||||
.log_err();
|
||||
|
@ -223,12 +227,15 @@ impl ChannelBuffer {
|
|||
&self.collaborators
|
||||
}
|
||||
|
||||
pub fn channel(&self) -> Arc<Channel> {
|
||||
self.channel.clone()
|
||||
pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
|
||||
self.channel_store
|
||||
.read(cx)
|
||||
.channel_for_id(self.channel_id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn disconnect(&mut self, cx: &mut ModelContext<Self>) {
|
||||
log::info!("channel buffer {} disconnected", self.channel.id);
|
||||
log::info!("channel buffer {} disconnected", self.channel_id);
|
||||
if self.connected {
|
||||
self.connected = false;
|
||||
self.subscription.take();
|
||||
|
@ -237,6 +244,11 @@ impl ChannelBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn channel_changed(&mut self, cx: &mut ModelContext<Self>) {
|
||||
cx.emit(ChannelBufferEvent::ChannelChanged);
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.connected
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use time::OffsetDateTime;
|
|||
use util::{post_inc, ResultExt as _, TryFutureExt};
|
||||
|
||||
pub struct ChannelChat {
|
||||
channel: Arc<Channel>,
|
||||
pub channel_id: ChannelId,
|
||||
messages: SumTree<ChannelMessage>,
|
||||
acknowledged_message_ids: HashSet<u64>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
|
@ -87,7 +87,7 @@ impl Entity for ChannelChat {
|
|||
fn release(&mut self, _: &mut AppContext) {
|
||||
self.rpc
|
||||
.send(proto::LeaveChannelChat {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ impl ChannelChat {
|
|||
|
||||
Ok(cx.add_model(|cx| {
|
||||
let mut this = Self {
|
||||
channel,
|
||||
channel_id: channel.id,
|
||||
user_store,
|
||||
channel_store,
|
||||
rpc: client,
|
||||
|
@ -130,8 +130,11 @@ impl ChannelChat {
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn channel(&self) -> &Arc<Channel> {
|
||||
&self.channel
|
||||
pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
|
||||
self.channel_store
|
||||
.read(cx)
|
||||
.channel_for_id(self.channel_id)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn client(&self) -> &Arc<Client> {
|
||||
|
@ -153,7 +156,7 @@ impl ChannelChat {
|
|||
.current_user()
|
||||
.ok_or_else(|| anyhow!("current_user is not present"))?;
|
||||
|
||||
let channel_id = self.channel.id;
|
||||
let channel_id = self.channel_id;
|
||||
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
|
||||
let nonce = self.rng.gen();
|
||||
self.insert_messages(
|
||||
|
@ -195,7 +198,7 @@ impl ChannelChat {
|
|||
|
||||
pub fn remove_message(&mut self, id: u64, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
let response = self.rpc.request(proto::RemoveChannelMessage {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
message_id: id,
|
||||
});
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
|
@ -215,7 +218,7 @@ impl ChannelChat {
|
|||
|
||||
let rpc = self.rpc.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let channel_id = self.channel.id;
|
||||
let channel_id = self.channel_id;
|
||||
let before_message_id = self.first_loaded_message_id()?;
|
||||
Some(cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
|
@ -288,13 +291,13 @@ impl ChannelChat {
|
|||
{
|
||||
self.rpc
|
||||
.send(proto::AckChannelMessage {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
message_id: latest_message_id,
|
||||
})
|
||||
.ok();
|
||||
self.last_acknowledged_id = Some(latest_message_id);
|
||||
self.channel_store.update(cx, |store, cx| {
|
||||
store.acknowledge_message_id(self.channel.id, latest_message_id, cx);
|
||||
store.acknowledge_message_id(self.channel_id, latest_message_id, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +306,7 @@ impl ChannelChat {
|
|||
pub fn rejoin(&mut self, cx: &mut ModelContext<Self>) {
|
||||
let user_store = self.user_store.clone();
|
||||
let rpc = self.rpc.clone();
|
||||
let channel_id = self.channel.id;
|
||||
let channel_id = self.channel_id;
|
||||
cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
let response = rpc.request(proto::JoinChannelChat { channel_id }).await?;
|
||||
|
@ -376,7 +379,7 @@ impl ChannelChat {
|
|||
if self.acknowledged_message_ids.insert(id) {
|
||||
self.rpc
|
||||
.send(proto::AckChannelMessage {
|
||||
channel_id: self.channel.id,
|
||||
channel_id: self.channel_id,
|
||||
message_id: id,
|
||||
})
|
||||
.ok();
|
||||
|
@ -412,7 +415,7 @@ impl ChannelChat {
|
|||
this.update(&mut cx, |this, cx| {
|
||||
this.insert_messages(SumTree::from_item(message, &()), cx);
|
||||
cx.emit(ChannelChatEvent::NewMessage {
|
||||
channel_id: this.channel.id,
|
||||
channel_id: this.channel_id,
|
||||
message_id,
|
||||
})
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ use db::RELEASE_CHANNEL;
|
|||
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
|
||||
use rpc::{
|
||||
proto::{self, ChannelEdge, ChannelPermission, ChannelRole, ChannelVisibility},
|
||||
proto::{self, ChannelEdge, ChannelVisibility},
|
||||
TypedEnvelope,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
@ -30,7 +30,6 @@ pub struct ChannelStore {
|
|||
channel_index: ChannelIndex,
|
||||
channel_invitations: Vec<Arc<Channel>>,
|
||||
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
|
||||
channels_with_admin_privileges: HashSet<ChannelId>,
|
||||
outgoing_invites: HashSet<(ChannelId, UserId)>,
|
||||
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
|
||||
opened_buffers: HashMap<ChannelId, OpenedModelHandle<ChannelBuffer>>,
|
||||
|
@ -50,6 +49,7 @@ pub struct Channel {
|
|||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub visibility: proto::ChannelVisibility,
|
||||
pub role: proto::ChannelRole,
|
||||
pub unseen_note_version: Option<(u64, clock::Global)>,
|
||||
pub unseen_message_id: Option<u64>,
|
||||
}
|
||||
|
@ -72,6 +72,10 @@ impl Channel {
|
|||
|
||||
slug.trim_matches(|c| c == '-').to_string()
|
||||
}
|
||||
|
||||
pub fn can_edit_notes(&self) -> bool {
|
||||
self.role == proto::ChannelRole::Member || self.role == proto::ChannelRole::Admin
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
|
||||
|
@ -161,7 +165,6 @@ impl ChannelStore {
|
|||
channel_invitations: Vec::default(),
|
||||
channel_index: ChannelIndex::default(),
|
||||
channel_participants: Default::default(),
|
||||
channels_with_admin_privileges: Default::default(),
|
||||
outgoing_invites: Default::default(),
|
||||
opened_buffers: Default::default(),
|
||||
opened_chats: Default::default(),
|
||||
|
@ -269,10 +272,11 @@ impl ChannelStore {
|
|||
) -> Task<Result<ModelHandle<ChannelBuffer>>> {
|
||||
let client = self.client.clone();
|
||||
let user_store = self.user_store.clone();
|
||||
let channel_store = cx.handle();
|
||||
self.open_channel_resource(
|
||||
channel_id,
|
||||
|this| &mut this.opened_buffers,
|
||||
|channel, cx| ChannelBuffer::new(channel, client, user_store, cx),
|
||||
|channel, cx| ChannelBuffer::new(channel, client, user_store, channel_store, cx),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
|
@ -449,16 +453,11 @@ impl ChannelStore {
|
|||
.spawn(async move { task.await.map_err(|error| anyhow!("{}", error)) })
|
||||
}
|
||||
|
||||
pub fn is_user_admin(&self, channel_id: ChannelId) -> bool {
|
||||
self.channel_index.iter().any(|path| {
|
||||
if let Some(ix) = path.iter().position(|id| *id == channel_id) {
|
||||
path[..=ix]
|
||||
.iter()
|
||||
.any(|id| self.channels_with_admin_privileges.contains(id))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
pub fn is_channel_admin(&self, channel_id: ChannelId) -> bool {
|
||||
let Some(channel) = self.channel_for_id(channel_id) else {
|
||||
return false;
|
||||
};
|
||||
channel.role == proto::ChannelRole::Admin
|
||||
}
|
||||
|
||||
pub fn channel_participants(&self, channel_id: ChannelId) -> &[Arc<User>] {
|
||||
|
@ -499,10 +498,6 @@ impl ChannelStore {
|
|||
proto::UpdateChannels {
|
||||
channels: vec![channel],
|
||||
insert_edge: parent_edge,
|
||||
channel_permissions: vec![ChannelPermission {
|
||||
channel_id,
|
||||
role: ChannelRole::Admin.into(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
|
@ -800,6 +795,11 @@ impl ChannelStore {
|
|||
}
|
||||
|
||||
fn handle_connect(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
self.channel_index.clear();
|
||||
self.channel_invitations.clear();
|
||||
self.channel_participants.clear();
|
||||
self.channel_index.clear();
|
||||
self.outgoing_invites.clear();
|
||||
self.disconnect_channel_buffers_task.take();
|
||||
|
||||
for chat in self.opened_chats.values() {
|
||||
|
@ -819,7 +819,7 @@ impl ChannelStore {
|
|||
let channel_buffer = buffer.read(cx);
|
||||
let buffer = channel_buffer.buffer().read(cx);
|
||||
buffer_versions.push(proto::ChannelBufferVersion {
|
||||
channel_id: channel_buffer.channel().id,
|
||||
channel_id: channel_buffer.channel_id,
|
||||
epoch: channel_buffer.epoch(),
|
||||
version: language::proto::serialize_version(&buffer.version()),
|
||||
});
|
||||
|
@ -846,13 +846,13 @@ impl ChannelStore {
|
|||
};
|
||||
|
||||
channel_buffer.update(cx, |channel_buffer, cx| {
|
||||
let channel_id = channel_buffer.channel().id;
|
||||
let channel_id = channel_buffer.channel_id;
|
||||
if let Some(remote_buffer) = response
|
||||
.buffers
|
||||
.iter_mut()
|
||||
.find(|buffer| buffer.channel_id == channel_id)
|
||||
{
|
||||
let channel_id = channel_buffer.channel().id;
|
||||
let channel_id = channel_buffer.channel_id;
|
||||
let remote_version =
|
||||
language::proto::deserialize_version(&remote_buffer.version);
|
||||
|
||||
|
@ -909,12 +909,6 @@ impl ChannelStore {
|
|||
}
|
||||
|
||||
fn handle_disconnect(&mut self, wait_for_reconnect: bool, cx: &mut ModelContext<Self>) {
|
||||
self.channel_index.clear();
|
||||
self.channel_invitations.clear();
|
||||
self.channel_participants.clear();
|
||||
self.channels_with_admin_privileges.clear();
|
||||
self.channel_index.clear();
|
||||
self.outgoing_invites.clear();
|
||||
cx.notify();
|
||||
|
||||
self.disconnect_channel_buffers_task.get_or_insert_with(|| {
|
||||
|
@ -958,6 +952,7 @@ impl ChannelStore {
|
|||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
visibility: channel.visibility(),
|
||||
role: channel.role(),
|
||||
name: channel.name,
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
|
@ -977,12 +972,17 @@ impl ChannelStore {
|
|||
if !payload.delete_channels.is_empty() {
|
||||
self.channel_index.delete_channels(&payload.delete_channels);
|
||||
self.channel_participants
|
||||
.retain(|channel_id, _| !payload.delete_channels.contains(channel_id));
|
||||
self.channels_with_admin_privileges
|
||||
.retain(|channel_id| !payload.delete_channels.contains(channel_id));
|
||||
.retain(|channel_id, _| !&payload.delete_channels.contains(channel_id));
|
||||
|
||||
for channel_id in &payload.delete_channels {
|
||||
let channel_id = *channel_id;
|
||||
if payload
|
||||
.channels
|
||||
.iter()
|
||||
.any(|channel| channel.id == channel_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Some(OpenedModelHandle::Open(buffer)) =
|
||||
self.opened_buffers.remove(&channel_id)
|
||||
{
|
||||
|
@ -995,7 +995,16 @@ impl ChannelStore {
|
|||
|
||||
let mut index = self.channel_index.bulk_insert();
|
||||
for channel in payload.channels {
|
||||
index.insert(channel)
|
||||
let id = channel.id;
|
||||
let channel_changed = index.insert(channel);
|
||||
|
||||
if channel_changed {
|
||||
if let Some(OpenedModelHandle::Open(buffer)) = self.opened_buffers.get(&id) {
|
||||
if let Some(buffer) = buffer.upgrade(cx) {
|
||||
buffer.update(cx, ChannelBuffer::channel_changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for unseen_buffer_change in payload.unseen_channel_buffer_changes {
|
||||
|
@ -1023,16 +1032,6 @@ impl ChannelStore {
|
|||
}
|
||||
}
|
||||
|
||||
for permission in payload.channel_permissions {
|
||||
if permission.role() == proto::ChannelRole::Admin {
|
||||
self.channels_with_admin_privileges
|
||||
.insert(permission.channel_id);
|
||||
} else {
|
||||
self.channels_with_admin_privileges
|
||||
.remove(&permission.channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
if payload.channel_participants.is_empty() {
|
||||
return None;
|
||||
|
|
|
@ -26,10 +26,8 @@ impl ChannelIndex {
|
|||
pub fn delete_channels(&mut self, channels: &[ChannelId]) {
|
||||
self.channels_by_id
|
||||
.retain(|channel_id, _| !channels.contains(channel_id));
|
||||
self.paths.retain(|path| {
|
||||
path.iter()
|
||||
.all(|channel_id| self.channels_by_id.contains_key(channel_id))
|
||||
});
|
||||
self.paths
|
||||
.retain(|path| !path.iter().any(|channel_id| channels.contains(channel_id)));
|
||||
}
|
||||
|
||||
pub fn bulk_insert(&mut self) -> ChannelPathsInsertGuard {
|
||||
|
@ -121,10 +119,17 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
insert_new_message(&mut self.channels_by_id, channel_id, message_id)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, channel_proto: proto::Channel) {
|
||||
pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
|
||||
let mut ret = false;
|
||||
if let Some(existing_channel) = self.channels_by_id.get_mut(&channel_proto.id) {
|
||||
let existing_channel = Arc::make_mut(existing_channel);
|
||||
|
||||
ret = existing_channel.visibility != channel_proto.visibility()
|
||||
|| existing_channel.role != channel_proto.role()
|
||||
|| existing_channel.name != channel_proto.name;
|
||||
|
||||
existing_channel.visibility = channel_proto.visibility();
|
||||
existing_channel.role = channel_proto.role();
|
||||
existing_channel.name = channel_proto.name;
|
||||
} else {
|
||||
self.channels_by_id.insert(
|
||||
|
@ -132,6 +137,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
Arc::new(Channel {
|
||||
id: channel_proto.id,
|
||||
visibility: channel_proto.visibility(),
|
||||
role: channel_proto.role(),
|
||||
name: channel_proto.name,
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
|
@ -139,6 +145,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
);
|
||||
self.insert_root(channel_proto.id);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn insert_edge(&mut self, channel_id: ChannelId, parent_id: ChannelId) {
|
||||
|
|
|
@ -19,17 +19,15 @@ fn test_update_channels(cx: &mut AppContext) {
|
|||
id: 1,
|
||||
name: "b".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::Channel {
|
||||
id: 2,
|
||||
name: "a".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Member.into(),
|
||||
},
|
||||
],
|
||||
channel_permissions: vec![proto::ChannelPermission {
|
||||
channel_id: 1,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
|
@ -38,8 +36,8 @@ fn test_update_channels(cx: &mut AppContext) {
|
|||
&channel_store,
|
||||
&[
|
||||
//
|
||||
(0, "a".to_string(), false),
|
||||
(0, "b".to_string(), true),
|
||||
(0, "a".to_string(), proto::ChannelRole::Member),
|
||||
(0, "b".to_string(), proto::ChannelRole::Admin),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
|
@ -52,11 +50,13 @@ fn test_update_channels(cx: &mut AppContext) {
|
|||
id: 3,
|
||||
name: "x".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::Channel {
|
||||
id: 4,
|
||||
name: "y".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Member.into(),
|
||||
},
|
||||
],
|
||||
insert_edge: vec![
|
||||
|
@ -76,10 +76,10 @@ fn test_update_channels(cx: &mut AppContext) {
|
|||
assert_channels(
|
||||
&channel_store,
|
||||
&[
|
||||
(0, "a".to_string(), false),
|
||||
(1, "y".to_string(), false),
|
||||
(0, "b".to_string(), true),
|
||||
(1, "x".to_string(), true),
|
||||
(0, "a".to_string(), proto::ChannelRole::Member),
|
||||
(1, "y".to_string(), proto::ChannelRole::Member),
|
||||
(0, "b".to_string(), proto::ChannelRole::Admin),
|
||||
(1, "x".to_string(), proto::ChannelRole::Admin),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
|
@ -97,16 +97,19 @@ fn test_dangling_channel_paths(cx: &mut AppContext) {
|
|||
id: 0,
|
||||
name: "a".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::Channel {
|
||||
id: 1,
|
||||
name: "b".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::Channel {
|
||||
id: 2,
|
||||
name: "c".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
],
|
||||
insert_edge: vec![
|
||||
|
@ -119,10 +122,6 @@ fn test_dangling_channel_paths(cx: &mut AppContext) {
|
|||
channel_id: 2,
|
||||
},
|
||||
],
|
||||
channel_permissions: vec![proto::ChannelPermission {
|
||||
channel_id: 0,
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
|
@ -132,9 +131,9 @@ fn test_dangling_channel_paths(cx: &mut AppContext) {
|
|||
&channel_store,
|
||||
&[
|
||||
//
|
||||
(0, "a".to_string(), true),
|
||||
(1, "b".to_string(), true),
|
||||
(2, "c".to_string(), true),
|
||||
(0, "a".to_string(), proto::ChannelRole::Admin),
|
||||
(1, "b".to_string(), proto::ChannelRole::Admin),
|
||||
(2, "c".to_string(), proto::ChannelRole::Admin),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
|
@ -149,7 +148,11 @@ fn test_dangling_channel_paths(cx: &mut AppContext) {
|
|||
);
|
||||
|
||||
// Make sure that the 1/2/3 path is gone
|
||||
assert_channels(&channel_store, &[(0, "a".to_string(), true)], cx);
|
||||
assert_channels(
|
||||
&channel_store,
|
||||
&[(0, "a".to_string(), proto::ChannelRole::Admin)],
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -166,12 +169,17 @@ async fn test_channel_messages(cx: &mut TestAppContext) {
|
|||
id: channel_id,
|
||||
name: "the-channel".to_string(),
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
role: proto::ChannelRole::Member.into(),
|
||||
}],
|
||||
..Default::default()
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
cx.read(|cx| {
|
||||
assert_channels(&channel_store, &[(0, "the-channel".to_string(), false)], cx);
|
||||
assert_channels(
|
||||
&channel_store,
|
||||
&[(0, "the-channel".to_string(), proto::ChannelRole::Member)],
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let get_users = server.receive::<proto::GetUsers>().await.unwrap();
|
||||
|
@ -371,19 +379,13 @@ fn update_channels(
|
|||
#[track_caller]
|
||||
fn assert_channels(
|
||||
channel_store: &ModelHandle<ChannelStore>,
|
||||
expected_channels: &[(usize, String, bool)],
|
||||
expected_channels: &[(usize, String, proto::ChannelRole)],
|
||||
cx: &AppContext,
|
||||
) {
|
||||
let actual = channel_store.read_with(cx, |store, _| {
|
||||
store
|
||||
.channel_dag_entries()
|
||||
.map(|(depth, channel)| {
|
||||
(
|
||||
depth,
|
||||
channel.name.to_string(),
|
||||
store.is_user_admin(channel.id),
|
||||
)
|
||||
})
|
||||
.map(|(depth, channel)| (depth, channel.name.to_string(), channel.role))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
assert_eq!(actual, expected_channels);
|
||||
|
|
|
@ -435,18 +435,103 @@ pub struct NewUserResult {
|
|||
pub signup_device_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MoveChannelResult {
|
||||
pub participants_to_update: HashMap<UserId, ChannelsForUser>,
|
||||
pub participants_to_remove: HashSet<UserId>,
|
||||
pub moved_channels: HashSet<ChannelId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RenameChannelResult {
|
||||
pub channel: Channel,
|
||||
pub participants_to_update: HashMap<UserId, Channel>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreateChannelResult {
|
||||
pub channel: Channel,
|
||||
pub participants_to_update: Vec<(UserId, ChannelsForUser)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SetChannelVisibilityResult {
|
||||
pub participants_to_update: HashMap<UserId, ChannelsForUser>,
|
||||
pub participants_to_remove: HashSet<UserId>,
|
||||
pub channels_to_remove: Vec<ChannelId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MembershipUpdated {
|
||||
pub channel_id: ChannelId,
|
||||
pub new_channels: ChannelsForUser,
|
||||
pub removed_channels: Vec<ChannelId>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SetMemberRoleResult {
|
||||
InviteUpdated(Channel),
|
||||
MembershipUpdated(MembershipUpdated),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InviteMemberResult {
|
||||
pub channel: Channel,
|
||||
pub notifications: NotificationBatch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RespondToChannelInvite {
|
||||
pub membership_update: Option<MembershipUpdated>,
|
||||
pub notifications: NotificationBatch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveChannelMemberResult {
|
||||
pub membership_update: MembershipUpdated,
|
||||
pub notification_id: Option<NotificationId>,
|
||||
}
|
||||
|
||||
#[derive(FromQueryResult, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub visibility: ChannelVisibility,
|
||||
pub role: ChannelRole,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn to_proto(&self) -> proto::Channel {
|
||||
proto::Channel {
|
||||
id: self.id.to_proto(),
|
||||
name: self.name.clone(),
|
||||
visibility: self.visibility.into(),
|
||||
role: self.role.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ChannelMember {
|
||||
pub role: ChannelRole,
|
||||
pub user_id: UserId,
|
||||
pub kind: proto::channel_member::Kind,
|
||||
}
|
||||
|
||||
impl ChannelMember {
|
||||
pub fn to_proto(&self) -> proto::ChannelMember {
|
||||
proto::ChannelMember {
|
||||
role: self.role.into(),
|
||||
user_id: self.user_id.to_proto(),
|
||||
kind: self.kind.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ChannelsForUser {
|
||||
pub channels: ChannelGraph,
|
||||
pub channel_participants: HashMap<ChannelId, Vec<UserId>>,
|
||||
pub channels_with_admin_privileges: HashSet<ChannelId>,
|
||||
pub unseen_buffer_changes: Vec<proto::UnseenChannelBufferChange>,
|
||||
pub channel_messages: Vec<proto::UnseenChannelMessage>,
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ id_type!(FlagId);
|
|||
id_type!(NotificationId);
|
||||
id_type!(NotificationKindId);
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default)]
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum, Default, Hash)]
|
||||
#[sea_orm(rs_type = "String", db_type = "String(None)")]
|
||||
pub enum ChannelRole {
|
||||
#[sea_orm(string_value = "admin")]
|
||||
|
@ -116,6 +116,22 @@ impl ChannelRole {
|
|||
other
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_see_all_descendants(&self) -> bool {
|
||||
use ChannelRole::*;
|
||||
match self {
|
||||
Admin | Member => true,
|
||||
Guest | Banned => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_only_see_public_descendants(&self) -> bool {
|
||||
use ChannelRole::*;
|
||||
match self {
|
||||
Guest => true,
|
||||
Admin | Member | Banned => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proto::ChannelRole> for ChannelRole {
|
||||
|
|
|
@ -16,7 +16,7 @@ impl Database {
|
|||
connection: ConnectionId,
|
||||
) -> Result<proto::JoinChannelBufferResponse> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_member(channel_id, user_id, &tx)
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &tx)
|
||||
.await?;
|
||||
|
||||
let buffer = channel::Model {
|
||||
|
@ -131,7 +131,7 @@ impl Database {
|
|||
for client_buffer in buffers {
|
||||
let channel_id = ChannelId::from_proto(client_buffer.channel_id);
|
||||
if self
|
||||
.check_user_is_channel_member(channel_id, user_id, &*tx)
|
||||
.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
|
@ -482,9 +482,7 @@ impl Database {
|
|||
)
|
||||
.await?;
|
||||
|
||||
channel_members = self
|
||||
.get_channel_participants_internal(channel_id, &*tx)
|
||||
.await?;
|
||||
channel_members = self.get_channel_participants(channel_id, &*tx).await?;
|
||||
let collaborators = self
|
||||
.get_channel_buffer_collaborators_internal(channel_id, &*tx)
|
||||
.await?;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,7 @@ impl Database {
|
|||
user_id: UserId,
|
||||
) -> Result<()> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_member(channel_id, user_id, &*tx)
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
.await?;
|
||||
channel_chat_participant::ActiveModel {
|
||||
id: ActiveValue::NotSet,
|
||||
|
@ -80,7 +80,7 @@ impl Database {
|
|||
before_message_id: Option<MessageId>,
|
||||
) -> Result<Vec<proto::ChannelMessage>> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_member(channel_id, user_id, &*tx)
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
let mut condition =
|
||||
|
@ -203,6 +203,9 @@ impl Database {
|
|||
nonce: u128,
|
||||
) -> Result<CreatedChannelMessage> {
|
||||
self.transaction(|tx| async move {
|
||||
self.check_user_is_channel_participant(channel_id, user_id, &*tx)
|
||||
.await?;
|
||||
|
||||
let mut rows = channel_chat_participant::Entity::find()
|
||||
.filter(channel_chat_participant::Column::ChannelId.eq(channel_id))
|
||||
.stream(&*tx)
|
||||
|
@ -307,9 +310,7 @@ impl Database {
|
|||
}
|
||||
}
|
||||
|
||||
let mut channel_members = self
|
||||
.get_channel_participants_internal(channel_id, &*tx)
|
||||
.await?;
|
||||
let mut channel_members = self.get_channel_participants(channel_id, &*tx).await?;
|
||||
channel_members.retain(|member| !participant_user_ids.contains(member));
|
||||
|
||||
Ok(CreatedChannelMessage {
|
||||
|
|
|
@ -53,9 +53,7 @@ impl Database {
|
|||
let (channel_id, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel_members;
|
||||
if let Some(channel_id) = channel_id {
|
||||
channel_members = self
|
||||
.get_channel_participants_internal(channel_id, &tx)
|
||||
.await?;
|
||||
channel_members = self.get_channel_participants(channel_id, &tx).await?;
|
||||
} else {
|
||||
channel_members = Vec::new();
|
||||
|
||||
|
@ -423,9 +421,7 @@ impl Database {
|
|||
.await?;
|
||||
|
||||
let room = self.get_room(room_id, &tx).await?;
|
||||
let channel_members = self
|
||||
.get_channel_participants_internal(channel_id, &tx)
|
||||
.await?;
|
||||
let channel_members = self.get_channel_participants(channel_id, &tx).await?;
|
||||
Ok(JoinRoom {
|
||||
room,
|
||||
channel_id: Some(channel_id),
|
||||
|
@ -724,8 +720,7 @@ impl Database {
|
|||
|
||||
let (channel_id, room) = self.get_channel_room(room_id, &tx).await?;
|
||||
let channel_members = if let Some(channel_id) = channel_id {
|
||||
self.get_channel_participants_internal(channel_id, &tx)
|
||||
.await?
|
||||
self.get_channel_participants(channel_id, &tx).await?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
@ -883,8 +878,7 @@ impl Database {
|
|||
};
|
||||
|
||||
let channel_members = if let Some(channel_id) = channel_id {
|
||||
self.get_channel_participants_internal(channel_id, &tx)
|
||||
.await?
|
||||
self.get_channel_participants(channel_id, &tx).await?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ use rpc::proto::ChannelEdge;
|
|||
use sea_orm::ConnectionTrait;
|
||||
use sqlx::migrate::MigrateDatabase;
|
||||
use std::sync::{
|
||||
atomic::{AtomicI32, Ordering::SeqCst},
|
||||
atomic::{AtomicI32, AtomicU32, Ordering::SeqCst},
|
||||
Arc,
|
||||
};
|
||||
|
||||
|
@ -154,17 +154,21 @@ impl Drop for TestDb {
|
|||
}
|
||||
|
||||
/// The second tuples are (channel_id, parent)
|
||||
fn graph(channels: &[(ChannelId, &'static str)], edges: &[(ChannelId, ChannelId)]) -> ChannelGraph {
|
||||
fn graph(
|
||||
channels: &[(ChannelId, &'static str, ChannelRole)],
|
||||
edges: &[(ChannelId, ChannelId)],
|
||||
) -> ChannelGraph {
|
||||
let mut graph = ChannelGraph {
|
||||
channels: vec![],
|
||||
edges: vec![],
|
||||
};
|
||||
|
||||
for (id, name) in channels {
|
||||
for (id, name, role) in channels {
|
||||
graph.channels.push(Channel {
|
||||
id: *id,
|
||||
name: name.to_string(),
|
||||
visibility: ChannelVisibility::Members,
|
||||
role: *role,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -193,3 +197,11 @@ async fn new_test_user(db: &Arc<Database>, email: &str) -> UserId {
|
|||
.unwrap()
|
||||
.user_id
|
||||
}
|
||||
|
||||
static TEST_CONNECTION_ID: AtomicU32 = AtomicU32::new(1);
|
||||
fn new_test_connection(server: ServerId) -> ConnectionId {
|
||||
ConnectionId {
|
||||
id: TEST_CONNECTION_ID.fetch_add(1, SeqCst),
|
||||
owner_id: server.0 as u32,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
queries::channels::ChannelGraph,
|
||||
tests::{graph, new_test_user, TEST_RELEASE_CHANNEL},
|
||||
tests::{graph, new_test_connection, new_test_user, TEST_RELEASE_CHANNEL},
|
||||
ChannelId, ChannelRole, Database, NewUserParams, RoomId,
|
||||
},
|
||||
test_both_dbs,
|
||||
|
@ -11,36 +13,12 @@ use rpc::{
|
|||
proto::{self},
|
||||
ConnectionId,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
|
||||
|
||||
async fn test_channels(db: &Arc<Database>) {
|
||||
let a_id = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
|
||||
let b_id = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let a_id = new_test_user(db, "user1@example.com").await;
|
||||
let b_id = new_test_user(db, "user2@example.com").await;
|
||||
|
||||
let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
|
||||
|
||||
|
@ -55,28 +33,28 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
|
||||
let crdb_id = db.create_sub_channel("crdb", zed_id, a_id).await.unwrap();
|
||||
let livestreaming_id = db
|
||||
.create_channel("livestreaming", Some(zed_id), a_id)
|
||||
.create_sub_channel("livestreaming", zed_id, a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let replace_id = db
|
||||
.create_channel("replace", Some(zed_id), a_id)
|
||||
.create_sub_channel("replace", zed_id, a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut members = db.get_channel_members(replace_id).await.unwrap();
|
||||
let mut members = db
|
||||
.transaction(|tx| async move { Ok(db.get_channel_participants(replace_id, &*tx).await?) })
|
||||
.await
|
||||
.unwrap();
|
||||
members.sort();
|
||||
assert_eq!(members, &[a_id, b_id]);
|
||||
|
||||
let rust_id = db.create_root_channel("rust", a_id).await.unwrap();
|
||||
let cargo_id = db
|
||||
.create_channel("cargo", Some(rust_id), a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let cargo_id = db.create_sub_channel("cargo", rust_id, a_id).await.unwrap();
|
||||
|
||||
let cargo_ra_id = db
|
||||
.create_channel("cargo-ra", Some(cargo_id), a_id)
|
||||
.create_sub_channel("cargo-ra", cargo_id, a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -85,13 +63,13 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace"),
|
||||
(rust_id, "rust"),
|
||||
(cargo_id, "cargo"),
|
||||
(cargo_ra_id, "cargo-ra")
|
||||
(zed_id, "zed", ChannelRole::Admin),
|
||||
(crdb_id, "crdb", ChannelRole::Admin),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
(replace_id, "replace", ChannelRole::Admin),
|
||||
(rust_id, "rust", ChannelRole::Admin),
|
||||
(cargo_id, "cargo", ChannelRole::Admin),
|
||||
(cargo_ra_id, "cargo-ra", ChannelRole::Admin)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
|
@ -108,10 +86,10 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace")
|
||||
(zed_id, "zed", ChannelRole::Member),
|
||||
(crdb_id, "crdb", ChannelRole::Member),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Member),
|
||||
(replace_id, "replace", ChannelRole::Member)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
|
@ -136,10 +114,10 @@ async fn test_channels(db: &Arc<Database>) {
|
|||
result.channels,
|
||||
graph(
|
||||
&[
|
||||
(zed_id, "zed"),
|
||||
(crdb_id, "crdb"),
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(replace_id, "replace")
|
||||
(zed_id, "zed", ChannelRole::Admin),
|
||||
(crdb_id, "crdb", ChannelRole::Admin),
|
||||
(livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
(replace_id, "replace", ChannelRole::Admin)
|
||||
],
|
||||
&[
|
||||
(crdb_id, zed_id),
|
||||
|
@ -173,35 +151,13 @@ test_both_dbs!(
|
|||
async fn test_joining_channels(db: &Arc<Database>) {
|
||||
let owner_id = db.create_server("test").await.unwrap().0 as u32;
|
||||
|
||||
let user_1 = db
|
||||
.create_user(
|
||||
"user1@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user1".into(),
|
||||
github_user_id: 5,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let user_2 = db
|
||||
.create_user(
|
||||
"user2@example.com",
|
||||
false,
|
||||
NewUserParams {
|
||||
github_login: "user2".into(),
|
||||
github_user_id: 6,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.user_id;
|
||||
let user_1 = new_test_user(db, "user1@example.com").await;
|
||||
let user_2 = new_test_user(db, "user2@example.com").await;
|
||||
|
||||
let channel_1 = db.create_root_channel("channel_1", user_1).await.unwrap();
|
||||
|
||||
// can join a room with membership to its channel
|
||||
let (joined_room, _) = db
|
||||
let (joined_room, _, _) = db
|
||||
.join_channel(
|
||||
channel_1,
|
||||
user_1,
|
||||
|
@ -305,7 +261,7 @@ async fn test_channel_invites(db: &Arc<Database>) {
|
|||
.unwrap();
|
||||
|
||||
let channel_1_3 = db
|
||||
.create_channel("channel_3", Some(channel_1_1), user_1)
|
||||
.create_sub_channel("channel_3", channel_1_1, user_1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -318,7 +274,7 @@ async fn test_channel_invites(db: &Arc<Database>) {
|
|||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: user_1.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::ChannelMember {
|
||||
|
@ -407,20 +363,17 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
|
||||
let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
|
||||
|
||||
let crdb_id = db.create_channel("crdb", Some(zed_id), a_id).await.unwrap();
|
||||
let crdb_id = db.create_sub_channel("crdb", zed_id, a_id).await.unwrap();
|
||||
|
||||
let gpui2_id = db
|
||||
.create_channel("gpui2", Some(zed_id), a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let gpui2_id = db.create_sub_channel("gpui2", zed_id, a_id).await.unwrap();
|
||||
|
||||
let livestreaming_id = db
|
||||
.create_channel("livestreaming", Some(crdb_id), a_id)
|
||||
.create_sub_channel("livestreaming", crdb_id, a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let livestreaming_dag_id = db
|
||||
.create_channel("livestreaming_dag", Some(livestreaming_id), a_id)
|
||||
.create_sub_channel("livestreaming_dag", livestreaming_id, a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -447,299 +400,311 @@ async fn test_db_channel_moving(db: &Arc<Database>) {
|
|||
.await
|
||||
.is_err());
|
||||
|
||||
// ========================================================================
|
||||
// Make a link
|
||||
db.link_channel(a_id, livestreaming_id, zed_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Make a link
|
||||
// db.link_channel(a_id, livestreaming_id, zed_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2
|
||||
// zed -- crdb - livestreaming - livestreaming_dag
|
||||
// \---------/
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
],
|
||||
);
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed -- crdb - livestreaming - livestreaming_dag
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Create a new channel below a channel with multiple parents
|
||||
let livestreaming_dag_sub_id = db
|
||||
.create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Create a new channel below a channel with multiple parents
|
||||
// let livestreaming_dag_sub_id = db
|
||||
// .create_channel("livestreaming_dag_sub", Some(livestreaming_dag_id), a_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2
|
||||
// zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// \---------/
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed -- crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Test a complex DAG by making another link
|
||||
let returned_channels = db
|
||||
.link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Test a complex DAG by making another link
|
||||
// let returned_channels = db
|
||||
// .link_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2 /---------------------\
|
||||
// zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// \--------/
|
||||
// // DAG is now:
|
||||
// // /- gpui2 /---------------------\
|
||||
// // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \--------/
|
||||
|
||||
// make sure we're getting just the new link
|
||||
// Not using the assert_dag helper because we want to make sure we're returning the full data
|
||||
pretty_assertions::assert_eq!(
|
||||
returned_channels,
|
||||
graph(
|
||||
&[(livestreaming_dag_sub_id, "livestreaming_dag_sub")],
|
||||
&[(livestreaming_dag_sub_id, livestreaming_id)]
|
||||
)
|
||||
);
|
||||
// // make sure we're getting just the new link
|
||||
// // Not using the assert_dag helper because we want to make sure we're returning the full data
|
||||
// pretty_assertions::assert_eq!(
|
||||
// returned_channels,
|
||||
// graph(
|
||||
// &[(
|
||||
// livestreaming_dag_sub_id,
|
||||
// "livestreaming_dag_sub",
|
||||
// ChannelRole::Admin
|
||||
// )],
|
||||
// &[(livestreaming_dag_sub_id, livestreaming_id)]
|
||||
// )
|
||||
// );
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Test a complex DAG by making another link
|
||||
let returned_channels = db
|
||||
.link_channel(a_id, livestreaming_id, gpui2_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Test a complex DAG by making another link
|
||||
// let returned_channels = db
|
||||
// .link_channel(a_id, livestreaming_id, gpui2_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2 -\ /---------------------\
|
||||
// zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// \---------/
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -\ /---------------------\
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub_id
|
||||
// // \---------/
|
||||
|
||||
// Make sure that we're correctly getting the full sub-dag
|
||||
pretty_assertions::assert_eq!(
|
||||
returned_channels,
|
||||
graph(
|
||||
&[
|
||||
(livestreaming_id, "livestreaming"),
|
||||
(livestreaming_dag_id, "livestreaming_dag"),
|
||||
(livestreaming_dag_sub_id, "livestreaming_dag_sub"),
|
||||
],
|
||||
&[
|
||||
(livestreaming_id, gpui2_id),
|
||||
(livestreaming_dag_id, livestreaming_id),
|
||||
(livestreaming_dag_sub_id, livestreaming_id),
|
||||
(livestreaming_dag_sub_id, livestreaming_dag_id),
|
||||
]
|
||||
)
|
||||
);
|
||||
// // Make sure that we're correctly getting the full sub-dag
|
||||
// pretty_assertions::assert_eq!(
|
||||
// returned_channels,
|
||||
// graph(
|
||||
// &[
|
||||
// (livestreaming_id, "livestreaming", ChannelRole::Admin),
|
||||
// (
|
||||
// livestreaming_dag_id,
|
||||
// "livestreaming_dag",
|
||||
// ChannelRole::Admin
|
||||
// ),
|
||||
// (
|
||||
// livestreaming_dag_sub_id,
|
||||
// "livestreaming_dag_sub",
|
||||
// ChannelRole::Admin
|
||||
// ),
|
||||
// ],
|
||||
// &[
|
||||
// (livestreaming_id, gpui2_id),
|
||||
// (livestreaming_dag_id, livestreaming_id),
|
||||
// (livestreaming_dag_sub_id, livestreaming_id),
|
||||
// (livestreaming_dag_sub_id, livestreaming_dag_id),
|
||||
// ]
|
||||
// )
|
||||
// );
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_id, Some(gpui2_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Test unlinking in a complex DAG by removing the inner link
|
||||
db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Test unlinking in a complex DAG by removing the inner link
|
||||
// db.unlink_channel(a_id, livestreaming_dag_sub_id, livestreaming_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2 -\
|
||||
// zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// \---------/
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -\
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \---------/
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(gpui2_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Test unlinking in a complex DAG by removing the inner link
|
||||
db.unlink_channel(a_id, livestreaming_id, gpui2_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Test unlinking in a complex DAG by removing the inner link
|
||||
// db.unlink_channel(a_id, livestreaming_id, gpui2_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2
|
||||
// zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// \---------/
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// // DAG is now:
|
||||
// // /- gpui2
|
||||
// // zed - crdb -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Test moving DAG nodes by moving livestreaming to be below gpui2
|
||||
db.move_channel(a_id, livestreaming_id, crdb_id, gpui2_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Test moving DAG nodes by moving livestreaming to be below gpui2
|
||||
// db.move_channel(livestreaming_id, Some(crdb_id), gpui2_id, a_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// zed - crdb /
|
||||
// \---------/
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(gpui2_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(gpui2_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// // DAG is now:
|
||||
// // /- gpui2 -- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // zed - crdb /
|
||||
// // \---------/
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (gpui2_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(gpui2_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Deleting a channel should not delete children that still have other parents
|
||||
db.delete_channel(gpui2_id, a_id).await.unwrap();
|
||||
// // ========================================================================
|
||||
// // Deleting a channel should not delete children that still have other parents
|
||||
// db.delete_channel(gpui2_id, a_id).await.unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// zed - crdb
|
||||
// \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// // DAG is now:
|
||||
// // zed - crdb
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Unlinking a channel from it's parent should automatically promote it to a root channel
|
||||
db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
// // ========================================================================
|
||||
// // Unlinking a channel from it's parent should automatically promote it to a root channel
|
||||
// db.unlink_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// crdb
|
||||
// zed
|
||||
// \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // DAG is now:
|
||||
// // crdb
|
||||
// // zed
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, None),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, None),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// You should be able to move a root channel into a non-root channel
|
||||
db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
// // ========================================================================
|
||||
// // You should be able to move a root channel into a non-root channel
|
||||
// db.link_channel(a_id, crdb_id, zed_id).await.unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// zed - crdb
|
||||
// \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // DAG is now:
|
||||
// // zed - crdb
|
||||
// // \- livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// ========================================================================
|
||||
// Prep for DAG deletion test
|
||||
db.link_channel(a_id, livestreaming_id, crdb_id)
|
||||
.await
|
||||
.unwrap();
|
||||
// // ========================================================================
|
||||
// // Prep for DAG deletion test
|
||||
// db.link_channel(a_id, livestreaming_id, crdb_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// DAG is now:
|
||||
// zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// \--------/
|
||||
// // DAG is now:
|
||||
// // zed - crdb - livestreaming - livestreaming_dag - livestreaming_dag_sub
|
||||
// // \--------/
|
||||
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(crdb_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(crdb_id)),
|
||||
(livestreaming_dag_id, Some(livestreaming_id)),
|
||||
(livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
],
|
||||
);
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (crdb_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(zed_id)),
|
||||
// (livestreaming_id, Some(crdb_id)),
|
||||
// (livestreaming_dag_id, Some(livestreaming_id)),
|
||||
// (livestreaming_dag_sub_id, Some(livestreaming_dag_id)),
|
||||
// ],
|
||||
// );
|
||||
|
||||
// Deleting the parent of a DAG should delete the whole DAG:
|
||||
db.delete_channel(zed_id, a_id).await.unwrap();
|
||||
let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
// // Deleting the parent of a DAG should delete the whole DAG:
|
||||
// db.delete_channel(zed_id, a_id).await.unwrap();
|
||||
// let result = db.get_channels_for_user(a_id).await.unwrap();
|
||||
|
||||
assert!(result.channels.is_empty())
|
||||
// assert!(result.channels.is_empty())
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
|
@ -765,12 +730,12 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
|
|||
let zed_id = db.create_root_channel("zed", user_id).await.unwrap();
|
||||
|
||||
let projects_id = db
|
||||
.create_channel("projects", Some(zed_id), user_id)
|
||||
.create_sub_channel("projects", zed_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let livestreaming_id = db
|
||||
.create_channel("livestreaming", Some(projects_id), user_id)
|
||||
.create_sub_channel("livestreaming", projects_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -778,25 +743,37 @@ async fn test_db_channel_moving_bugs(db: &Arc<Database>) {
|
|||
|
||||
// Move to same parent should be a no-op
|
||||
assert!(db
|
||||
.move_channel(user_id, projects_id, zed_id, zed_id)
|
||||
.move_channel(projects_id, Some(zed_id), zed_id, user_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
|
||||
// Stranding a channel should retain it's sub channels
|
||||
db.unlink_channel(user_id, projects_id, zed_id)
|
||||
.await
|
||||
.unwrap();
|
||||
.is_none());
|
||||
|
||||
let result = db.get_channels_for_user(user_id).await.unwrap();
|
||||
assert_dag(
|
||||
result.channels,
|
||||
&[
|
||||
(zed_id, None),
|
||||
(projects_id, None),
|
||||
(projects_id, Some(zed_id)),
|
||||
(livestreaming_id, Some(projects_id)),
|
||||
],
|
||||
);
|
||||
|
||||
// Stranding a channel should retain it's sub channels
|
||||
// Commented out as we don't fix permissions when this happens yet.
|
||||
//
|
||||
// db.unlink_channel(user_id, projects_id, zed_id)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let result = db.get_channels_for_user(user_id).await.unwrap();
|
||||
// assert_dag(
|
||||
// result.channels,
|
||||
// &[
|
||||
// (zed_id, None),
|
||||
// (projects_id, None),
|
||||
// (livestreaming_id, Some(projects_id)),
|
||||
// ],
|
||||
// );
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
|
@ -812,11 +789,11 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
|
||||
let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
|
||||
let active_channel = db
|
||||
.create_channel("active", Some(zed_channel), admin)
|
||||
.create_sub_channel("active", zed_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
let vim_channel = db
|
||||
.create_channel("vim", Some(active_channel), admin)
|
||||
.create_sub_channel("vim", active_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -859,7 +836,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: admin.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::ChannelMember {
|
||||
|
@ -917,7 +894,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: admin.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::ChannelMember {
|
||||
|
@ -958,7 +935,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: admin.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::ChannelMember {
|
||||
|
@ -1006,7 +983,7 @@ async fn test_user_is_channel_participant(db: &Arc<Database>) {
|
|||
&[
|
||||
proto::ChannelMember {
|
||||
user_id: admin.to_proto(),
|
||||
kind: proto::channel_member::Kind::Member.into(),
|
||||
kind: proto::channel_member::Kind::AncestorMember.into(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
},
|
||||
proto::ChannelMember {
|
||||
|
@ -1041,17 +1018,17 @@ async fn test_user_joins_correct_channel(db: &Arc<Database>) {
|
|||
let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
|
||||
|
||||
let active_channel = db
|
||||
.create_channel("active", Some(zed_channel), admin)
|
||||
.create_sub_channel("active", zed_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let vim_channel = db
|
||||
.create_channel("vim", Some(active_channel), admin)
|
||||
.create_sub_channel("vim", active_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let vim2_channel = db
|
||||
.create_channel("vim2", Some(vim_channel), admin)
|
||||
.create_sub_channel("vim2", vim_channel, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1068,15 +1045,52 @@ async fn test_user_joins_correct_channel(db: &Arc<Database>) {
|
|||
.unwrap();
|
||||
|
||||
let most_public = db
|
||||
.transaction(
|
||||
|tx| async move { db.most_public_ancestor_for_channel(vim_channel, &*tx).await },
|
||||
)
|
||||
.transaction(|tx| async move {
|
||||
Ok(db
|
||||
.public_path_to_channel(vim_channel, &tx)
|
||||
.await?
|
||||
.first()
|
||||
.cloned())
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(most_public, Some(zed_channel))
|
||||
}
|
||||
|
||||
test_both_dbs!(
|
||||
test_guest_access,
|
||||
test_guest_access_postgres,
|
||||
test_guest_access_sqlite
|
||||
);
|
||||
|
||||
async fn test_guest_access(db: &Arc<Database>) {
|
||||
let server = db.create_server("test").await.unwrap();
|
||||
|
||||
let admin = new_test_user(db, "admin@example.com").await;
|
||||
let guest = new_test_user(db, "guest@example.com").await;
|
||||
let guest_connection = new_test_connection(server);
|
||||
|
||||
let zed_channel = db.create_root_channel("zed", admin).await.unwrap();
|
||||
db.set_channel_visibility(zed_channel, crate::db::ChannelVisibility::Public, admin)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(db
|
||||
.join_channel_chat(zed_channel, guest_connection, guest)
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
db.join_channel(zed_channel, guest, guest_connection, TEST_RELEASE_CHANNEL)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(db
|
||||
.join_channel_chat(zed_channel, guest_connection, guest)
|
||||
.await
|
||||
.is_ok())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_dag(actual: ChannelGraph, expected: &[(ChannelId, Option<ChannelId>)]) {
|
||||
let mut actual_map: HashMap<ChannelId, HashSet<ChannelId>> = HashMap::default();
|
||||
|
|
|
@ -15,18 +15,22 @@ test_both_dbs!(
|
|||
|
||||
async fn test_channel_message_retrieval(db: &Arc<Database>) {
|
||||
let user = new_test_user(db, "user@example.com").await;
|
||||
let channel = db.create_channel("channel", None, user).await.unwrap();
|
||||
let result = db.create_channel("channel", None, user).await.unwrap();
|
||||
|
||||
let owner_id = db.create_server("test").await.unwrap().0 as u32;
|
||||
db.join_channel_chat(channel, rpc::ConnectionId { owner_id, id: 0 }, user)
|
||||
.await
|
||||
.unwrap();
|
||||
db.join_channel_chat(
|
||||
result.channel.id,
|
||||
rpc::ConnectionId { owner_id, id: 0 },
|
||||
user,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut all_messages = Vec::new();
|
||||
for i in 0..10 {
|
||||
all_messages.push(
|
||||
db.create_channel_message(
|
||||
channel,
|
||||
result.channel.id,
|
||||
user,
|
||||
&i.to_string(),
|
||||
&[],
|
||||
|
@ -41,7 +45,7 @@ async fn test_channel_message_retrieval(db: &Arc<Database>) {
|
|||
}
|
||||
|
||||
let messages = db
|
||||
.get_channel_messages(channel, user, 3, None)
|
||||
.get_channel_messages(result.channel.id, user, 3, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
|
@ -51,7 +55,7 @@ async fn test_channel_message_retrieval(db: &Arc<Database>) {
|
|||
|
||||
let messages = db
|
||||
.get_channel_messages(
|
||||
channel,
|
||||
result.channel.id,
|
||||
user,
|
||||
4,
|
||||
Some(MessageId::from_proto(all_messages[6])),
|
||||
|
@ -74,7 +78,7 @@ async fn test_channel_message_nonces(db: &Arc<Database>) {
|
|||
let user_a = new_test_user(db, "user_a@example.com").await;
|
||||
let user_b = new_test_user(db, "user_b@example.com").await;
|
||||
let user_c = new_test_user(db, "user_c@example.com").await;
|
||||
let channel = db.create_channel("channel", None, user_a).await.unwrap();
|
||||
let channel = db.create_root_channel("channel", user_a).await.unwrap();
|
||||
db.invite_channel_member(channel, user_b, user_a, ChannelRole::Member)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -206,8 +210,8 @@ async fn test_unseen_channel_messages(db: &Arc<Database>) {
|
|||
let user = new_test_user(db, "user_a@example.com").await;
|
||||
let observer = new_test_user(db, "user_b@example.com").await;
|
||||
|
||||
let channel_1 = db.create_channel("channel", None, user).await.unwrap();
|
||||
let channel_2 = db.create_channel("channel-2", None, user).await.unwrap();
|
||||
let channel_1 = db.create_root_channel("channel", user).await.unwrap();
|
||||
let channel_2 = db.create_root_channel("channel-2", user).await.unwrap();
|
||||
|
||||
db.invite_channel_member(channel_1, observer, user, ChannelRole::Member)
|
||||
.await
|
||||
|
@ -362,7 +366,12 @@ async fn test_channel_message_mentions(db: &Arc<Database>) {
|
|||
let user_b = new_test_user(db, "user_b@example.com").await;
|
||||
let user_c = new_test_user(db, "user_c@example.com").await;
|
||||
|
||||
let channel = db.create_channel("channel", None, user_a).await.unwrap();
|
||||
let channel = db
|
||||
.create_channel("channel", None, user_a)
|
||||
.await
|
||||
.unwrap()
|
||||
.channel
|
||||
.id;
|
||||
db.invite_channel_member(channel, user_b, user_a, ChannelRole::Member)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
@ -3,8 +3,11 @@ mod connection_pool;
|
|||
use crate::{
|
||||
auth,
|
||||
db::{
|
||||
self, BufferId, ChannelId, ChannelVisibility, ChannelsForUser, CreatedChannelMessage,
|
||||
Database, MessageId, NotificationId, ProjectId, RoomId, ServerId, User, UserId,
|
||||
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, CreateChannelResult,
|
||||
CreatedChannelMessage, Database, InviteMemberResult, MembershipUpdated, MessageId,
|
||||
MoveChannelResult, NotificationId, ProjectId, RemoveChannelMemberResult,
|
||||
RenameChannelResult, RespondToChannelInvite, RoomId, ServerId, SetChannelVisibilityResult,
|
||||
User, UserId,
|
||||
},
|
||||
executor::Executor,
|
||||
AppState, Result,
|
||||
|
@ -38,8 +41,8 @@ use lazy_static::lazy_static;
|
|||
use prometheus::{register_int_gauge, IntGauge};
|
||||
use rpc::{
|
||||
proto::{
|
||||
self, Ack, AnyTypedEnvelope, ChannelEdge, EntityMessage, EnvelopedMessage,
|
||||
LiveKitConnectionInfo, RequestMessage, UpdateChannelBufferCollaborators,
|
||||
self, Ack, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, LiveKitConnectionInfo,
|
||||
RequestMessage, UpdateChannelBufferCollaborators,
|
||||
},
|
||||
Connection, ConnectionId, Peer, Receipt, TypedEnvelope,
|
||||
};
|
||||
|
@ -594,7 +597,7 @@ impl Server {
|
|||
let mut pool = this.connection_pool.lock();
|
||||
pool.add_connection(connection_id, user_id, user.admin);
|
||||
this.peer.send(connection_id, build_initial_contacts_update(contacts, &pool))?;
|
||||
this.peer.send(connection_id, build_initial_channels_update(
|
||||
this.peer.send(connection_id, build_channels_update(
|
||||
channels_for_user,
|
||||
channel_invites
|
||||
))?;
|
||||
|
@ -951,6 +954,7 @@ async fn create_room(
|
|||
Some(proto::LiveKitConnectionInfo {
|
||||
server_url: live_kit.url().into(),
|
||||
token,
|
||||
can_publish: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1031,6 +1035,7 @@ async fn join_room(
|
|||
Some(proto::LiveKitConnectionInfo {
|
||||
server_url: live_kit.url().into(),
|
||||
token,
|
||||
can_publish: true,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -2217,38 +2222,21 @@ async fn create_channel(
|
|||
let db = session.db().await;
|
||||
|
||||
let parent_id = request.parent_id.map(|id| ChannelId::from_proto(id));
|
||||
let id = db
|
||||
let CreateChannelResult {
|
||||
channel,
|
||||
participants_to_update,
|
||||
} = db
|
||||
.create_channel(&request.name, parent_id, session.user_id)
|
||||
.await?;
|
||||
|
||||
let channel = proto::Channel {
|
||||
id: id.to_proto(),
|
||||
name: request.name,
|
||||
visibility: proto::ChannelVisibility::Members as i32,
|
||||
};
|
||||
|
||||
response.send(proto::CreateChannelResponse {
|
||||
channel: Some(channel.clone()),
|
||||
channel: Some(channel.to_proto()),
|
||||
parent_id: request.parent_id,
|
||||
})?;
|
||||
|
||||
let Some(parent_id) = parent_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let update = proto::UpdateChannels {
|
||||
channels: vec![channel],
|
||||
insert_edge: vec![ChannelEdge {
|
||||
parent_id: parent_id.to_proto(),
|
||||
channel_id: id.to_proto(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let user_ids_to_notify = db.get_channel_members(parent_id).await?;
|
||||
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for user_id in user_ids_to_notify {
|
||||
for (user_id, channels) in participants_to_update {
|
||||
let update = build_channels_update(channels, vec![]);
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
if user_id == session.user_id {
|
||||
continue;
|
||||
|
@ -2297,7 +2285,10 @@ async fn invite_channel_member(
|
|||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let invitee_id = UserId::from_proto(request.user_id);
|
||||
let notifications = db
|
||||
let InviteMemberResult {
|
||||
channel,
|
||||
notifications,
|
||||
} = db
|
||||
.invite_channel_member(
|
||||
channel_id,
|
||||
invitee_id,
|
||||
|
@ -2306,21 +2297,17 @@ async fn invite_channel_member(
|
|||
)
|
||||
.await?;
|
||||
|
||||
let channel = db.get_channel(channel_id, session.user_id).await?;
|
||||
let update = proto::UpdateChannels {
|
||||
channel_invitations: vec![channel.to_proto()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update.channel_invitations.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
visibility: channel.visibility.into(),
|
||||
name: channel.name,
|
||||
});
|
||||
|
||||
let pool = session.connection_pool().await;
|
||||
for connection_id in pool.user_connection_ids(invitee_id) {
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for connection_id in connection_pool.user_connection_ids(invitee_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
|
||||
send_notifications(&*pool, &session.peer, notifications);
|
||||
send_notifications(&*connection_pool, &session.peer, notifications);
|
||||
|
||||
response.send(proto::Ack {})?;
|
||||
Ok(())
|
||||
|
@ -2335,20 +2322,22 @@ async fn remove_channel_member(
|
|||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let member_id = UserId::from_proto(request.user_id);
|
||||
|
||||
let removed_notification_id = db
|
||||
let RemoveChannelMemberResult {
|
||||
membership_update,
|
||||
notification_id,
|
||||
} = db
|
||||
.remove_channel_member(channel_id, member_id, session.user_id)
|
||||
.await?;
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update.delete_channels.push(channel_id.to_proto());
|
||||
|
||||
for connection_id in session
|
||||
.connection_pool()
|
||||
.await
|
||||
.user_connection_ids(member_id)
|
||||
{
|
||||
session.peer.send(connection_id, update.clone()).trace_err();
|
||||
if let Some(notification_id) = removed_notification_id {
|
||||
let connection_pool = &session.connection_pool().await;
|
||||
notify_membership_updated(
|
||||
&connection_pool,
|
||||
membership_update,
|
||||
member_id,
|
||||
&session.peer,
|
||||
);
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
if let Some(notification_id) = notification_id {
|
||||
session
|
||||
.peer
|
||||
.send(
|
||||
|
@ -2374,22 +2363,27 @@ async fn set_channel_visibility(
|
|||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let visibility = request.visibility().into();
|
||||
|
||||
let channel = db
|
||||
let SetChannelVisibilityResult {
|
||||
participants_to_update,
|
||||
participants_to_remove,
|
||||
channels_to_remove,
|
||||
} = db
|
||||
.set_channel_visibility(channel_id, visibility, session.user_id)
|
||||
.await?;
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update.channels.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
visibility: channel.visibility.into(),
|
||||
});
|
||||
|
||||
let member_ids = db.get_channel_members(channel_id).await?;
|
||||
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for member_id in member_ids {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
for (user_id, channels) in participants_to_update {
|
||||
let update = build_channels_update(channels, vec![]);
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
for user_id in participants_to_remove {
|
||||
let update = proto::UpdateChannels {
|
||||
delete_channels: channels_to_remove.iter().map(|id| id.to_proto()).collect(),
|
||||
..Default::default()
|
||||
};
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
@ -2406,7 +2400,7 @@ async fn set_channel_member_role(
|
|||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let member_id = UserId::from_proto(request.user_id);
|
||||
let channel_member = db
|
||||
let result = db
|
||||
.set_channel_member_role(
|
||||
channel_id,
|
||||
session.user_id,
|
||||
|
@ -2415,22 +2409,30 @@ async fn set_channel_member_role(
|
|||
)
|
||||
.await?;
|
||||
|
||||
let channel = db.get_channel(channel_id, session.user_id).await?;
|
||||
match result {
|
||||
db::SetMemberRoleResult::MembershipUpdated(membership_update) => {
|
||||
let connection_pool = session.connection_pool().await;
|
||||
notify_membership_updated(
|
||||
&connection_pool,
|
||||
membership_update,
|
||||
member_id,
|
||||
&session.peer,
|
||||
)
|
||||
}
|
||||
db::SetMemberRoleResult::InviteUpdated(channel) => {
|
||||
let update = proto::UpdateChannels {
|
||||
channel_invitations: vec![channel.to_proto()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
if channel_member.accepted {
|
||||
update.channel_permissions.push(proto::ChannelPermission {
|
||||
channel_id: channel.id.to_proto(),
|
||||
role: request.role,
|
||||
});
|
||||
}
|
||||
|
||||
for connection_id in session
|
||||
.connection_pool()
|
||||
.await
|
||||
.user_connection_ids(member_id)
|
||||
{
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
for connection_id in session
|
||||
.connection_pool()
|
||||
.await
|
||||
.user_connection_ids(member_id)
|
||||
{
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.send(proto::Ack {})?;
|
||||
|
@ -2444,26 +2446,25 @@ async fn rename_channel(
|
|||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let channel = db
|
||||
let RenameChannelResult {
|
||||
channel,
|
||||
participants_to_update,
|
||||
} = db
|
||||
.rename_channel(channel_id, session.user_id, &request.name)
|
||||
.await?;
|
||||
|
||||
let channel = proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
visibility: channel.visibility.into(),
|
||||
};
|
||||
response.send(proto::RenameChannelResponse {
|
||||
channel: Some(channel.clone()),
|
||||
channel: Some(channel.to_proto()),
|
||||
})?;
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update.channels.push(channel);
|
||||
|
||||
let member_ids = db.get_channel_members(channel_id).await?;
|
||||
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for member_id in member_ids {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
for (user_id, channel) in participants_to_update {
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
let update = proto::UpdateChannels {
|
||||
channels: vec![channel.to_proto()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
@ -2471,6 +2472,8 @@ async fn rename_channel(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Implement in terms of symlinks
|
||||
// Current behavior of this is more like 'Move root channel'
|
||||
async fn link_channel(
|
||||
request: proto::LinkChannel,
|
||||
response: Response<proto::LinkChannel>,
|
||||
|
@ -2479,64 +2482,26 @@ async fn link_channel(
|
|||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let to = ChannelId::from_proto(request.to);
|
||||
let channels_to_send = db.link_channel(session.user_id, channel_id, to).await?;
|
||||
|
||||
let members = db.get_channel_members(to).await?;
|
||||
let connection_pool = session.connection_pool().await;
|
||||
let update = proto::UpdateChannels {
|
||||
channels: channels_to_send
|
||||
.channels
|
||||
.into_iter()
|
||||
.map(|channel| proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
visibility: channel.visibility.into(),
|
||||
name: channel.name,
|
||||
})
|
||||
.collect(),
|
||||
insert_edge: channels_to_send.edges,
|
||||
..Default::default()
|
||||
};
|
||||
for member_id in members {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
let result = db
|
||||
.move_channel(channel_id, None, to, session.user_id)
|
||||
.await?;
|
||||
drop(db);
|
||||
|
||||
notify_channel_moved(result, session).await?;
|
||||
|
||||
response.send(Ack {})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Implement in terms of symlinks
|
||||
async fn unlink_channel(
|
||||
request: proto::UnlinkChannel,
|
||||
response: Response<proto::UnlinkChannel>,
|
||||
session: Session,
|
||||
_request: proto::UnlinkChannel,
|
||||
_response: Response<proto::UnlinkChannel>,
|
||||
_session: Session,
|
||||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let from = ChannelId::from_proto(request.from);
|
||||
|
||||
db.unlink_channel(session.user_id, channel_id, from).await?;
|
||||
|
||||
let members = db.get_channel_members(from).await?;
|
||||
|
||||
let update = proto::UpdateChannels {
|
||||
delete_edge: vec![proto::ChannelEdge {
|
||||
channel_id: channel_id.to_proto(),
|
||||
parent_id: from.to_proto(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for member_id in members {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
response.send(Ack {})?;
|
||||
|
||||
Ok(())
|
||||
Err(anyhow!("unimplemented").into())
|
||||
}
|
||||
|
||||
async fn move_channel(
|
||||
|
@ -2549,53 +2514,46 @@ async fn move_channel(
|
|||
let from_parent = ChannelId::from_proto(request.from);
|
||||
let to = ChannelId::from_proto(request.to);
|
||||
|
||||
let channels_to_send = db
|
||||
.move_channel(session.user_id, channel_id, from_parent, to)
|
||||
let result = db
|
||||
.move_channel(channel_id, Some(from_parent), to, session.user_id)
|
||||
.await?;
|
||||
drop(db);
|
||||
|
||||
if channels_to_send.is_empty() {
|
||||
response.send(Ack {})?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let members_from = db.get_channel_members(from_parent).await?;
|
||||
let members_to = db.get_channel_members(to).await?;
|
||||
|
||||
let update = proto::UpdateChannels {
|
||||
delete_edge: vec![proto::ChannelEdge {
|
||||
channel_id: channel_id.to_proto(),
|
||||
parent_id: from_parent.to_proto(),
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for member_id in members_from {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
let update = proto::UpdateChannels {
|
||||
channels: channels_to_send
|
||||
.channels
|
||||
.into_iter()
|
||||
.map(|channel| proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
visibility: channel.visibility.into(),
|
||||
name: channel.name,
|
||||
})
|
||||
.collect(),
|
||||
insert_edge: channels_to_send.edges,
|
||||
..Default::default()
|
||||
};
|
||||
for member_id in members_to {
|
||||
for connection_id in connection_pool.user_connection_ids(member_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
notify_channel_moved(result, session).await?;
|
||||
|
||||
response.send(Ack {})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn notify_channel_moved(result: Option<MoveChannelResult>, session: Session) -> Result<()> {
|
||||
let Some(MoveChannelResult {
|
||||
participants_to_remove,
|
||||
participants_to_update,
|
||||
moved_channels,
|
||||
}) = result
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let moved_channels: Vec<u64> = moved_channels.iter().map(|id| id.to_proto()).collect();
|
||||
|
||||
let connection_pool = session.connection_pool().await;
|
||||
for (user_id, channels) in participants_to_update {
|
||||
let mut update = build_channels_update(channels, vec![]);
|
||||
update.delete_channels = moved_channels.clone();
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
for user_id in participants_to_remove {
|
||||
let update = proto::UpdateChannels {
|
||||
delete_channels: moved_channels.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -2620,81 +2578,39 @@ async fn respond_to_channel_invite(
|
|||
) -> Result<()> {
|
||||
let db = session.db().await;
|
||||
let channel_id = ChannelId::from_proto(request.channel_id);
|
||||
let notifications = db
|
||||
let RespondToChannelInvite {
|
||||
membership_update,
|
||||
notifications,
|
||||
} = db
|
||||
.respond_to_channel_invite(channel_id, session.user_id, request.accept)
|
||||
.await?;
|
||||
|
||||
if request.accept {
|
||||
channel_membership_updated(db, channel_id, &session).await?;
|
||||
let connection_pool = session.connection_pool().await;
|
||||
if let Some(membership_update) = membership_update {
|
||||
notify_membership_updated(
|
||||
&connection_pool,
|
||||
membership_update,
|
||||
session.user_id,
|
||||
&session.peer,
|
||||
);
|
||||
} else {
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update
|
||||
.remove_channel_invitations
|
||||
.push(channel_id.to_proto());
|
||||
session.peer.send(session.connection_id, update)?;
|
||||
}
|
||||
let update = proto::UpdateChannels {
|
||||
remove_channel_invitations: vec![channel_id.to_proto()],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for connection_id in connection_pool.user_connection_ids(session.user_id) {
|
||||
session.peer.send(connection_id, update.clone())?;
|
||||
}
|
||||
};
|
||||
|
||||
send_notifications(&*connection_pool, &session.peer, notifications);
|
||||
|
||||
send_notifications(
|
||||
&*session.connection_pool().await,
|
||||
&session.peer,
|
||||
notifications,
|
||||
);
|
||||
response.send(proto::Ack {})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn channel_membership_updated(
|
||||
db: tokio::sync::MutexGuard<'_, DbHandle>,
|
||||
channel_id: ChannelId,
|
||||
session: &Session,
|
||||
) -> Result<(), crate::Error> {
|
||||
let mut update = proto::UpdateChannels::default();
|
||||
update
|
||||
.remove_channel_invitations
|
||||
.push(channel_id.to_proto());
|
||||
|
||||
let result = db.get_channel_for_user(channel_id, session.user_id).await?;
|
||||
update.channels.extend(
|
||||
result
|
||||
.channels
|
||||
.channels
|
||||
.into_iter()
|
||||
.map(|channel| proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
visibility: channel.visibility.into(),
|
||||
name: channel.name,
|
||||
}),
|
||||
);
|
||||
update.unseen_channel_messages = result.channel_messages;
|
||||
update.unseen_channel_buffer_changes = result.unseen_buffer_changes;
|
||||
update.insert_edge = result.channels.edges;
|
||||
update
|
||||
.channel_participants
|
||||
.extend(
|
||||
result
|
||||
.channel_participants
|
||||
.into_iter()
|
||||
.map(|(channel_id, user_ids)| proto::ChannelParticipants {
|
||||
channel_id: channel_id.to_proto(),
|
||||
participant_user_ids: user_ids.into_iter().map(UserId::to_proto).collect(),
|
||||
}),
|
||||
);
|
||||
update
|
||||
.channel_permissions
|
||||
.extend(
|
||||
result
|
||||
.channels_with_admin_privileges
|
||||
.into_iter()
|
||||
.map(|channel_id| proto::ChannelPermission {
|
||||
channel_id: channel_id.to_proto(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
}),
|
||||
);
|
||||
session.peer.send(session.connection_id, update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn join_channel(
|
||||
request: proto::JoinChannel,
|
||||
response: Response<proto::JoinChannel>,
|
||||
|
@ -2727,7 +2643,7 @@ async fn join_channel_internal(
|
|||
leave_room_for_session(&session).await?;
|
||||
let db = session.db().await;
|
||||
|
||||
let (joined_room, joined_channel) = db
|
||||
let (joined_room, membership_updated, role) = db
|
||||
.join_channel(
|
||||
channel_id,
|
||||
session.user_id,
|
||||
|
@ -2737,16 +2653,32 @@ async fn join_channel_internal(
|
|||
.await?;
|
||||
|
||||
let live_kit_connection_info = session.live_kit_client.as_ref().and_then(|live_kit| {
|
||||
let token = live_kit
|
||||
.room_token(
|
||||
&joined_room.room.live_kit_room,
|
||||
&session.user_id.to_string(),
|
||||
let (can_publish, token) = if role == ChannelRole::Guest {
|
||||
(
|
||||
false,
|
||||
live_kit
|
||||
.guest_token(
|
||||
&joined_room.room.live_kit_room,
|
||||
&session.user_id.to_string(),
|
||||
)
|
||||
.trace_err()?,
|
||||
)
|
||||
.trace_err()?;
|
||||
} else {
|
||||
(
|
||||
true,
|
||||
live_kit
|
||||
.room_token(
|
||||
&joined_room.room.live_kit_room,
|
||||
&session.user_id.to_string(),
|
||||
)
|
||||
.trace_err()?,
|
||||
)
|
||||
};
|
||||
|
||||
Some(LiveKitConnectionInfo {
|
||||
server_url: live_kit.url().into(),
|
||||
token,
|
||||
can_publish,
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -2756,8 +2688,14 @@ async fn join_channel_internal(
|
|||
live_kit_connection_info,
|
||||
})?;
|
||||
|
||||
if let Some(joined_channel) = joined_channel {
|
||||
channel_membership_updated(db, joined_channel, &session).await?
|
||||
let connection_pool = session.connection_pool().await;
|
||||
if let Some(membership_updated) = membership_updated {
|
||||
notify_membership_updated(
|
||||
&connection_pool,
|
||||
membership_updated,
|
||||
session.user_id,
|
||||
&session.peer,
|
||||
);
|
||||
}
|
||||
|
||||
room_updated(&joined_room.room, &session.peer);
|
||||
|
@ -3281,7 +3219,26 @@ fn to_tungstenite_message(message: AxumMessage) -> TungsteniteMessage {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_initial_channels_update(
|
||||
fn notify_membership_updated(
|
||||
connection_pool: &ConnectionPool,
|
||||
result: MembershipUpdated,
|
||||
user_id: UserId,
|
||||
peer: &Peer,
|
||||
) {
|
||||
let mut update = build_channels_update(result.new_channels, vec![]);
|
||||
update.delete_channels = result
|
||||
.removed_channels
|
||||
.into_iter()
|
||||
.map(|id| id.to_proto())
|
||||
.collect();
|
||||
update.remove_channel_invitations = vec![result.channel_id.to_proto()];
|
||||
|
||||
for connection_id in connection_pool.user_connection_ids(user_id) {
|
||||
peer.send(connection_id, update.clone()).trace_err();
|
||||
}
|
||||
}
|
||||
|
||||
fn build_channels_update(
|
||||
channels: ChannelsForUser,
|
||||
channel_invites: Vec<db::Channel>,
|
||||
) -> proto::UpdateChannels {
|
||||
|
@ -3292,6 +3249,7 @@ fn build_initial_channels_update(
|
|||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
visibility: channel.visibility.into(),
|
||||
role: channel.role.into(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3308,24 +3266,12 @@ fn build_initial_channels_update(
|
|||
});
|
||||
}
|
||||
|
||||
update
|
||||
.channel_permissions
|
||||
.extend(
|
||||
channels
|
||||
.channels_with_admin_privileges
|
||||
.into_iter()
|
||||
.map(|id| proto::ChannelPermission {
|
||||
channel_id: id.to_proto(),
|
||||
role: proto::ChannelRole::Admin.into(),
|
||||
}),
|
||||
);
|
||||
|
||||
for channel in channel_invites {
|
||||
update.channel_invitations.push(proto::Channel {
|
||||
id: channel.id.to_proto(),
|
||||
name: channel.name,
|
||||
// TODO: Visibility
|
||||
visibility: ChannelVisibility::Public.into(),
|
||||
visibility: channel.visibility.into(),
|
||||
role: channel.role.into(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -410,10 +410,10 @@ async fn test_channel_buffer_disconnect(
|
|||
server.disconnect_client(client_a.peer_id().unwrap());
|
||||
deterministic.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
|
||||
|
||||
channel_buffer_a.update(cx_a, |buffer, _| {
|
||||
channel_buffer_a.update(cx_a, |buffer, cx| {
|
||||
assert_eq!(
|
||||
buffer.channel().as_ref(),
|
||||
&channel(channel_id, "the-channel")
|
||||
buffer.channel(cx).unwrap().as_ref(),
|
||||
&channel(channel_id, "the-channel", proto::ChannelRole::Admin)
|
||||
);
|
||||
assert!(!buffer.is_connected());
|
||||
});
|
||||
|
@ -435,18 +435,16 @@ async fn test_channel_buffer_disconnect(
|
|||
deterministic.run_until_parked();
|
||||
|
||||
// Channel buffer observed the deletion
|
||||
channel_buffer_b.update(cx_b, |buffer, _| {
|
||||
assert_eq!(
|
||||
buffer.channel().as_ref(),
|
||||
&channel(channel_id, "the-channel")
|
||||
);
|
||||
channel_buffer_b.update(cx_b, |buffer, cx| {
|
||||
assert!(buffer.channel(cx).is_none());
|
||||
assert!(!buffer.is_connected());
|
||||
});
|
||||
}
|
||||
|
||||
fn channel(id: u64, name: &'static str) -> Channel {
|
||||
fn channel(id: u64, name: &'static str, role: proto::ChannelRole) -> Channel {
|
||||
Channel {
|
||||
id,
|
||||
role,
|
||||
name: name.to_string(),
|
||||
visibility: proto::ChannelVisibility::Members,
|
||||
unseen_note_version: None,
|
||||
|
@ -698,7 +696,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
.await
|
||||
.unwrap();
|
||||
channel_view_1_a.update(cx_a, |notes, cx| {
|
||||
assert_eq!(notes.channel(cx).name, "channel-1");
|
||||
assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
|
||||
notes.editor.update(cx, |editor, cx| {
|
||||
editor.insert("Hello from A.", cx);
|
||||
editor.change_selections(None, cx, |selections| {
|
||||
|
@ -730,7 +728,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
.expect("active item is not a channel view")
|
||||
});
|
||||
channel_view_1_b.read_with(cx_b, |notes, cx| {
|
||||
assert_eq!(notes.channel(cx).name, "channel-1");
|
||||
assert_eq!(notes.channel(cx).unwrap().name, "channel-1");
|
||||
let editor = notes.editor.read(cx);
|
||||
assert_eq!(editor.text(cx), "Hello from A.");
|
||||
assert_eq!(editor.selections.ranges::<usize>(cx), &[3..4]);
|
||||
|
@ -742,7 +740,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
.await
|
||||
.unwrap();
|
||||
channel_view_2_a.read_with(cx_a, |notes, cx| {
|
||||
assert_eq!(notes.channel(cx).name, "channel-2");
|
||||
assert_eq!(notes.channel(cx).unwrap().name, "channel-2");
|
||||
});
|
||||
|
||||
// Client B is taken to the notes for channel 2.
|
||||
|
@ -759,7 +757,7 @@ async fn test_following_to_channel_notes_without_a_shared_project(
|
|||
.expect("active item is not a channel view")
|
||||
});
|
||||
channel_view_2_b.read_with(cx_b, |notes, cx| {
|
||||
assert_eq!(notes.channel(cx).name, "channel-2");
|
||||
assert_eq!(notes.channel(cx).unwrap().name, "channel-2");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -48,7 +48,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
let db = &server.app_state.db;
|
||||
for ix in 0..CHANNEL_COUNT {
|
||||
let id = db
|
||||
.create_channel(&format!("channel-{ix}"), None, users[0].user_id)
|
||||
.create_root_channel(&format!("channel-{ix}"), users[0].user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
for user in &users[1..] {
|
||||
|
@ -98,15 +98,16 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
|
||||
30..=40 => {
|
||||
if let Some(buffer) = channel_buffers.iter().choose(rng) {
|
||||
let channel_name = buffer.read_with(cx, |b, _| b.channel().name.clone());
|
||||
let channel_name =
|
||||
buffer.read_with(cx, |b, cx| b.channel(cx).unwrap().name.clone());
|
||||
break ChannelBufferOperation::LeaveChannelNotes { channel_name };
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
if let Some(buffer) = channel_buffers.iter().choose(rng) {
|
||||
break buffer.read_with(cx, |b, _| {
|
||||
let channel_name = b.channel().name.clone();
|
||||
break buffer.read_with(cx, |b, cx| {
|
||||
let channel_name = b.channel(cx).unwrap().name.clone();
|
||||
let edits = b
|
||||
.buffer()
|
||||
.read_with(cx, |buffer, _| buffer.get_random_edits(rng, 3));
|
||||
|
@ -153,7 +154,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
let buffer = cx.update(|cx| {
|
||||
let mut left_buffer = Err(TestError::Inapplicable);
|
||||
client.channel_buffers().retain(|buffer| {
|
||||
if buffer.read(cx).channel().name == channel_name {
|
||||
if buffer.read(cx).channel(cx).unwrap().name == channel_name {
|
||||
left_buffer = Ok(buffer.clone());
|
||||
false
|
||||
} else {
|
||||
|
@ -179,7 +180,9 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
client
|
||||
.channel_buffers()
|
||||
.iter()
|
||||
.find(|buffer| buffer.read(cx).channel().name == channel_name)
|
||||
.find(|buffer| {
|
||||
buffer.read(cx).channel(cx).unwrap().name == channel_name
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
.ok_or_else(|| TestError::Inapplicable)?;
|
||||
|
@ -250,7 +253,7 @@ impl RandomizedTest for RandomChannelBufferTest {
|
|||
if let Some(channel_buffer) = client
|
||||
.channel_buffers()
|
||||
.iter()
|
||||
.find(|b| b.read(cx).channel().id == channel_id.to_proto())
|
||||
.find(|b| b.read(cx).channel_id == channel_id.to_proto())
|
||||
{
|
||||
let channel_buffer = channel_buffer.read(cx);
|
||||
|
||||
|
|
|
@ -611,38 +611,6 @@ impl TestClient {
|
|||
) -> WindowHandle<Workspace> {
|
||||
cx.add_window(|cx| Workspace::new(0, project.clone(), self.app_state.clone(), cx))
|
||||
}
|
||||
|
||||
pub async fn add_admin_to_channel(
|
||||
&self,
|
||||
user: (&TestClient, &mut TestAppContext),
|
||||
channel: u64,
|
||||
cx_self: &mut TestAppContext,
|
||||
) {
|
||||
let (other_client, other_cx) = user;
|
||||
|
||||
cx_self
|
||||
.read(ChannelStore::global)
|
||||
.update(cx_self, |channel_store, cx| {
|
||||
channel_store.invite_member(
|
||||
channel,
|
||||
other_client.user_id().unwrap(),
|
||||
ChannelRole::Admin,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx_self.foreground().run_until_parked();
|
||||
|
||||
other_cx
|
||||
.read(ChannelStore::global)
|
||||
.update(other_cx, |channel_store, cx| {
|
||||
channel_store.respond_to_channel_invite(channel, true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestClient {
|
||||
|
|
|
@ -61,6 +61,7 @@ postage.workspace = true
|
|||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
time.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
call = { path = "../call", features = ["test-support"] }
|
||||
|
|
|
@ -15,13 +15,14 @@ use gpui::{
|
|||
ViewContext, ViewHandle,
|
||||
};
|
||||
use project::Project;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemHandle},
|
||||
item::{FollowableItem, Item, ItemEvent, ItemHandle},
|
||||
register_followable_item,
|
||||
searchable::SearchableItemHandle,
|
||||
ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
|
||||
|
@ -140,6 +141,12 @@ impl ChannelView {
|
|||
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
||||
channel_buffer.clone(),
|
||||
)));
|
||||
editor.set_read_only(
|
||||
!channel_buffer
|
||||
.read(cx)
|
||||
.channel(cx)
|
||||
.is_some_and(|c| c.can_edit_notes()),
|
||||
);
|
||||
editor
|
||||
});
|
||||
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
|
||||
|
@ -157,8 +164,8 @@ impl ChannelView {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn channel(&self, cx: &AppContext) -> Arc<Channel> {
|
||||
self.channel_buffer.read(cx).channel()
|
||||
pub fn channel(&self, cx: &AppContext) -> Option<Arc<Channel>> {
|
||||
self.channel_buffer.read(cx).channel(cx)
|
||||
}
|
||||
|
||||
fn handle_channel_buffer_event(
|
||||
|
@ -172,6 +179,13 @@ impl ChannelView {
|
|||
editor.set_read_only(true);
|
||||
cx.notify();
|
||||
}),
|
||||
ChannelBufferEvent::ChannelChanged => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
|
||||
cx.emit(editor::Event::TitleChanged);
|
||||
cx.notify()
|
||||
});
|
||||
}
|
||||
ChannelBufferEvent::BufferEdited => {
|
||||
if cx.is_self_focused() || self.editor.is_focused(cx) {
|
||||
self.acknowledge_buffer_version(cx);
|
||||
|
@ -179,7 +193,7 @@ impl ChannelView {
|
|||
self.channel_store.update(cx, |store, cx| {
|
||||
let channel_buffer = self.channel_buffer.read(cx);
|
||||
store.notes_changed(
|
||||
channel_buffer.channel().id,
|
||||
channel_buffer.channel_id,
|
||||
channel_buffer.epoch(),
|
||||
&channel_buffer.buffer().read(cx).version(),
|
||||
cx,
|
||||
|
@ -187,7 +201,7 @@ impl ChannelView {
|
|||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
ChannelBufferEvent::CollaboratorsChanged => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +209,7 @@ impl ChannelView {
|
|||
self.channel_store.update(cx, |store, cx| {
|
||||
let channel_buffer = self.channel_buffer.read(cx);
|
||||
store.acknowledge_notes_version(
|
||||
channel_buffer.channel().id,
|
||||
channel_buffer.channel_id,
|
||||
channel_buffer.epoch(),
|
||||
&channel_buffer.buffer().read(cx).version(),
|
||||
cx,
|
||||
|
@ -250,11 +264,17 @@ impl Item for ChannelView {
|
|||
style: &theme::Tab,
|
||||
cx: &gpui::AppContext,
|
||||
) -> AnyElement<V> {
|
||||
let channel_name = &self.channel_buffer.read(cx).channel().name;
|
||||
let label = if self.channel_buffer.read(cx).is_connected() {
|
||||
format!("#{}", channel_name)
|
||||
let label = if let Some(channel) = self.channel(cx) {
|
||||
match (
|
||||
channel.can_edit_notes(),
|
||||
self.channel_buffer.read(cx).is_connected(),
|
||||
) {
|
||||
(true, true) => format!("#{}", channel.name),
|
||||
(false, true) => format!("#{} (read-only)", channel.name),
|
||||
(_, false) => format!("#{} (disconnected)", channel.name),
|
||||
}
|
||||
} else {
|
||||
format!("#{} (disconnected)", channel_name)
|
||||
format!("channel notes (disconnected)")
|
||||
};
|
||||
Label::new(label, style.label.to_owned()).into_any()
|
||||
}
|
||||
|
@ -298,6 +318,10 @@ impl Item for ChannelView {
|
|||
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
|
||||
self.editor.read(cx).pixel_position_of_cursor(cx)
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
||||
editor::Editor::to_item_events(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl FollowableItem for ChannelView {
|
||||
|
@ -313,7 +337,7 @@ impl FollowableItem for ChannelView {
|
|||
|
||||
Some(proto::view::Variant::ChannelView(
|
||||
proto::view::ChannelView {
|
||||
channel_id: channel_buffer.channel().id,
|
||||
channel_id: channel_buffer.channel_id,
|
||||
editor: if let Some(proto::view::Variant::Editor(proto)) =
|
||||
self.editor.read(cx).to_state_proto(cx)
|
||||
{
|
||||
|
|
|
@ -263,21 +263,22 @@ impl ChatPanel {
|
|||
|
||||
fn set_active_chat(&mut self, chat: ModelHandle<ChannelChat>, cx: &mut ViewContext<Self>) {
|
||||
if self.active_chat.as_ref().map(|e| &e.0) != Some(&chat) {
|
||||
self.markdown_data.clear();
|
||||
let id = {
|
||||
let channel_id = chat.read(cx).channel_id;
|
||||
{
|
||||
self.markdown_data.clear();
|
||||
let chat = chat.read(cx);
|
||||
let channel = chat.channel().clone();
|
||||
self.message_list.reset(chat.message_count());
|
||||
|
||||
let channel_name = chat.channel(cx).map(|channel| channel.name.clone());
|
||||
self.input_editor.update(cx, |editor, cx| {
|
||||
editor.set_channel(channel.clone(), cx);
|
||||
editor.set_channel(channel_id, channel_name, cx);
|
||||
});
|
||||
channel.id
|
||||
};
|
||||
let subscription = cx.subscribe(&chat, Self::channel_did_change);
|
||||
self.active_chat = Some((chat, subscription));
|
||||
self.acknowledge_last_message(cx);
|
||||
self.channel_select.update(cx, |select, cx| {
|
||||
if let Some(ix) = self.channel_store.read(cx).index_of_channel(id) {
|
||||
if let Some(ix) = self.channel_store.read(cx).index_of_channel(channel_id) {
|
||||
select.set_selected_index(ix, cx);
|
||||
}
|
||||
});
|
||||
|
@ -361,7 +362,8 @@ impl ChatPanel {
|
|||
let is_admin = self
|
||||
.channel_store
|
||||
.read(cx)
|
||||
.is_user_admin(active_chat.channel().id);
|
||||
.is_channel_admin(active_chat.channel_id);
|
||||
|
||||
let last_message = active_chat.message(ix.saturating_sub(1));
|
||||
let this_message = active_chat.message(ix).clone();
|
||||
let is_continuation = last_message.id != this_message.id
|
||||
|
@ -676,7 +678,7 @@ impl ChatPanel {
|
|||
.active_chat
|
||||
.as_ref()
|
||||
.and_then(|(chat, _)| {
|
||||
(chat.read(cx).channel().id == selected_channel_id)
|
||||
(chat.read(cx).channel_id == selected_channel_id)
|
||||
.then(|| Task::ready(anyhow::Ok(chat.clone())))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
|
@ -714,7 +716,7 @@ impl ChatPanel {
|
|||
|
||||
fn open_notes(&mut self, _: &OpenChannelNotes, cx: &mut ViewContext<Self>) {
|
||||
if let Some((chat, _)) = &self.active_chat {
|
||||
let channel_id = chat.read(cx).channel().id;
|
||||
let channel_id = chat.read(cx).channel_id;
|
||||
if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
ChannelView::open(channel_id, workspace, cx).detach();
|
||||
}
|
||||
|
@ -723,7 +725,7 @@ impl ChatPanel {
|
|||
|
||||
fn join_call(&mut self, _: &JoinCall, cx: &mut ViewContext<Self>) {
|
||||
if let Some((chat, _)) = &self.active_chat {
|
||||
let channel_id = chat.read(cx).channel().id;
|
||||
let channel_id = chat.read(cx).channel_id;
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.join_channel(channel_id, cx))
|
||||
.detach_and_log_err(cx);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use channel::{Channel, ChannelMembership, ChannelStore, MessageParams};
|
||||
use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
|
||||
use client::UserId;
|
||||
use collections::HashMap;
|
||||
use editor::{AnchorRangeExt, Editor};
|
||||
|
@ -30,7 +30,7 @@ pub struct MessageEditor {
|
|||
users: HashMap<String, UserId>,
|
||||
mentions: Vec<UserId>,
|
||||
mentions_task: Option<Task<()>>,
|
||||
channel: Option<Arc<Channel>>,
|
||||
channel_id: Option<ChannelId>,
|
||||
}
|
||||
|
||||
impl MessageEditor {
|
||||
|
@ -68,24 +68,33 @@ impl MessageEditor {
|
|||
editor,
|
||||
channel_store,
|
||||
users: HashMap::default(),
|
||||
channel: None,
|
||||
channel_id: None,
|
||||
mentions: Vec::new(),
|
||||
mentions_task: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_channel(&mut self, channel: Arc<Channel>, cx: &mut ViewContext<Self>) {
|
||||
pub fn set_channel(
|
||||
&mut self,
|
||||
channel_id: u64,
|
||||
channel_name: Option<String>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text(format!("Message #{}", channel.name), cx);
|
||||
if let Some(channel_name) = channel_name {
|
||||
editor.set_placeholder_text(format!("Message #{}", channel_name), cx);
|
||||
} else {
|
||||
editor.set_placeholder_text(format!("Message Channel"), cx);
|
||||
}
|
||||
});
|
||||
self.channel = Some(channel);
|
||||
self.channel_id = Some(channel_id);
|
||||
self.refresh_users(cx);
|
||||
}
|
||||
|
||||
pub fn refresh_users(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(channel) = &self.channel {
|
||||
if let Some(channel_id) = self.channel_id {
|
||||
let members = self.channel_store.update(cx, |store, cx| {
|
||||
store.get_channel_member_details(channel.id, cx)
|
||||
store.get_channel_member_details(channel_id, cx)
|
||||
});
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let members = members.await?;
|
||||
|
|
|
@ -120,22 +120,11 @@ struct StartLinkChannelFor {
|
|||
parent_id: Option<ChannelId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct LinkChannel {
|
||||
to: ChannelId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct MoveChannel {
|
||||
to: ChannelId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct UnlinkChannel {
|
||||
channel_id: ChannelId,
|
||||
parent_id: ChannelId,
|
||||
}
|
||||
|
||||
type DraggedChannel = (Channel, Option<ChannelId>);
|
||||
|
||||
actions!(
|
||||
|
@ -147,8 +136,7 @@ actions!(
|
|||
CollapseSelectedChannel,
|
||||
ExpandSelectedChannel,
|
||||
StartMoveChannel,
|
||||
StartLinkChannel,
|
||||
MoveOrLinkToSelected,
|
||||
MoveSelected,
|
||||
InsertSpace,
|
||||
]
|
||||
);
|
||||
|
@ -166,11 +154,8 @@ impl_actions!(
|
|||
JoinChannelCall,
|
||||
JoinChannelChat,
|
||||
CopyChannelLink,
|
||||
LinkChannel,
|
||||
StartMoveChannelFor,
|
||||
StartLinkChannelFor,
|
||||
MoveChannel,
|
||||
UnlinkChannel,
|
||||
ToggleSelectedIx
|
||||
]
|
||||
);
|
||||
|
@ -185,7 +170,7 @@ struct ChannelMoveClipboard {
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum ClipboardIntent {
|
||||
Move,
|
||||
Link,
|
||||
// Link,
|
||||
}
|
||||
|
||||
const COLLABORATION_PANEL_KEY: &'static str = "CollaborationPanel";
|
||||
|
@ -238,18 +223,6 @@ pub fn init(cx: &mut AppContext) {
|
|||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel,
|
||||
action: &StartLinkChannelFor,
|
||||
_: &mut ViewContext<CollabPanel>| {
|
||||
panel.channel_clipboard = Some(ChannelMoveClipboard {
|
||||
channel_id: action.channel_id,
|
||||
parent_id: action.parent_id,
|
||||
intent: ClipboardIntent::Link,
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, _: &StartMoveChannel, _: &mut ViewContext<CollabPanel>| {
|
||||
if let Some((_, path)) = panel.selected_channel() {
|
||||
|
@ -263,86 +236,51 @@ pub fn init(cx: &mut AppContext) {
|
|||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, _: &StartLinkChannel, _: &mut ViewContext<CollabPanel>| {
|
||||
if let Some((_, path)) = panel.selected_channel() {
|
||||
panel.channel_clipboard = Some(ChannelMoveClipboard {
|
||||
channel_id: path.channel_id(),
|
||||
parent_id: path.parent_id(),
|
||||
intent: ClipboardIntent::Link,
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, _: &MoveOrLinkToSelected, cx: &mut ViewContext<CollabPanel>| {
|
||||
|panel: &mut CollabPanel, _: &MoveSelected, cx: &mut ViewContext<CollabPanel>| {
|
||||
let clipboard = panel.channel_clipboard.take();
|
||||
if let Some(((selected_channel, _), clipboard)) =
|
||||
panel.selected_channel().zip(clipboard)
|
||||
{
|
||||
match clipboard.intent {
|
||||
ClipboardIntent::Move if clipboard.parent_id.is_some() => {
|
||||
let parent_id = clipboard.parent_id.unwrap();
|
||||
panel.channel_store.update(cx, |channel_store, cx| {
|
||||
channel_store
|
||||
.move_channel(
|
||||
clipboard.channel_id,
|
||||
parent_id,
|
||||
selected_channel.id,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
}
|
||||
_ => panel.channel_store.update(cx, |channel_store, cx| {
|
||||
channel_store
|
||||
.link_channel(clipboard.channel_id, selected_channel.id, cx)
|
||||
.detach_and_log_err(cx)
|
||||
ClipboardIntent::Move => panel.channel_store.update(cx, |channel_store, cx| {
|
||||
match clipboard.parent_id {
|
||||
Some(parent_id) => channel_store.move_channel(
|
||||
clipboard.channel_id,
|
||||
parent_id,
|
||||
selected_channel.id,
|
||||
cx,
|
||||
),
|
||||
None => channel_store.link_channel(
|
||||
clipboard.channel_id,
|
||||
selected_channel.id,
|
||||
cx,
|
||||
),
|
||||
}
|
||||
.detach_and_log_err(cx)
|
||||
}),
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, action: &LinkChannel, cx: &mut ViewContext<CollabPanel>| {
|
||||
if let Some(clipboard) = panel.channel_clipboard.take() {
|
||||
panel.channel_store.update(cx, |channel_store, cx| {
|
||||
channel_store
|
||||
.link_channel(clipboard.channel_id, action.to, cx)
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, action: &MoveChannel, cx: &mut ViewContext<CollabPanel>| {
|
||||
if let Some(clipboard) = panel.channel_clipboard.take() {
|
||||
panel.channel_store.update(cx, |channel_store, cx| {
|
||||
if let Some(parent) = clipboard.parent_id {
|
||||
channel_store
|
||||
.move_channel(clipboard.channel_id, parent, action.to, cx)
|
||||
.detach_and_log_err(cx)
|
||||
} else {
|
||||
channel_store
|
||||
.link_channel(clipboard.channel_id, action.to, cx)
|
||||
.detach_and_log_err(cx)
|
||||
match clipboard.parent_id {
|
||||
Some(parent_id) => channel_store.move_channel(
|
||||
clipboard.channel_id,
|
||||
parent_id,
|
||||
action.to,
|
||||
cx,
|
||||
),
|
||||
None => channel_store.link_channel(clipboard.channel_id, action.to, cx),
|
||||
}
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cx.add_action(
|
||||
|panel: &mut CollabPanel, action: &UnlinkChannel, cx: &mut ViewContext<CollabPanel>| {
|
||||
panel.channel_store.update(cx, |channel_store, cx| {
|
||||
channel_store
|
||||
.unlink_channel(action.channel_id, action.parent_id, cx)
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -2235,33 +2173,23 @@ impl CollabPanel {
|
|||
this.deploy_channel_context_menu(Some(e.position), &path, ix, cx);
|
||||
}
|
||||
})
|
||||
.on_up(MouseButton::Left, move |e, this, cx| {
|
||||
.on_up(MouseButton::Left, move |_, this, cx| {
|
||||
if let Some((_, dragged_channel)) = cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<DraggedChannel>(cx.window())
|
||||
{
|
||||
if e.modifiers.alt {
|
||||
this.channel_store.update(cx, |channel_store, cx| {
|
||||
channel_store
|
||||
.link_channel(dragged_channel.0.id, channel_id, cx)
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
} else {
|
||||
this.channel_store.update(cx, |channel_store, cx| {
|
||||
match dragged_channel.1 {
|
||||
Some(parent_id) => channel_store.move_channel(
|
||||
dragged_channel.0.id,
|
||||
parent_id,
|
||||
channel_id,
|
||||
cx,
|
||||
),
|
||||
None => {
|
||||
channel_store.link_channel(dragged_channel.0.id, channel_id, cx)
|
||||
}
|
||||
}
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
}
|
||||
this.channel_store.update(cx, |channel_store, cx| {
|
||||
match dragged_channel.1 {
|
||||
Some(parent_id) => channel_store.move_channel(
|
||||
dragged_channel.0.id,
|
||||
parent_id,
|
||||
channel_id,
|
||||
cx,
|
||||
),
|
||||
None => channel_store.link_channel(dragged_channel.0.id, channel_id, cx),
|
||||
}
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
}
|
||||
})
|
||||
.on_move({
|
||||
|
@ -2288,18 +2216,10 @@ impl CollabPanel {
|
|||
})
|
||||
.as_draggable(
|
||||
(channel.clone(), path.parent_id()),
|
||||
move |modifiers, (channel, _), cx: &mut ViewContext<Workspace>| {
|
||||
move |_, (channel, _), cx: &mut ViewContext<Workspace>| {
|
||||
let theme = &theme::current(cx).collab_panel;
|
||||
|
||||
Flex::<Workspace>::row()
|
||||
.with_children(modifiers.alt.then(|| {
|
||||
Svg::new("icons/plus.svg")
|
||||
.with_color(theme.channel_hash.color)
|
||||
.constrained()
|
||||
.with_width(theme.channel_hash.width)
|
||||
.aligned()
|
||||
.left()
|
||||
}))
|
||||
.with_child(
|
||||
Svg::new("icons/hash.svg")
|
||||
.with_color(theme.channel_hash.color)
|
||||
|
@ -2721,7 +2641,11 @@ impl CollabPanel {
|
|||
},
|
||||
));
|
||||
|
||||
if self.channel_store.read(cx).is_user_admin(path.channel_id()) {
|
||||
if self
|
||||
.channel_store
|
||||
.read(cx)
|
||||
.is_channel_admin(path.channel_id())
|
||||
{
|
||||
let parent_id = path.parent_id();
|
||||
|
||||
items.extend([
|
||||
|
@ -2738,20 +2662,6 @@ impl CollabPanel {
|
|||
location: path.clone(),
|
||||
},
|
||||
),
|
||||
ContextMenuItem::Separator,
|
||||
]);
|
||||
|
||||
if let Some(parent_id) = parent_id {
|
||||
items.push(ContextMenuItem::action(
|
||||
"Unlink from parent",
|
||||
UnlinkChannel {
|
||||
channel_id: path.channel_id(),
|
||||
parent_id,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
items.extend([
|
||||
ContextMenuItem::action(
|
||||
"Move this channel",
|
||||
StartMoveChannelFor {
|
||||
|
@ -2759,13 +2669,6 @@ impl CollabPanel {
|
|||
parent_id,
|
||||
},
|
||||
),
|
||||
ContextMenuItem::action(
|
||||
"Link this channel",
|
||||
StartLinkChannelFor {
|
||||
channel_id: path.channel_id(),
|
||||
parent_id,
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
if let Some(channel_name) = channel_name {
|
||||
|
@ -2776,12 +2679,6 @@ impl CollabPanel {
|
|||
to: path.channel_id(),
|
||||
},
|
||||
));
|
||||
items.push(ContextMenuItem::action(
|
||||
format!("Link '#{}' here", channel_name),
|
||||
LinkChannel {
|
||||
to: path.channel_id(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
items.extend([
|
||||
|
@ -3160,7 +3057,7 @@ impl CollabPanel {
|
|||
|
||||
fn rename_channel(&mut self, action: &RenameChannel, cx: &mut ViewContext<Self>) {
|
||||
let channel_store = self.channel_store.read(cx);
|
||||
if !channel_store.is_user_admin(action.location.channel_id()) {
|
||||
if !channel_store.is_channel_admin(action.location.channel_id()) {
|
||||
return;
|
||||
}
|
||||
if let Some(channel) = channel_store
|
||||
|
|
|
@ -88,8 +88,10 @@ impl View for CollabTitlebarItem {
|
|||
.zip(peer_id)
|
||||
.zip(ActiveCall::global(cx).read(cx).room().cloned())
|
||||
{
|
||||
right_container
|
||||
.add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
|
||||
if room.read(cx).can_publish() {
|
||||
right_container
|
||||
.add_children(self.render_in_call_share_unshare_button(&workspace, &theme, cx));
|
||||
}
|
||||
right_container.add_child(self.render_leave_call(&theme, cx));
|
||||
let muted = room.read(cx).is_muted(cx);
|
||||
let speaking = room.read(cx).is_speaking();
|
||||
|
@ -97,9 +99,14 @@ impl View for CollabTitlebarItem {
|
|||
self.render_current_user(&workspace, &theme, &user, peer_id, muted, speaking, cx),
|
||||
);
|
||||
left_container.add_children(self.render_collaborators(&workspace, &theme, &room, cx));
|
||||
right_container.add_child(self.render_toggle_mute(&theme, &room, cx));
|
||||
if room.read(cx).can_publish() {
|
||||
right_container.add_child(self.render_toggle_mute(&theme, &room, cx));
|
||||
}
|
||||
right_container.add_child(self.render_toggle_deafen(&theme, &room, cx));
|
||||
right_container.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx));
|
||||
if room.read(cx).can_publish() {
|
||||
right_container
|
||||
.add_child(self.render_toggle_screen_sharing_button(&theme, &room, cx));
|
||||
}
|
||||
}
|
||||
|
||||
let status = workspace.read(cx).client().status();
|
||||
|
|
|
@ -477,7 +477,7 @@ impl NotificationPanel {
|
|||
return panel.read_with(cx, |panel, cx| {
|
||||
panel.is_scrolled_to_bottom()
|
||||
&& panel.active_chat().map_or(false, |chat| {
|
||||
chat.read(cx).channel().id == *channel_id
|
||||
chat.read(cx).channel_id == *channel_id
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -306,6 +306,16 @@ impl live_kit_server::api::Client for TestApiClient {
|
|||
token::VideoGrant::to_join(room),
|
||||
)
|
||||
}
|
||||
|
||||
fn guest_token(&self, room: &str, identity: &str) -> Result<String> {
|
||||
let server = TestServer::get(&self.url)?;
|
||||
token::create(
|
||||
&server.api_key,
|
||||
&server.secret_key,
|
||||
Some(identity),
|
||||
token::VideoGrant::for_guest(room),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Sid = String;
|
||||
|
|
|
@ -12,6 +12,7 @@ pub trait Client: Send + Sync {
|
|||
async fn delete_room(&self, name: String) -> Result<()>;
|
||||
async fn remove_participant(&self, room: String, identity: String) -> Result<()>;
|
||||
fn room_token(&self, room: &str, identity: &str) -> Result<String>;
|
||||
fn guest_token(&self, room: &str, identity: &str) -> Result<String>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -138,4 +139,13 @@ impl Client for LiveKitClient {
|
|||
token::VideoGrant::to_join(room),
|
||||
)
|
||||
}
|
||||
|
||||
fn guest_token(&self, room: &str, identity: &str) -> Result<String> {
|
||||
token::create(
|
||||
&self.key,
|
||||
&self.secret,
|
||||
Some(identity),
|
||||
token::VideoGrant::for_guest(room),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,15 @@ impl<'a> VideoGrant<'a> {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_guest(room: &'a str) -> Self {
|
||||
Self {
|
||||
room: Some(Cow::Borrowed(room)),
|
||||
room_join: Some(true),
|
||||
can_subscribe: Some(true),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
|
|
|
@ -342,6 +342,7 @@ message RoomUpdated {
|
|||
message LiveKitConnectionInfo {
|
||||
string server_url = 1;
|
||||
string token = 2;
|
||||
bool can_publish = 3;
|
||||
}
|
||||
|
||||
message ShareProject {
|
||||
|
@ -977,7 +978,6 @@ message UpdateChannels {
|
|||
repeated Channel channel_invitations = 5;
|
||||
repeated uint64 remove_channel_invitations = 6;
|
||||
repeated ChannelParticipants channel_participants = 7;
|
||||
repeated ChannelPermission channel_permissions = 8;
|
||||
repeated UnseenChannelMessage unseen_channel_messages = 9;
|
||||
repeated UnseenChannelBufferChange unseen_channel_buffer_changes = 10;
|
||||
}
|
||||
|
@ -1585,6 +1585,7 @@ message Channel {
|
|||
uint64 id = 1;
|
||||
string name = 2;
|
||||
ChannelVisibility visibility = 3;
|
||||
ChannelRole role = 4;
|
||||
}
|
||||
|
||||
message Contact {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue