1225 lines · 42.2 KB · Rust
RawDownload
mod channel_index; use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};use anyhow::{anyhow, Result};use channel_index::ChannelIndex;use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore};use collections::{hash_map, HashMap, HashSet};use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};use gpui::{    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, SharedString, Task,    WeakEntity,};use language::Capability;use rpc::{    proto::{self, ChannelRole, ChannelVisibility},    TypedEnvelope,};use settings::Settings;use std::{mem, sync::Arc, time::Duration};use util::{maybe, ResultExt}; pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub fn init(client: &Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {    let channel_store = cx.new(|cx| ChannelStore::new(client.clone(), user_store.clone(), cx));    cx.set_global(GlobalChannelStore(channel_store));} #[derive(Debug, Clone, Default, PartialEq)]struct NotesVersion {    epoch: u64,    version: clock::Global,} pub struct ChannelStore {    pub channel_index: ChannelIndex,    channel_invitations: Vec<Arc<Channel>>,    channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,    channel_states: HashMap<ChannelId, ChannelState>,    outgoing_invites: HashSet<(ChannelId, UserId)>,    update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,    opened_buffers: HashMap<ChannelId, OpenEntityHandle<ChannelBuffer>>,    opened_chats: HashMap<ChannelId, OpenEntityHandle<ChannelChat>>,    client: Arc<Client>,    did_subscribe: bool,    user_store: Entity<UserStore>,    _rpc_subscriptions: [Subscription; 2],    _watch_connection_status: Task<Option<()>>,    disconnect_channel_buffers_task: Option<Task<()>>,    _update_channels: Task<()>,} #[derive(Clone, Debug)]pub struct Channel {    pub id: ChannelId,    pub name: SharedString,    pub visibility: proto::ChannelVisibility,    pub parent_path: Vec<ChannelId>,} #[derive(Default, Debug)]pub struct ChannelState {    latest_chat_message: Option<u64>,    latest_notes_version: NotesVersion,    observed_notes_version: NotesVersion,    observed_chat_message: Option<u64>,    role: Option<ChannelRole>,} impl Channel {    pub fn link(&self, cx: &App) -> String {        format!(            "{}/channel/{}-{}",            ClientSettings::get_global(cx).server_url,            Self::slug(&self.name),            self.id        )    }     pub fn notes_link(&self, heading: Option<String>, cx: &App) -> String {        self.link(cx)            + "/notes"            + &heading                .map(|h| format!("#{}", Self::slug(&h)))                .unwrap_or_default()    }     pub fn is_root_channel(&self) -> bool {        self.parent_path.is_empty()    }     pub fn root_id(&self) -> ChannelId {        self.parent_path.first().copied().unwrap_or(self.id)    }     pub fn slug(str: &str) -> String {        let slug: String = str            .chars()            .map(|c| if c.is_alphanumeric() { c } else { '-' })            .collect();         slug.trim_matches(|c| c == '-').to_string()    }} #[derive(Debug)]pub struct ChannelMembership {    pub user: Arc<User>,    pub kind: proto::channel_member::Kind,    pub role: proto::ChannelRole,}impl ChannelMembership {    pub fn sort_key(&self) -> MembershipSortKey {        MembershipSortKey {            role_order: match self.role {                proto::ChannelRole::Admin => 0,                proto::ChannelRole::Member => 1,                proto::ChannelRole::Banned => 2,                proto::ChannelRole::Talker => 3,                proto::ChannelRole::Guest => 4,            },            kind_order: match self.kind {                proto::channel_member::Kind::Member => 0,                proto::channel_member::Kind::Invitee => 1,            },            username_order: self.user.github_login.as_str(),        }    }} #[derive(PartialOrd, Ord, PartialEq, Eq)]pub struct MembershipSortKey<'a> {    role_order: u8,    kind_order: u8,    username_order: &'a str,} pub enum ChannelEvent {    ChannelCreated(ChannelId),    ChannelRenamed(ChannelId),} impl EventEmitter<ChannelEvent> for ChannelStore {} enum OpenEntityHandle<E> {    Open(WeakEntity<E>),    Loading(Shared<Task<Result<Entity<E>, Arc<anyhow::Error>>>>),} struct GlobalChannelStore(Entity<ChannelStore>); impl Global for GlobalChannelStore {} impl ChannelStore {    pub fn global(cx: &App) -> Entity<Self> {        cx.global::<GlobalChannelStore>().0.clone()    }     pub fn new(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut Context<Self>) -> Self {        let rpc_subscriptions = [            client.add_message_handler(cx.weak_entity(), Self::handle_update_channels),            client.add_message_handler(cx.weak_entity(), Self::handle_update_user_channels),        ];         let mut connection_status = client.status();        let (update_channels_tx, mut update_channels_rx) = mpsc::unbounded();        let watch_connection_status = cx.spawn(|this, mut cx| async move {            while let Some(status) = connection_status.next().await {                let this = this.upgrade()?;                match status {                    client::Status::Connected { .. } => {                        this.update(&mut cx, |this, cx| this.handle_connect(cx))                            .ok()?                            .await                            .log_err()?;                    }                    client::Status::SignedOut | client::Status::UpgradeRequired => {                        this.update(&mut cx, |this, cx| this.handle_disconnect(false, cx))                            .ok();                    }                    _ => {                        this.update(&mut cx, |this, cx| this.handle_disconnect(true, cx))                            .ok();                    }                }            }            Some(())        });         Self {            channel_invitations: Vec::default(),            channel_index: ChannelIndex::default(),            channel_participants: Default::default(),            outgoing_invites: Default::default(),            opened_buffers: Default::default(),            opened_chats: Default::default(),            update_channels_tx,            client,            user_store,            _rpc_subscriptions: rpc_subscriptions,            _watch_connection_status: watch_connection_status,            disconnect_channel_buffers_task: None,            _update_channels: cx.spawn(|this, mut cx| async move {                maybe!(async move {                    while let Some(update_channels) = update_channels_rx.next().await {                        if let Some(this) = this.upgrade() {                            let update_task = this.update(&mut cx, |this, cx| {                                this.update_channels(update_channels, cx)                            })?;                            if let Some(update_task) = update_task {                                update_task.await.log_err();                            }                        }                    }                    anyhow::Ok(())                })                .await                .log_err();            }),            channel_states: Default::default(),            did_subscribe: false,        }    }     pub fn initialize(&mut self) {        if !self.did_subscribe            && self                .client                .send(proto::SubscribeToChannels {})                .log_err()                .is_some()        {            self.did_subscribe = true;        }    }     pub fn client(&self) -> Arc<Client> {        self.client.clone()    }     /// Returns the number of unique channels in the store    pub fn channel_count(&self) -> usize {        self.channel_index.by_id().len()    }     /// Returns the index of a channel ID in the list of unique channels    pub fn index_of_channel(&self, channel_id: ChannelId) -> Option<usize> {        self.channel_index            .by_id()            .keys()            .position(|id| *id == channel_id)    }     /// Returns an iterator over all unique channels    pub fn channels(&self) -> impl '_ + Iterator<Item = &Arc<Channel>> {        self.channel_index.by_id().values()    }     /// Iterate over all entries in the channel DAG    pub fn ordered_channels(&self) -> impl '_ + Iterator<Item = (usize, &Arc<Channel>)> {        self.channel_index            .ordered_channels()            .iter()            .filter_map(move |id| {                let channel = self.channel_index.by_id().get(id)?;                Some((channel.parent_path.len(), channel))            })    }     pub fn channel_at_index(&self, ix: usize) -> Option<&Arc<Channel>> {        let channel_id = self.channel_index.ordered_channels().get(ix)?;        self.channel_index.by_id().get(channel_id)    }     pub fn channel_at(&self, ix: usize) -> Option<&Arc<Channel>> {        self.channel_index.by_id().values().nth(ix)    }     pub fn has_channel_invitation(&self, channel_id: ChannelId) -> bool {        self.channel_invitations            .iter()            .any(|channel| channel.id == channel_id)    }     pub fn channel_invitations(&self) -> &[Arc<Channel>] {        &self.channel_invitations    }     pub fn channel_for_id(&self, channel_id: ChannelId) -> Option<&Arc<Channel>> {        self.channel_index.by_id().get(&channel_id)    }     pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &App) -> bool {        if let Some(buffer) = self.opened_buffers.get(&channel_id) {            if let OpenEntityHandle::Open(buffer) = buffer {                return buffer.upgrade().is_some();            }        }        false    }     pub fn open_channel_buffer(        &mut self,        channel_id: ChannelId,        cx: &mut Context<Self>,    ) -> Task<Result<Entity<ChannelBuffer>>> {        let client = self.client.clone();        let user_store = self.user_store.clone();        let channel_store = cx.entity();        self.open_channel_resource(            channel_id,            |this| &mut this.opened_buffers,            |channel, cx| ChannelBuffer::new(channel, client, user_store, channel_store, cx),            cx,        )    }     pub fn fetch_channel_messages(        &self,        message_ids: Vec<u64>,        cx: &mut Context<Self>,    ) -> Task<Result<Vec<ChannelMessage>>> {        let request = if message_ids.is_empty() {            None        } else {            Some(                self.client                    .request(proto::GetChannelMessagesById { message_ids }),            )        };        cx.spawn(|this, mut cx| async move {            if let Some(request) = request {                let response = request.await?;                let this = this                    .upgrade()                    .ok_or_else(|| anyhow!("channel store dropped"))?;                let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?;                ChannelMessage::from_proto_vec(response.messages, &user_store, &mut cx).await            } else {                Ok(Vec::new())            }        })    }     pub fn has_channel_buffer_changed(&self, channel_id: ChannelId) -> bool {        self.channel_states            .get(&channel_id)            .is_some_and(|state| state.has_channel_buffer_changed())    }     pub fn has_new_messages(&self, channel_id: ChannelId) -> bool {        self.channel_states            .get(&channel_id)            .is_some_and(|state| state.has_new_messages())    }     pub fn set_acknowledged_message_id(&mut self, channel_id: ChannelId, message_id: Option<u64>) {        if let Some(state) = self.channel_states.get_mut(&channel_id) {            state.latest_chat_message = message_id;        }    }     pub fn last_acknowledge_message_id(&self, channel_id: ChannelId) -> Option<u64> {        self.channel_states.get(&channel_id).and_then(|state| {            if let Some(last_message_id) = state.latest_chat_message {                if state                    .last_acknowledged_message_id()                    .is_some_and(|id| id < last_message_id)                {                    return state.last_acknowledged_message_id();                }            }             None        })    }     pub fn acknowledge_message_id(        &mut self,        channel_id: ChannelId,        message_id: u64,        cx: &mut Context<Self>,    ) {        self.channel_states            .entry(channel_id)            .or_default()            .acknowledge_message_id(message_id);        cx.notify();    }     pub fn update_latest_message_id(        &mut self,        channel_id: ChannelId,        message_id: u64,        cx: &mut Context<Self>,    ) {        self.channel_states            .entry(channel_id)            .or_default()            .update_latest_message_id(message_id);        cx.notify();    }     pub fn acknowledge_notes_version(        &mut self,        channel_id: ChannelId,        epoch: u64,        version: &clock::Global,        cx: &mut Context<Self>,    ) {        self.channel_states            .entry(channel_id)            .or_default()            .acknowledge_notes_version(epoch, version);        cx.notify()    }     pub fn update_latest_notes_version(        &mut self,        channel_id: ChannelId,        epoch: u64,        version: &clock::Global,        cx: &mut Context<Self>,    ) {        self.channel_states            .entry(channel_id)            .or_default()            .update_latest_notes_version(epoch, version);        cx.notify()    }     pub fn open_channel_chat(        &mut self,        channel_id: ChannelId,        cx: &mut Context<Self>,    ) -> Task<Result<Entity<ChannelChat>>> {        let client = self.client.clone();        let user_store = self.user_store.clone();        let this = cx.entity();        self.open_channel_resource(            channel_id,            |this| &mut this.opened_chats,            |channel, cx| ChannelChat::new(channel, this, user_store, client, cx),            cx,        )    }     /// Asynchronously open a given resource associated with a channel.    ///    /// Make sure that the resource is only opened once, even if this method    /// is called multiple times with the same channel id while the first task    /// is still running.    fn open_channel_resource<T, F, Fut>(        &mut self,        channel_id: ChannelId,        get_map: fn(&mut Self) -> &mut HashMap<ChannelId, OpenEntityHandle<T>>,        load: F,        cx: &mut Context<Self>,    ) -> Task<Result<Entity<T>>>    where        F: 'static + FnOnce(Arc<Channel>, AsyncApp) -> Fut,        Fut: Future<Output = Result<Entity<T>>>,        T: 'static,    {        let task = loop {            match get_map(self).entry(channel_id) {                hash_map::Entry::Occupied(e) => match e.get() {                    OpenEntityHandle::Open(entity) => {                        if let Some(entity) = entity.upgrade() {                            break Task::ready(Ok(entity)).shared();                        } else {                            get_map(self).remove(&channel_id);                            continue;                        }                    }                    OpenEntityHandle::Loading(task) => {                        break task.clone();                    }                },                hash_map::Entry::Vacant(e) => {                    let task = cx                        .spawn(move |this, mut cx| async move {                            let channel = this.update(&mut cx, |this, _| {                                this.channel_for_id(channel_id).cloned().ok_or_else(|| {                                    Arc::new(anyhow!("no channel for id: {}", channel_id))                                })                            })??;                             load(channel, cx).await.map_err(Arc::new)                        })                        .shared();                     e.insert(OpenEntityHandle::Loading(task.clone()));                    cx.spawn({                        let task = task.clone();                        move |this, mut cx| async move {                            let result = task.await;                            this.update(&mut cx, |this, _| match result {                                Ok(model) => {                                    get_map(this).insert(

Lines 1–500 of 1225.