Get more of chat panel compiling, but lots of todos

This commit is contained in:
Nathan Sobo 2023-12-07 21:05:07 -07:00 committed by Max Brunsfeld
parent 6955579f19
commit 213ed2028c
12 changed files with 1058 additions and 992 deletions

View file

@ -8,7 +8,8 @@ use collections::{hash_map, HashMap, HashSet};
use db::RELEASE_CHANNEL; use db::RELEASE_CHANNEL;
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt}; use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{ use gpui::{
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel, AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
WeakModel,
}; };
use rpc::{ use rpc::{
proto::{self, ChannelVisibility}, proto::{self, ChannelVisibility},
@ -46,7 +47,7 @@ pub struct ChannelStore {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Channel { pub struct Channel {
pub id: ChannelId, pub id: ChannelId,
pub name: String, pub name: SharedString,
pub visibility: proto::ChannelVisibility, pub visibility: proto::ChannelVisibility,
pub role: proto::ChannelRole, pub role: proto::ChannelRole,
pub unseen_note_version: Option<(u64, clock::Global)>, pub unseen_note_version: Option<(u64, clock::Global)>,
@ -895,14 +896,16 @@ impl ChannelStore {
.channel_invitations .channel_invitations
.binary_search_by_key(&channel.id, |c| c.id) .binary_search_by_key(&channel.id, |c| c.id)
{ {
Ok(ix) => Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name, Ok(ix) => {
Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name.into()
}
Err(ix) => self.channel_invitations.insert( Err(ix) => self.channel_invitations.insert(
ix, ix,
Arc::new(Channel { Arc::new(Channel {
id: channel.id, id: channel.id,
visibility: channel.visibility(), visibility: channel.visibility(),
role: channel.role(), role: channel.role(),
name: channel.name, name: channel.name.into(),
unseen_note_version: None, unseen_note_version: None,
unseen_message_id: None, unseen_message_id: None,
parent_path: channel.parent_path, parent_path: channel.parent_path,

View file

@ -104,7 +104,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
existing_channel.visibility = channel_proto.visibility(); existing_channel.visibility = channel_proto.visibility();
existing_channel.role = channel_proto.role(); existing_channel.role = channel_proto.role();
existing_channel.name = channel_proto.name; existing_channel.name = channel_proto.name.into();
} else { } else {
self.channels_by_id.insert( self.channels_by_id.insert(
channel_proto.id, channel_proto.id,
@ -112,7 +112,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
id: channel_proto.id, id: channel_proto.id,
visibility: channel_proto.visibility(), visibility: channel_proto.visibility(),
role: channel_proto.role(), role: channel_proto.role(),
name: channel_proto.name, name: channel_proto.name.into(),
unseen_note_version: None, unseen_note_version: None,
unseen_message_id: None, unseen_message_id: None,
parent_path: channel_proto.parent_path, parent_path: channel_proto.parent_path,
@ -146,11 +146,11 @@ fn channel_path_sorting_key<'a>(
let (parent_path, name) = channels_by_id let (parent_path, name) = channels_by_id
.get(&id) .get(&id)
.map_or((&[] as &[_], None), |channel| { .map_or((&[] as &[_], None), |channel| {
(channel.parent_path.as_slice(), Some(channel.name.as_str())) (channel.parent_path.as_slice(), Some(channel.name.as_ref()))
}); });
parent_path parent_path
.iter() .iter()
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_str())) .filter_map(|id| Some(channels_by_id.get(id)?.name.as_ref()))
.chain(name) .chain(name)
} }

View file

@ -7,7 +7,7 @@ use call::ActiveCall;
use channel::{ChannelId, ChannelMembership, ChannelStore}; use channel::{ChannelId, ChannelMembership, ChannelStore};
use client::User; use client::User;
use futures::future::try_join_all; use futures::future::try_join_all;
use gpui::{BackgroundExecutor, Model, TestAppContext}; use gpui::{BackgroundExecutor, Model, SharedString, TestAppContext};
use rpc::{ use rpc::{
proto::{self, ChannelRole}, proto::{self, ChannelRole},
RECEIVE_TIMEOUT, RECEIVE_TIMEOUT,
@ -46,13 +46,13 @@ async fn test_core_channels(
&[ &[
ExpectedChannel { ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_b_id, id: channel_b_id,
name: "channel-b".to_string(), name: "channel-b".into(),
depth: 1, depth: 1,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
@ -92,7 +92,7 @@ async fn test_core_channels(
cx_b, cx_b,
&[ExpectedChannel { &[ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
@ -140,13 +140,13 @@ async fn test_core_channels(
&[ &[
ExpectedChannel { ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
depth: 0, depth: 0,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_b_id, id: channel_b_id,
name: "channel-b".to_string(), name: "channel-b".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
depth: 1, depth: 1,
}, },
@ -168,19 +168,19 @@ async fn test_core_channels(
&[ &[
ExpectedChannel { ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
depth: 0, depth: 0,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_b_id, id: channel_b_id,
name: "channel-b".to_string(), name: "channel-b".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
depth: 1, depth: 1,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_c_id, id: channel_c_id,
name: "channel-c".to_string(), name: "channel-c".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
depth: 2, depth: 2,
}, },
@ -211,19 +211,19 @@ async fn test_core_channels(
&[ &[
ExpectedChannel { ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_b_id, id: channel_b_id,
name: "channel-b".to_string(), name: "channel-b".into(),
depth: 1, depth: 1,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
ExpectedChannel { ExpectedChannel {
id: channel_c_id, id: channel_c_id,
name: "channel-c".to_string(), name: "channel-c".into(),
depth: 2, depth: 2,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
@ -245,7 +245,7 @@ async fn test_core_channels(
cx_a, cx_a,
&[ExpectedChannel { &[ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}], }],
@ -255,7 +255,7 @@ async fn test_core_channels(
cx_b, cx_b,
&[ExpectedChannel { &[ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}], }],
@ -278,7 +278,7 @@ async fn test_core_channels(
cx_a, cx_a,
&[ExpectedChannel { &[ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a".to_string(), name: "channel-a".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}], }],
@ -309,7 +309,7 @@ async fn test_core_channels(
cx_a, cx_a,
&[ExpectedChannel { &[ExpectedChannel {
id: channel_a_id, id: channel_a_id,
name: "channel-a-renamed".to_string(), name: "channel-a-renamed".into(),
depth: 0, depth: 0,
role: ChannelRole::Admin, role: ChannelRole::Admin,
}], }],
@ -418,7 +418,7 @@ async fn test_channel_room(
cx_b, cx_b,
&[ExpectedChannel { &[ExpectedChannel {
id: zed_id, id: zed_id,
name: "zed".to_string(), name: "zed".into(),
depth: 0, depth: 0,
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
@ -680,7 +680,7 @@ async fn test_permissions_update_while_invited(
&[ExpectedChannel { &[ExpectedChannel {
depth: 0, depth: 0,
id: rust_id, id: rust_id,
name: "rust".to_string(), name: "rust".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
); );
@ -708,7 +708,7 @@ async fn test_permissions_update_while_invited(
&[ExpectedChannel { &[ExpectedChannel {
depth: 0, depth: 0,
id: rust_id, id: rust_id,
name: "rust".to_string(), name: "rust".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
); );
@ -747,7 +747,7 @@ async fn test_channel_rename(
&[ExpectedChannel { &[ExpectedChannel {
depth: 0, depth: 0,
id: rust_id, id: rust_id,
name: "rust-archive".to_string(), name: "rust-archive".into(),
role: ChannelRole::Admin, role: ChannelRole::Admin,
}], }],
); );
@ -759,7 +759,7 @@ async fn test_channel_rename(
&[ExpectedChannel { &[ExpectedChannel {
depth: 0, depth: 0,
id: rust_id, id: rust_id,
name: "rust-archive".to_string(), name: "rust-archive".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
); );
@ -888,7 +888,7 @@ async fn test_lost_channel_creation(
&[ExpectedChannel { &[ExpectedChannel {
depth: 0, depth: 0,
id: channel_id, id: channel_id,
name: "x".to_string(), name: "x".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}], }],
); );
@ -912,13 +912,13 @@ async fn test_lost_channel_creation(
ExpectedChannel { ExpectedChannel {
depth: 0, depth: 0,
id: channel_id, id: channel_id,
name: "x".to_string(), name: "x".into(),
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
ExpectedChannel { ExpectedChannel {
depth: 1, depth: 1,
id: subchannel_id, id: subchannel_id,
name: "subchannel".to_string(), name: "subchannel".into(),
role: ChannelRole::Admin, role: ChannelRole::Admin,
}, },
], ],
@ -943,13 +943,13 @@ async fn test_lost_channel_creation(
ExpectedChannel { ExpectedChannel {
depth: 0, depth: 0,
id: channel_id, id: channel_id,
name: "x".to_string(), name: "x".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}, },
ExpectedChannel { ExpectedChannel {
depth: 1, depth: 1,
id: subchannel_id, id: subchannel_id,
name: "subchannel".to_string(), name: "subchannel".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}, },
], ],
@ -1221,13 +1221,13 @@ async fn test_channel_membership_notifications(
ExpectedChannel { ExpectedChannel {
depth: 0, depth: 0,
id: zed_channel, id: zed_channel,
name: "zed".to_string(), name: "zed".into(),
role: ChannelRole::Guest, role: ChannelRole::Guest,
}, },
ExpectedChannel { ExpectedChannel {
depth: 1, depth: 1,
id: vim_channel, id: vim_channel,
name: "vim".to_string(), name: "vim".into(),
role: ChannelRole::Member, role: ChannelRole::Member,
}, },
], ],
@ -1250,13 +1250,13 @@ async fn test_channel_membership_notifications(
ExpectedChannel { ExpectedChannel {
depth: 0, depth: 0,
id: zed_channel, id: zed_channel,
name: "zed".to_string(), name: "zed".into(),
role: ChannelRole::Guest, role: ChannelRole::Guest,
}, },
ExpectedChannel { ExpectedChannel {
depth: 1, depth: 1,
id: vim_channel, id: vim_channel,
name: "vim".to_string(), name: "vim".into(),
role: ChannelRole::Guest, role: ChannelRole::Guest,
}, },
], ],
@ -1476,7 +1476,7 @@ async fn test_channel_moving(
struct ExpectedChannel { struct ExpectedChannel {
depth: usize, depth: usize,
id: ChannelId, id: ChannelId,
name: String, name: SharedString,
role: ChannelRole, role: ChannelRole,
} }
@ -1515,7 +1515,7 @@ fn assert_channels(
.ordered_channels() .ordered_channels()
.map(|(depth, channel)| ExpectedChannel { .map(|(depth, channel)| ExpectedChannel {
depth, depth,
name: channel.name.clone(), name: channel.name.clone().into(),
id: channel.id, id: channel.id,
role: channel.role, role: channel.role,
}) })

View file

@ -3,7 +3,7 @@ use crate::db::ChannelRole;
use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan}; use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
use anyhow::Result; use anyhow::Result;
use async_trait::async_trait; use async_trait::async_trait;
use gpui::{BackgroundExecutor, TestAppContext}; use gpui::{BackgroundExecutor, SharedString, TestAppContext};
use rand::prelude::*; use rand::prelude::*;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::{ use std::{
@ -30,13 +30,13 @@ struct RandomChannelBufferTest;
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
enum ChannelBufferOperation { enum ChannelBufferOperation {
JoinChannelNotes { JoinChannelNotes {
channel_name: String, channel_name: SharedString,
}, },
LeaveChannelNotes { LeaveChannelNotes {
channel_name: String, channel_name: SharedString,
}, },
EditChannelNotes { EditChannelNotes {
channel_name: String, channel_name: SharedString,
edits: Vec<(Range<usize>, Arc<str>)>, edits: Vec<(Range<usize>, Arc<str>)>,
}, },
Noop, Noop,

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,9 @@ use channel::{ChannelId, ChannelMembership, ChannelStore, MessageParams};
use client::UserId; use client::UserId;
use collections::HashMap; use collections::HashMap;
use editor::{AnchorRangeExt, Editor}; use editor::{AnchorRangeExt, Editor};
use gpui::{AnyView, AsyncAppContext, Model, Render, Task, View, ViewContext, WeakView}; use gpui::{
AnyView, AsyncWindowContext, Model, Render, SharedString, Task, View, ViewContext, WeakView,
};
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry}; use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use project::search::SearchQuery; use project::search::SearchQuery;
@ -46,15 +48,14 @@ impl MessageEditor {
cx.subscribe(&buffer, Self::on_buffer_event).detach(); cx.subscribe(&buffer, Self::on_buffer_event).detach();
let markdown = language_registry.language_for_name("Markdown"); let markdown = language_registry.language_for_name("Markdown");
cx.app_context() cx.spawn(|_, mut cx| async move {
.spawn(|mut cx| async move { let markdown = markdown.await?;
let markdown = markdown.await?; buffer.update(&mut cx, |buffer, cx| {
buffer.update(&mut cx, |buffer, cx| { buffer.set_language(Some(markdown), cx)
buffer.set_language(Some(markdown), cx) });
}); anyhow::Ok(())
anyhow::Ok(()) })
}) .detach_and_log_err(cx);
.detach_and_log_err(cx);
Self { Self {
editor, editor,
@ -69,7 +70,7 @@ impl MessageEditor {
pub fn set_channel( pub fn set_channel(
&mut self, &mut self,
channel_id: u64, channel_id: u64,
channel_name: Option<String>, channel_name: Option<SharedString>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
@ -137,7 +138,9 @@ impl MessageEditor {
if let language::Event::Reparsed | language::Event::Edited = event { if let language::Event::Reparsed | language::Event::Edited = event {
let buffer = buffer.read(cx).snapshot(); let buffer = buffer.read(cx).snapshot();
self.mentions_task = Some(cx.spawn(|this, cx| async move { self.mentions_task = Some(cx.spawn(|this, cx| async move {
cx.background().timer(MENTIONS_DEBOUNCE_INTERVAL).await; cx.background_executor()
.timer(MENTIONS_DEBOUNCE_INTERVAL)
.await;
Self::find_mentions(this, buffer, cx).await; Self::find_mentions(this, buffer, cx).await;
})); }));
} }
@ -146,10 +149,10 @@ impl MessageEditor {
async fn find_mentions( async fn find_mentions(
this: WeakView<MessageEditor>, this: WeakView<MessageEditor>,
buffer: BufferSnapshot, buffer: BufferSnapshot,
mut cx: AsyncAppContext, mut cx: AsyncWindowContext,
) { ) {
let (buffer, ranges) = cx let (buffer, ranges) = cx
.background() .background_executor()
.spawn(async move { .spawn(async move {
let ranges = MENTIONS_SEARCH.search(&buffer, None).await; let ranges = MENTIONS_SEARCH.search(&buffer, None).await;
(buffer, ranges) (buffer, ranges)
@ -186,6 +189,10 @@ impl MessageEditor {
}) })
.ok(); .ok();
} }
pub(crate) fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
todo!()
}
} }
impl Render for MessageEditor { impl Render for MessageEditor {
@ -196,98 +203,98 @@ impl Render for MessageEditor {
} }
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use client::{Client, User, UserStore}; // use client::{Client, User, UserStore};
use gpui::{TestAppContext, WindowHandle}; // use gpui::{TestAppContext, WindowHandle};
use language::{Language, LanguageConfig}; // use language::{Language, LanguageConfig};
use rpc::proto; // use rpc::proto;
use settings::SettingsStore; // use settings::SettingsStore;
use util::{http::FakeHttpClient, test::marked_text_ranges}; // use util::{http::FakeHttpClient, test::marked_text_ranges};
#[gpui::test] // #[gpui::test]
async fn test_message_editor(cx: &mut TestAppContext) { // async fn test_message_editor(cx: &mut TestAppContext) {
let editor = init_test(cx); // let editor = init_test(cx);
let editor = editor.root(cx); // let editor = editor.root(cx);
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
editor.set_members( // editor.set_members(
vec![ // vec![
ChannelMembership { // ChannelMembership {
user: Arc::new(User { // user: Arc::new(User {
github_login: "a-b".into(), // github_login: "a-b".into(),
id: 101, // id: 101,
avatar: None, // avatar: None,
}), // }),
kind: proto::channel_member::Kind::Member, // kind: proto::channel_member::Kind::Member,
role: proto::ChannelRole::Member, // role: proto::ChannelRole::Member,
}, // },
ChannelMembership { // ChannelMembership {
user: Arc::new(User { // user: Arc::new(User {
github_login: "C_D".into(), // github_login: "C_D".into(),
id: 102, // id: 102,
avatar: None, // avatar: None,
}), // }),
kind: proto::channel_member::Kind::Member, // kind: proto::channel_member::Kind::Member,
role: proto::ChannelRole::Member, // role: proto::ChannelRole::Member,
}, // },
], // ],
cx, // cx,
); // );
editor.editor.update(cx, |editor, cx| { // editor.editor.update(cx, |editor, cx| {
editor.set_text("Hello, @a-b! Have you met @C_D?", cx) // editor.set_text("Hello, @a-b! Have you met @C_D?", cx)
}); // });
}); // });
cx.foreground().advance_clock(MENTIONS_DEBOUNCE_INTERVAL); // cx.foreground().advance_clock(MENTIONS_DEBOUNCE_INTERVAL);
editor.update(cx, |editor, cx| { // editor.update(cx, |editor, cx| {
let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false); // let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
assert_eq!( // assert_eq!(
editor.take_message(cx), // editor.take_message(cx),
MessageParams { // MessageParams {
text, // text,
mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)], // mentions: vec![(ranges[0].clone(), 101), (ranges[1].clone(), 102)],
} // }
); // );
}); // });
} // }
fn init_test(cx: &mut TestAppContext) -> WindowHandle<MessageEditor> { // fn init_test(cx: &mut TestAppContext) -> WindowHandle<MessageEditor> {
cx.foreground().forbid_parking(); // cx.foreground().forbid_parking();
cx.update(|cx| { // cx.update(|cx| {
let http = FakeHttpClient::with_404_response(); // let http = FakeHttpClient::with_404_response();
let client = Client::new(http.clone(), cx); // let client = Client::new(http.clone(), cx);
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); // let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
cx.set_global(SettingsStore::test(cx)); // cx.set_global(SettingsStore::test(cx));
theme::init((), cx); // theme::init((), cx);
language::init(cx); // language::init(cx);
editor::init(cx); // editor::init(cx);
client::init(&client, cx); // client::init(&client, cx);
channel::init(&client, user_store, cx); // channel::init(&client, user_store, cx);
}); // });
let language_registry = Arc::new(LanguageRegistry::test()); // let language_registry = Arc::new(LanguageRegistry::test());
language_registry.add(Arc::new(Language::new( // language_registry.add(Arc::new(Language::new(
LanguageConfig { // LanguageConfig {
name: "Markdown".into(), // name: "Markdown".into(),
..Default::default() // ..Default::default()
}, // },
Some(tree_sitter_markdown::language()), // Some(tree_sitter_markdown::language()),
))); // )));
let editor = cx.add_window(|cx| { // let editor = cx.add_window(|cx| {
MessageEditor::new( // MessageEditor::new(
language_registry, // language_registry,
ChannelStore::global(cx), // ChannelStore::global(cx),
cx.add_view(|cx| Editor::auto_height(4, cx)), // cx.add_view(|cx| Editor::auto_height(4, cx)),
cx, // cx,
) // )
}); // });
cx.foreground().run_until_parked(); // cx.foreground().run_until_parked();
editor // editor
} // }
} // }

View file

@ -852,7 +852,7 @@ impl CollabPanel {
.extend(channel_store.ordered_channels().enumerate().map( .extend(channel_store.ordered_channels().enumerate().map(
|(ix, (_, channel))| StringMatchCandidate { |(ix, (_, channel))| StringMatchCandidate {
id: ix, id: ix,
string: channel.name.clone(), string: channel.name.clone().into(),
char_bag: channel.name.chars().collect(), char_bag: channel.name.chars().collect(),
}, },
)); ));
@ -2262,7 +2262,7 @@ impl CollabPanel {
} }
}; };
Some(channel.name.as_str()) Some(channel.name.as_ref())
}); });
if let Some(name) = channel_name { if let Some(name) = channel_name {

View file

@ -12,6 +12,7 @@ use std::{rc::Rc, sync::Arc};
use call::{report_call_event_for_room, ActiveCall, Room}; use call::{report_call_event_for_room, ActiveCall, Room};
pub use collab_panel::CollabPanel; pub use collab_panel::CollabPanel;
pub use collab_titlebar_item::CollabTitlebarItem; pub use collab_titlebar_item::CollabTitlebarItem;
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
use gpui::{ use gpui::{
actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowBounds, actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowBounds,
WindowKind, WindowOptions, WindowKind, WindowOptions,
@ -157,6 +158,6 @@ fn notification_window_options(
// .into_any() // .into_any()
// } // }
// fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool { fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
// cx.is_staff() || cx.has_flag::<ChannelsAlpha>() cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
// } }

View file

@ -13,6 +13,7 @@ use smallvec::SmallVec;
use smol::future::FutureExt; use smol::future::FutureExt;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub use test_context::*; pub use test_context::*;
use time::UtcOffset;
use crate::{ use crate::{
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
@ -536,6 +537,10 @@ impl AppContext {
self.platform.restart() self.platform.restart()
} }
pub fn local_timezone(&self) -> UtcOffset {
self.platform.local_timezone()
}
pub(crate) fn push_effect(&mut self, effect: Effect) { pub(crate) fn push_effect(&mut self, effect: Effect) {
match &effect { match &effect {
Effect::Notify { emitter } => { Effect::Notify { emitter } => {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
px, AnyElement, AvailableSpace, BorrowAppContext, DispatchPhase, Element, IntoElement, Pixels, px, AnyElement, AvailableSpace, BorrowAppContext, DispatchPhase, Element, IntoElement, Pixels,
Point, ScrollWheelEvent, Size, Style, StyleRefinement, ViewContext, WindowContext, Point, ScrollWheelEvent, Size, Style, StyleRefinement, WindowContext,
}; };
use collections::VecDeque; use collections::VecDeque;
use std::{cell::RefCell, ops::Range, rc::Rc}; use std::{cell::RefCell, ops::Range, rc::Rc};
@ -26,14 +26,14 @@ struct StateInner {
render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>, render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
items: SumTree<ListItem>, items: SumTree<ListItem>,
logical_scroll_top: Option<ListOffset>, logical_scroll_top: Option<ListOffset>,
orientation: Orientation, alignment: ListAlignment,
overdraw: Pixels, overdraw: Pixels,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>, scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation { pub enum ListAlignment {
Top, Top,
Bottom, Bottom,
} }
@ -70,28 +70,23 @@ struct UnrenderedCount(usize);
struct Height(Pixels); struct Height(Pixels);
impl ListState { impl ListState {
pub fn new<F, V>( pub fn new<F>(
element_count: usize, element_count: usize,
orientation: Orientation, orientation: ListAlignment,
overdraw: Pixels, overdraw: Pixels,
cx: &mut ViewContext<V>, render_item: F,
mut render_item: F,
) -> Self ) -> Self
where where
F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement, F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
V: 'static,
{ {
let mut items = SumTree::new(); let mut items = SumTree::new();
items.extend((0..element_count).map(|_| ListItem::Unrendered), &()); items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
let view = cx.view().clone();
Self(Rc::new(RefCell::new(StateInner { Self(Rc::new(RefCell::new(StateInner {
last_layout_width: None, last_layout_width: None,
render_item: Box::new(move |ix, cx| { render_item: Box::new(render_item),
view.update(cx, |view, cx| render_item(view, ix, cx))
}),
items, items,
logical_scroll_top: None, logical_scroll_top: None,
orientation, alignment: orientation,
overdraw, overdraw,
scroll_handler: None, scroll_handler: None,
}))) })))
@ -179,7 +174,7 @@ impl StateInner {
.max(px(0.)) .max(px(0.))
.min(scroll_max); .min(scroll_max);
if self.orientation == Orientation::Bottom && new_scroll_top == scroll_max { if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
self.logical_scroll_top = None; self.logical_scroll_top = None;
} else { } else {
let mut cursor = self.items.cursor::<ListItemSummary>(); let mut cursor = self.items.cursor::<ListItemSummary>();
@ -208,12 +203,12 @@ impl StateInner {
fn logical_scroll_top(&self) -> ListOffset { fn logical_scroll_top(&self) -> ListOffset {
self.logical_scroll_top self.logical_scroll_top
.unwrap_or_else(|| match self.orientation { .unwrap_or_else(|| match self.alignment {
Orientation::Top => ListOffset { ListAlignment::Top => ListOffset {
item_ix: 0, item_ix: 0,
offset_in_item: px(0.), offset_in_item: px(0.),
}, },
Orientation::Bottom => ListOffset { ListAlignment::Bottom => ListOffset {
item_ix: self.items.summary().count, item_ix: self.items.summary().count,
offset_in_item: px(0.), offset_in_item: px(0.),
}, },
@ -344,12 +339,12 @@ impl Element for List {
offset_in_item: rendered_height - bounds.size.height, offset_in_item: rendered_height - bounds.size.height,
}; };
match state.orientation { match state.alignment {
Orientation::Top => { ListAlignment::Top => {
scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.)); scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
state.logical_scroll_top = Some(scroll_top); state.logical_scroll_top = Some(scroll_top);
} }
Orientation::Bottom => { ListAlignment::Bottom => {
scroll_top = ListOffset { scroll_top = ListOffset {
item_ix: cursor.start().0, item_ix: cursor.start().0,
offset_in_item: rendered_height - bounds.size.height, offset_in_item: rendered_height - bounds.size.height,

View file

@ -1,6 +1,7 @@
#[macro_use] #[macro_use]
mod action; mod action;
mod app; mod app;
mod assets; mod assets;
mod color; mod color;
mod element; mod element;
@ -15,6 +16,7 @@ mod keymap;
mod platform; mod platform;
pub mod prelude; pub mod prelude;
mod scene; mod scene;
mod shared_string;
mod style; mod style;
mod styled; mod styled;
mod subscription; mod subscription;
@ -57,6 +59,7 @@ pub use scene::*;
pub use serde; pub use serde;
pub use serde_derive; pub use serde_derive;
pub use serde_json; pub use serde_json;
pub use shared_string::*;
pub use smallvec; pub use smallvec;
pub use smol::Timer; pub use smol::Timer;
pub use style::*; pub use style::*;
@ -76,6 +79,7 @@ use serde::{Deserialize, Serialize};
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
borrow::{Borrow, BorrowMut}, borrow::{Borrow, BorrowMut},
sync::Arc,
}; };
use taffy::TaffyLayoutEngine; use taffy::TaffyLayoutEngine;
@ -210,61 +214,3 @@ impl<T> Flatten<T> for Result<T> {
self self
} }
} }
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
pub struct SharedString(ArcCow<'static, str>);
impl Default for SharedString {
fn default() -> Self {
Self(ArcCow::Owned("".into()))
}
}
impl AsRef<str> for SharedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl std::fmt::Debug for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_ref())
}
}
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl Serialize for SharedString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}
impl<'de> Deserialize<'de> for SharedString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(SharedString::from(s))
}
}

View file

@ -0,0 +1,101 @@
use derive_more::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, sync::Arc};
use util::arc_cow::ArcCow;
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
pub struct SharedString(ArcCow<'static, str>);
impl Default for SharedString {
fn default() -> Self {
Self(ArcCow::Owned("".into()))
}
}
impl AsRef<str> for SharedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for SharedString {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl std::fmt::Debug for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for SharedString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.as_ref())
}
}
impl PartialEq<String> for SharedString {
fn eq(&self, other: &String) -> bool {
self.as_ref() == other
}
}
impl PartialEq<SharedString> for String {
fn eq(&self, other: &SharedString) -> bool {
self == other.as_ref()
}
}
impl PartialEq<str> for SharedString {
fn eq(&self, other: &str) -> bool {
self.as_ref() == other
}
}
impl<'a> PartialEq<&'a str> for SharedString {
fn eq(&self, other: &&'a str) -> bool {
self.as_ref() == *other
}
}
impl Into<Arc<str>> for SharedString {
fn into(self) -> Arc<str> {
match self.0 {
ArcCow::Borrowed(borrowed) => Arc::from(borrowed),
ArcCow::Owned(owned) => owned.clone(),
}
}
}
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl Into<String> for SharedString {
fn into(self) -> String {
self.0.to_string()
}
}
impl Serialize for SharedString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}
impl<'de> Deserialize<'de> for SharedString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(SharedString::from(s))
}
}