From f41af73f327c62b18a3f81ccb98becc36ec5278c Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 20 Nov 2024 17:41:06 +0100 Subject: [PATCH 01/18] feat: add reactions to the chat message model --- src/chat/src/lib.rs | 1 + src/chat/src/message.rs | 2 + src/chat/src/reaction.rs | 20 +++++++ src/discord/src/channel/mod.rs | 1 + src/discord/src/message/mod.rs | 16 +++++- src/discord/src/message/reaction.rs | 81 +++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/chat/src/reaction.rs create mode 100644 src/discord/src/message/reaction.rs diff --git a/src/chat/src/lib.rs b/src/chat/src/lib.rs index 29fe6c3..6694450 100644 --- a/src/chat/src/lib.rs +++ b/src/chat/src/lib.rs @@ -1,3 +1,4 @@ pub mod async_list; pub mod channel; pub mod message; +pub mod reaction; diff --git a/src/chat/src/message.rs b/src/chat/src/message.rs index 5ffea52..9641bc7 100644 --- a/src/chat/src/message.rs +++ b/src/chat/src/message.rs @@ -2,6 +2,7 @@ use chrono::{DateTime, Utc}; use gpui::Element; use crate::async_list::AsyncListItem; +use crate::reaction::MessageReaction; pub trait Message: Clone + AsyncListItem + Send { fn get_author(&self) -> &impl MessageAuthor; @@ -10,6 +11,7 @@ pub trait Message: Clone + AsyncListItem + Send { fn get_nonce(&self) -> Option<&String>; fn should_group(&self, previous: &Self) -> bool; fn get_timestamp(&self) -> Option>; + fn get_reactions(&self) -> Vec; } pub trait MessageAuthor: PartialEq + Eq { diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs new file mode 100644 index 0000000..42bba92 --- /dev/null +++ b/src/chat/src/reaction.rs @@ -0,0 +1,20 @@ +use gpui::Rgba; + +#[derive(Copy, Clone, Debug)] +pub enum MessageReactionType { + Normal, + Burst, +} + +#[derive(Clone, Debug)] +pub enum ReactionEmoji { + Simple(String), + Custom { url: String, animated: bool, name: Option }, +} + +pub trait MessageReaction { + fn get_count(&self, kind: Option) -> u32; + fn get_self_reaction(&self) -> Option; + fn get_emoji(&self) -> ReactionEmoji; + fn get_burst_colors(&self) -> Vec; +} diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index eeb6590..e8ef4ac 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -61,6 +61,7 @@ impl Channel for DiscordChannel { id: Snowflake { content: 0 }, nonce: Some(nonce), creation_time: Timestamp::now(), + reactions: vec![], } } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 96ec2da..3024700 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -4,11 +4,13 @@ use content::DiscordMessageContent; use gpui::{Element, IntoElement}; use scope_chat::{async_list::AsyncListItem, message::Message}; use serenity::all::Nonce; - +use scope_chat::message::MessageAuthor; +use scope_chat::reaction::MessageReaction; use crate::snowflake::Snowflake; pub mod author; pub mod content; +mod reaction; #[derive(Clone, Debug)] pub struct DiscordMessage { @@ -17,10 +19,15 @@ pub struct DiscordMessage { pub id: Snowflake, pub nonce: Option, pub creation_time: serenity::model::Timestamp, + pub reactions: Vec, } impl DiscordMessage { pub fn from_serenity(msg: &serenity::all::Message) -> Self { + let reactions = &msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_serenity(r)).filter_map(Result::ok).collect::>(); + if !reactions.is_empty() { + println!("Reactions: {:?}", reactions); + } DiscordMessage { id: Snowflake { content: msg.id.get() }, author: DiscordMessageAuthor { @@ -37,12 +44,13 @@ impl DiscordMessage { Nonce::String(s) => s, }), creation_time: msg.timestamp, + reactions: reactions.clone(), } } } impl Message for DiscordMessage { - fn get_author(&self) -> &impl scope_chat::message::MessageAuthor { + fn get_author(&self) -> &impl MessageAuthor { &self.author } @@ -69,6 +77,10 @@ impl Message for DiscordMessage { DateTime::from_timestamp_millis(ts) } + + fn get_reactions(&self) -> Vec { + self.reactions.clone() + } } impl AsyncListItem for DiscordMessage { diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs new file mode 100644 index 0000000..99657dd --- /dev/null +++ b/src/discord/src/message/reaction.rs @@ -0,0 +1,81 @@ +use gpui::Rgba; +use serenity::all::ReactionType; +use scope_chat::reaction::{MessageReaction, ReactionEmoji, MessageReactionType}; + +#[derive(Clone, Debug)] +pub struct DiscordMessageReaction { + pub count: u32, + pub count_burst: u32, + pub self_reaction: Option, + pub emoji: ReactionEmoji, + pub burst_colors: Vec, +} + +impl DiscordMessageReaction { + pub fn from_serenity(reaction: &serenity::all::MessageReaction) -> Result { + let emoji = match &reaction.reaction_type { + ReactionType::Custom { animated, id, name } => { + ReactionEmoji::Custom { + url: format!("https://cdn.discordapp.com/emojis/{}.png", id), + animated: *animated, + name: name.clone(), + } + } + ReactionType::Unicode(character) => { + ReactionEmoji::Simple(character.clone()) + } + ty => { + return Err(format!("Unsupported reaction type: {:?}", ty)); + } + }; + + let count = reaction.count_details.normal as u32; + let count_burst = reaction.count_details.burst as u32; + let burst_colors = reaction.burst_colours.iter().map(|color| { + Rgba { + r: color.r() as f32, + g: color.g() as f32, + b: color.b() as f32, + a: 1f32, + } + }).collect(); + + let self_reaction = if reaction.me { + Some(MessageReactionType::Normal) + } else if reaction.me_burst { + Some(MessageReactionType::Burst) + } else { + None + }; + + Ok(DiscordMessageReaction { + count, + count_burst, + self_reaction, + emoji, + burst_colors, + }) + } +} + +impl MessageReaction for DiscordMessageReaction { + fn get_count(&self, kind: Option) -> u32 { + match kind { + Some(MessageReactionType::Burst) => self.count_burst, + Some(MessageReactionType::Normal) => self.count, + None => self.count + self.count_burst, + } + } + + fn get_self_reaction(&self) -> Option { + self.self_reaction + } + + fn get_emoji(&self) -> ReactionEmoji { + self.emoji.clone() + } + + fn get_burst_colors(&self) -> Vec { + self.burst_colors.clone() + } +} From 002e44c6d2b68868d4a6c8bee21e4b9aace608d3 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Sun, 24 Nov 2024 19:04:17 +0100 Subject: [PATCH 02/18] refactor: reactions use the serenity object as a store now --- src/chat/src/reaction.rs | 2 +- src/discord/src/message/mod.rs | 10 ++-- src/discord/src/message/reaction.rs | 87 ++++++++++++----------------- 3 files changed, 42 insertions(+), 57 deletions(-) diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index 42bba92..512b22f 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -13,7 +13,7 @@ pub enum ReactionEmoji { } pub trait MessageReaction { - fn get_count(&self, kind: Option) -> u32; + fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; fn get_emoji(&self) -> ReactionEmoji; fn get_burst_colors(&self) -> Vec; diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 3024700..c515c12 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -1,12 +1,12 @@ +use crate::snowflake::Snowflake; use author::{DiscordMessageAuthor, DisplayName}; use chrono::{DateTime, Utc}; use content::DiscordMessageContent; use gpui::{Element, IntoElement}; -use scope_chat::{async_list::AsyncListItem, message::Message}; -use serenity::all::Nonce; use scope_chat::message::MessageAuthor; use scope_chat::reaction::MessageReaction; -use crate::snowflake::Snowflake; +use scope_chat::{async_list::AsyncListItem, message::Message}; +use serenity::all::Nonce; pub mod author; pub mod content; @@ -24,9 +24,9 @@ pub struct DiscordMessage { impl DiscordMessage { pub fn from_serenity(msg: &serenity::all::Message) -> Self { - let reactions = &msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_serenity(r)).filter_map(Result::ok).collect::>(); + let reactions = &msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_serenity(r)).collect::>(); if !reactions.is_empty() { - println!("Reactions: {:?}", reactions); + println!("Reactions: {:?}", reactions); } DiscordMessage { id: Snowflake { content: msg.id.get() }, diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 99657dd..b306df3 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -4,16 +4,38 @@ use scope_chat::reaction::{MessageReaction, ReactionEmoji, MessageReactionType}; #[derive(Clone, Debug)] pub struct DiscordMessageReaction { - pub count: u32, - pub count_burst: u32, - pub self_reaction: Option, - pub emoji: ReactionEmoji, - pub burst_colors: Vec, + pub data: serenity::all::MessageReaction, } impl DiscordMessageReaction { - pub fn from_serenity(reaction: &serenity::all::MessageReaction) -> Result { - let emoji = match &reaction.reaction_type { + pub fn from_serenity(reaction: &serenity::all::MessageReaction) -> Self { + DiscordMessageReaction { + data: reaction.clone(), + } + } +} + +impl MessageReaction for DiscordMessageReaction { + fn get_count(&self, kind: Option) -> u64 { + match kind { + Some(MessageReactionType::Burst) => self.data.count_details.burst, + Some(MessageReactionType::Normal) => self.data.count_details.normal, + None => self.data.count, + } + } + + fn get_self_reaction(&self) -> Option { + if self.data.me { + Some(MessageReactionType::Normal) + } else if self.data.me_burst { + Some(MessageReactionType::Burst) + } else { + None + } + } + + fn get_emoji(&self) -> ReactionEmoji { + match &self.data.reaction_type { ReactionType::Custom { animated, id, name } => { ReactionEmoji::Custom { url: format!("https://cdn.discordapp.com/emojis/{}.png", id), @@ -25,57 +47,20 @@ impl DiscordMessageReaction { ReactionEmoji::Simple(character.clone()) } ty => { - return Err(format!("Unsupported reaction type: {:?}", ty)); + eprintln!("Unsupported reaction type: {:?}", ty); + ReactionEmoji::Simple("❓".to_string()) } - }; + } + } - let count = reaction.count_details.normal as u32; - let count_burst = reaction.count_details.burst as u32; - let burst_colors = reaction.burst_colours.iter().map(|color| { + fn get_burst_colors(&self) -> Vec { + self.data.burst_colours.iter().map(|color| { Rgba { r: color.r() as f32, g: color.g() as f32, b: color.b() as f32, a: 1f32, } - }).collect(); - - let self_reaction = if reaction.me { - Some(MessageReactionType::Normal) - } else if reaction.me_burst { - Some(MessageReactionType::Burst) - } else { - None - }; - - Ok(DiscordMessageReaction { - count, - count_burst, - self_reaction, - emoji, - burst_colors, - }) - } -} - -impl MessageReaction for DiscordMessageReaction { - fn get_count(&self, kind: Option) -> u32 { - match kind { - Some(MessageReactionType::Burst) => self.count_burst, - Some(MessageReactionType::Normal) => self.count, - None => self.count + self.count_burst, - } - } - - fn get_self_reaction(&self) -> Option { - self.self_reaction - } - - fn get_emoji(&self) -> ReactionEmoji { - self.emoji.clone() - } - - fn get_burst_colors(&self) -> Vec { - self.burst_colors.clone() + }).collect() } } From 330c07577d2b933ac8878226dfbaf9a31a2a012a Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Thu, 28 Nov 2024 23:33:26 +0100 Subject: [PATCH 03/18] feat: use both serenity and local state, load live reactions --- Cargo.lock | 1 + src/chat/src/channel.rs | 4 +- src/chat/src/message.rs | 4 +- src/chat/src/reaction.rs | 23 ++- src/discord/Cargo.toml | 1 + src/discord/src/channel/mod.rs | 37 +++-- src/discord/src/client.rs | 78 +++++++-- src/discord/src/message/mod.rs | 15 +- src/discord/src/message/reaction.rs | 240 +++++++++++++++++++++++----- src/discord/src/snowflake.rs | 6 + src/ui/src/channel/message_list.rs | 18 ++- src/ui/src/channel/mod.rs | 31 +++- 12 files changed, 375 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac5bc33..3930e71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5474,6 +5474,7 @@ version = "0.1.0" dependencies = [ "chrono", "gpui", + "log", "scope-backend-cache", "scope-chat", "serenity", diff --git a/src/chat/src/channel.rs b/src/chat/src/channel.rs index 12d2a3b..53564c1 100644 --- a/src/chat/src/channel.rs +++ b/src/chat/src/channel.rs @@ -1,11 +1,13 @@ use tokio::sync::broadcast; use crate::{async_list::AsyncList, message::Message}; +use crate::reaction::ReactionOperation; pub trait Channel: AsyncList + Send + Sync + Clone { type Message: Message; - fn get_receiver(&self) -> broadcast::Receiver; + fn get_message_receiver(&self) -> broadcast::Receiver; + fn get_reaction_receiver(&self) -> broadcast::Receiver<(String, ReactionOperation)>; fn send_message(&self, content: String, nonce: String) -> Self::Message; } diff --git a/src/chat/src/message.rs b/src/chat/src/message.rs index 9641bc7..f17dc3b 100644 --- a/src/chat/src/message.rs +++ b/src/chat/src/message.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use gpui::Element; use crate::async_list::AsyncListItem; -use crate::reaction::MessageReaction; +use crate::reaction::ReactionList; pub trait Message: Clone + AsyncListItem + Send { fn get_author(&self) -> &impl MessageAuthor; @@ -11,7 +11,7 @@ pub trait Message: Clone + AsyncListItem + Send { fn get_nonce(&self) -> Option<&String>; fn should_group(&self, previous: &Self) -> bool; fn get_timestamp(&self) -> Option>; - fn get_reactions(&self) -> Vec; + fn get_reactions(&mut self) -> &mut impl ReactionList; } pub trait MessageAuthor: PartialEq + Eq { diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index 512b22f..dc51193 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use gpui::Rgba; #[derive(Copy, Clone, Debug)] @@ -6,15 +7,33 @@ pub enum MessageReactionType { Burst, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum ReactionEmoji { Simple(String), Custom { url: String, animated: bool, name: Option }, } -pub trait MessageReaction { +pub trait MessageReaction: Debug { fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; fn get_emoji(&self) -> ReactionEmoji; fn get_burst_colors(&self) -> Vec; + fn increment(&mut self, kind: MessageReactionType, user_is_self: bool, by: isize); +} + +#[derive(Clone, Debug)] +pub enum ReactionOperation { + Add(ReactionEmoji, MessageReactionType), + AddSelf(ReactionEmoji, MessageReactionType), + Remove(ReactionEmoji), + RemoveSelf(ReactionEmoji), + RemoveAll, + RemoveEmoji(ReactionEmoji), +} + +pub trait ReactionList { + fn get_reactions(&self) -> &Vec; + fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction>; + fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize); + fn apply(&mut self, operation: ReactionOperation); } diff --git a/src/discord/Cargo.toml b/src/discord/Cargo.toml index a4996d6..116c759 100644 --- a/src/discord/Cargo.toml +++ b/src/discord/Cargo.toml @@ -10,3 +10,4 @@ serenity = { git = "https://github.com/scopeclient/serenity", version = "0.12" } tokio = "1.41.1" chrono.workspace = true scope-backend-cache = { version = "0.1.0", path = "../cache" } +log = "0.4.22" diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index e8ef4ac..9eea7cd 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -1,6 +1,12 @@ use std::sync::Arc; +use crate::{ + client::DiscordClient, + message::{content::DiscordMessageContent, DiscordMessage}, + snowflake::Snowflake, +}; use scope_backend_cache::async_list::{refcacheslice::Exists, AsyncListCache}; +use scope_chat::reaction::ReactionOperation; use scope_chat::{ async_list::{AsyncList, AsyncListIndex, AsyncListItem, AsyncListResult}, channel::Channel, @@ -8,15 +14,10 @@ use scope_chat::{ use serenity::all::{GetMessages, MessageId, Timestamp}; use tokio::sync::{broadcast, Mutex, Semaphore}; -use crate::{ - client::DiscordClient, - message::{content::DiscordMessageContent, DiscordMessage}, - snowflake::Snowflake, -}; - pub struct DiscordChannel { channel_id: Snowflake, - receiver: broadcast::Receiver, + message_receiver: broadcast::Receiver, + reaction_receiver: broadcast::Receiver<(String, ReactionOperation)>, client: Arc, cache: Arc>>, blocker: Semaphore, @@ -24,13 +25,16 @@ pub struct DiscordChannel { impl DiscordChannel { pub async fn new(client: Arc, channel_id: Snowflake) -> Self { - let (sender, receiver) = broadcast::channel(10); + let (message_sender, message_receiver) = broadcast::channel(10); + client.add_channel_message_sender(channel_id, message_sender).await; - client.add_channel_message_sender(channel_id, sender).await; + let (reaction_sender, reaction_receiver) = broadcast::channel(10); + client.add_channel_reaction_sender(channel_id, reaction_sender).await; DiscordChannel { channel_id, - receiver, + message_receiver, + reaction_receiver, client, cache: Arc::new(Mutex::new(AsyncListCache::new())), blocker: Semaphore::new(1), @@ -41,8 +45,12 @@ impl DiscordChannel { impl Channel for DiscordChannel { type Message = DiscordMessage; - fn get_receiver(&self) -> broadcast::Receiver { - self.receiver.resubscribe() + fn get_message_receiver(&self) -> broadcast::Receiver { + self.message_receiver.resubscribe() + } + + fn get_reaction_receiver(&self) -> broadcast::Receiver<(String, ReactionOperation)> { + self.reaction_receiver.resubscribe() } fn send_message(&self, content: String, nonce: String) -> DiscordMessage { @@ -61,7 +69,7 @@ impl Channel for DiscordChannel { id: Snowflake { content: 0 }, nonce: Some(nonce), creation_time: Timestamp::now(), - reactions: vec![], + reactions: Default::default(), } } } @@ -233,7 +241,8 @@ impl Clone for DiscordChannel { fn clone(&self) -> Self { Self { channel_id: self.channel_id, - receiver: self.receiver.resubscribe(), + message_receiver: self.message_receiver.resubscribe(), + reaction_receiver: self.reaction_receiver.resubscribe(), client: self.client.clone(), cache: self.cache.clone(), blocker: Semaphore::new(1), diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index cc33f78..c09cf2d 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -3,12 +3,6 @@ use std::{ sync::{Arc, OnceLock}, }; -use serenity::{ - all::{Cache, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, Http, Message, MessageId, Ready}, - async_trait, -}; -use tokio::sync::{broadcast, RwLock}; - use crate::{ channel::DiscordChannel, message::{ @@ -17,6 +11,14 @@ use crate::{ }, snowflake::Snowflake, }; +use scope_chat::reaction::{MessageReactionType, ReactionOperation}; +use serenity::all::Reaction; +use serenity::{ + all::{Cache, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, Http, Message, MessageId, Ready}, + async_trait, +}; +use tokio::sync::{broadcast, RwLock}; +use crate::message::reaction::discord_reaction_to_emoji; #[allow(dead_code)] struct SerenityClient { @@ -29,6 +31,7 @@ struct SerenityClient { #[derive(Default)] pub struct DiscordClient { channel_message_event_handlers: RwLock>>>, + channel_reaction_event_handlers: RwLock>>>, client: OnceLock, user: OnceLock, channels: RwLock>>, @@ -38,10 +41,7 @@ impl DiscordClient { pub async fn new(token: String) -> Arc { let client = Arc::new(DiscordClient::default()); - let mut discord = serenity::Client::builder(token, GatewayIntents::all()) - .event_handler_arc(client.clone()) - .await - .expect("Error creating client"); + let mut discord = serenity::Client::builder(token, GatewayIntents::all()).event_handler_arc(client.clone()).await.expect("Error creating client"); let _ = client.client.set(SerenityClient { // voice_manager: discord.voice_manager.clone(), @@ -70,6 +70,10 @@ impl DiscordClient { self.channel_message_event_handlers.write().await.entry(channel).or_default().push(sender); } + pub async fn add_channel_reaction_sender(&self, channel: Snowflake, sender: broadcast::Sender<(String, ReactionOperation)>) { + self.channel_reaction_event_handlers.write().await.entry(channel).or_default().push(sender); + } + pub async fn channel(self: Arc, channel_id: Snowflake) -> Arc { let self_clone = self.clone(); let mut channels = self_clone.channels.write().await; @@ -107,8 +111,15 @@ impl DiscordClient { // FIXME: proper error handling Some(ChannelId::new(channel_id.content).message(self.discord().http.clone(), MessageId::new(message_id.content)).await.unwrap()) } -} + async fn send_reaction_operation(&self, channel_id: Snowflake, message_id: MessageId, operation: ReactionOperation) { + if let Some(vec) = self.channel_reaction_event_handlers.read().await.get(&channel_id) { + for sender in vec { + let _ = sender.send((message_id.to_string(), operation.clone())); + } + } + } +} #[async_trait] impl EventHandler for DiscordClient { @@ -130,4 +141,49 @@ impl EventHandler for DiscordClient { } } } + + async fn reaction_add(&self, _: Context, reaction: Reaction) { + let channel_snowflake = reaction.channel_id.into(); + + let ty = if reaction.burst { + MessageReactionType::Burst + } else { + MessageReactionType::Normal + }; + + let emoji = discord_reaction_to_emoji(&reaction.emoji); + + let operation = if reaction.member.is_none_or(|member| member.user.id.get() == self.user().id.parse::().unwrap()) { + ReactionOperation::AddSelf(emoji, ty) + } else { + ReactionOperation::Add(emoji, ty) + }; + + self.send_reaction_operation(channel_snowflake, reaction.message_id, operation).await; + } + + async fn reaction_remove(&self, _: Context, reaction: Reaction) { + let channel_snowflake = reaction.channel_id.into(); + + let emoji = discord_reaction_to_emoji(&reaction.emoji); + + let operation = if reaction.member.is_none_or(|member| member.user.id.get() == self.user().id.parse::().unwrap()) { + ReactionOperation::RemoveSelf(emoji) + } else { + ReactionOperation::Remove(emoji) + }; + + self.send_reaction_operation(channel_snowflake, reaction.message_id, operation).await; + } + + async fn reaction_remove_all(&self, _: Context, channel_id: ChannelId, removed_from_message_id: MessageId) { + let channel_snowflake = channel_id.into(); + self.send_reaction_operation(channel_snowflake, removed_from_message_id, ReactionOperation::RemoveAll).await; + } + + async fn reaction_remove_emoji(&self, _: Context, removed_reactions: Reaction) { + let channel_snowflake = removed_reactions.channel_id.into(); + let emoji = discord_reaction_to_emoji(&removed_reactions.emoji); + self.send_reaction_operation(channel_snowflake, removed_reactions.message_id, ReactionOperation::RemoveEmoji(emoji)).await; + } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index c515c12..956e9e1 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -1,16 +1,17 @@ +use crate::message::reaction::DiscordReactionList; use crate::snowflake::Snowflake; use author::{DiscordMessageAuthor, DisplayName}; use chrono::{DateTime, Utc}; use content::DiscordMessageContent; use gpui::{Element, IntoElement}; use scope_chat::message::MessageAuthor; -use scope_chat::reaction::MessageReaction; +use scope_chat::reaction::ReactionList; use scope_chat::{async_list::AsyncListItem, message::Message}; use serenity::all::Nonce; pub mod author; pub mod content; -mod reaction; +pub mod reaction; #[derive(Clone, Debug)] pub struct DiscordMessage { @@ -19,12 +20,12 @@ pub struct DiscordMessage { pub id: Snowflake, pub nonce: Option, pub creation_time: serenity::model::Timestamp, - pub reactions: Vec, + pub reactions: DiscordReactionList, } impl DiscordMessage { pub fn from_serenity(msg: &serenity::all::Message) -> Self { - let reactions = &msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_serenity(r)).collect::>(); + let reactions = msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_message(r)).collect::>(); if !reactions.is_empty() { println!("Reactions: {:?}", reactions); } @@ -44,7 +45,7 @@ impl DiscordMessage { Nonce::String(s) => s, }), creation_time: msg.timestamp, - reactions: reactions.clone(), + reactions: DiscordReactionList::new(reactions), } } } @@ -78,8 +79,8 @@ impl Message for DiscordMessage { DateTime::from_timestamp_millis(ts) } - fn get_reactions(&self) -> Vec { - self.reactions.clone() + fn get_reactions(&mut self) -> &mut impl ReactionList { + &mut self.reactions } } diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index b306df3..4b46e7d 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,66 +1,220 @@ use gpui::Rgba; +use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; use serenity::all::ReactionType; -use scope_chat::reaction::{MessageReaction, ReactionEmoji, MessageReactionType}; +use MessageReactionType::Burst; +use scope_chat::reaction::MessageReactionType::Normal; + +#[derive(Clone, Debug)] +pub enum ReactionData { + Message(serenity::all::MessageReaction), + Local { + count_normal: u64, + count_burst: u64, + me: Option, + emoji: ReactionEmoji, + burst_colours: Vec, + }, +} + +impl ReactionData { + fn count(&self) -> u64 { + match self { + ReactionData::Message(reaction) => reaction.count, + ReactionData::Local { count_normal, .. } => *count_normal, + } + } + + fn count_burst(&self) -> u64 { + match self { + ReactionData::Message(reaction) => reaction.count, + ReactionData::Local { count_burst, .. } => *count_burst, + } + } +} #[derive(Clone, Debug)] pub struct DiscordMessageReaction { - pub data: serenity::all::MessageReaction, + pub data: ReactionData, } impl DiscordMessageReaction { - pub fn from_serenity(reaction: &serenity::all::MessageReaction) -> Self { - DiscordMessageReaction { - data: reaction.clone(), - } + pub fn from_message(reaction: &serenity::all::MessageReaction) -> Self { + DiscordMessageReaction { + data: ReactionData::Message(reaction.clone()), } + } + + fn use_local(&mut self) { + let (count_normal, count_burst) = match &self.data { + ReactionData::Message(reaction) => (reaction.count_details.normal, reaction.count_details.burst), + ReactionData::Local { + count_normal, count_burst, .. + } => (*count_normal, *count_burst), + }; + let me = self.get_self_reaction(); + let emoji = self.get_emoji(); + let burst_colours = self.get_burst_colors(); + self.data = ReactionData::Local { + count_normal, + count_burst, + me, + emoji, + burst_colours, + } + } } impl MessageReaction for DiscordMessageReaction { - fn get_count(&self, kind: Option) -> u64 { - match kind { - Some(MessageReactionType::Burst) => self.data.count_details.burst, - Some(MessageReactionType::Normal) => self.data.count_details.normal, - None => self.data.count, - } + fn get_count(&self, kind: Option) -> u64 { + match kind { + Some(Burst) => self.data.count_burst(), + Some(Normal) => self.data.count(), + None => self.data.count_burst() + self.data.count(), } + } - fn get_self_reaction(&self) -> Option { - if self.data.me { - Some(MessageReactionType::Normal) - } else if self.data.me_burst { - Some(MessageReactionType::Burst) + fn get_self_reaction(&self) -> Option { + match &self.data { + ReactionData::Message(reaction) => { + if reaction.me { + Some(Normal) + } else if reaction.me_burst { + Some(Burst) } else { - None + None } + } + ReactionData::Local { me, .. } => me.clone(), + } + } + + fn get_emoji(&self) -> ReactionEmoji { + match &self.data { + ReactionData::Message(reaction) => discord_reaction_to_emoji(&reaction.reaction_type), + ReactionData::Local { emoji, .. } => emoji.clone(), + } + } + + fn get_burst_colors(&self) -> Vec { + match &self.data { + ReactionData::Message(reaction) => reaction + .burst_colours + .iter() + .map(|color| Rgba { + r: color.r() as f32, + g: color.g() as f32, + b: color.b() as f32, + a: 1f32, + }) + .collect(), + ReactionData::Local { burst_colours, .. } => burst_colours.clone(), } + } - fn get_emoji(&self) -> ReactionEmoji { - match &self.data.reaction_type { - ReactionType::Custom { animated, id, name } => { - ReactionEmoji::Custom { - url: format!("https://cdn.discordapp.com/emojis/{}.png", id), - animated: *animated, - name: name.clone(), - } - } - ReactionType::Unicode(character) => { - ReactionEmoji::Simple(character.clone()) - } - ty => { - eprintln!("Unsupported reaction type: {:?}", ty); - ReactionEmoji::Simple("❓".to_string()) - } + fn increment(&mut self, kind: MessageReactionType, user_is_self: bool, by: isize) { + self.use_local(); + match kind { + Burst => { + if let ReactionData::Local { count_burst, .. } = &mut self.data { + *count_burst = (*count_burst as isize + by).max(0) as u64; + } + } + Normal => { + if let ReactionData::Local { count_normal, .. } = &mut self.data { + *count_normal = (*count_normal as isize + by).max(0) as u64; } + } } - fn get_burst_colors(&self) -> Vec { - self.data.burst_colours.iter().map(|color| { - Rgba { - r: color.r() as f32, - g: color.g() as f32, - b: color.b() as f32, - a: 1f32, - } - }).collect() + if user_is_self { + if let ReactionData::Local { me, .. } = &mut self.data { + if by < 0 { + *me = None; + } else { + *me = Some(kind); + } + } + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct DiscordReactionList { + reactions: Vec, +} + +impl DiscordReactionList { + pub fn new(reactions: Vec) -> Self { + DiscordReactionList { reactions } + } +} + +impl ReactionList for DiscordReactionList { + fn get_reactions(&self) -> &Vec { + &self.reactions + } + + fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction> { + self.reactions.iter().find(|reaction| reaction.get_emoji() == *emoji) + } + + fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize) { + if let Some(reaction) = self.reactions.iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { + reaction.increment(kind, user_is_self, by); + if reaction.get_count(None) == 0 { + self.reactions.retain(|reaction| reaction.get_emoji() != *emoji); + } + } else if by > 0 { + let mut reaction = DiscordMessageReaction { + data: ReactionData::Local { + count_normal: 0, + count_burst: 0, + me: None, + emoji: emoji.clone(), + burst_colours: vec![], + }, + }; + + reaction.increment(kind, user_is_self, by); + self.reactions.push(reaction); } + } + + fn apply(&mut self, operation: ReactionOperation) { + match operation { + ReactionOperation::Add(emoji, ty) => { + self.increment(&emoji, ty, false, 1); + } + ReactionOperation::AddSelf(emoji, ty) => { + self.increment(&emoji, ty, true, 1); + } + ReactionOperation::Remove(emoji) => { + self.increment(&emoji, Normal, false, -1); + } + ReactionOperation::RemoveSelf(emoji) => { + self.increment(&emoji, Normal, true, -1); + } + ReactionOperation::RemoveAll => { + self.reactions.clear(); + } + ReactionOperation::RemoveEmoji(emoji) => { + self.reactions.retain(|reaction| reaction.get_emoji() != emoji); + } + } + } } + +pub fn discord_reaction_to_emoji(reaction: &serenity::all::ReactionType) -> ReactionEmoji { + match reaction { + ReactionType::Custom { animated, id, name } => ReactionEmoji::Custom { + url: format!("https://cdn.discordapp.com/emojis/{}.png", id), + animated: *animated, + name: name.clone(), + }, + ReactionType::Unicode(character) => ReactionEmoji::Simple(character.clone()), + ty => { + eprintln!("Unsupported reaction type: {:?}", ty); + ReactionEmoji::Simple("❓".to_string()) + } + } +} \ No newline at end of file diff --git a/src/discord/src/snowflake.rs b/src/discord/src/snowflake.rs index 1d70ebd..57d9cff 100644 --- a/src/discord/src/snowflake.rs +++ b/src/discord/src/snowflake.rs @@ -9,3 +9,9 @@ impl ToString for Snowflake { self.content.to_string() } } + +impl From for Snowflake where T: Into { + fn from(content: T) -> Self { + Snowflake { content: content.into() } + } +} diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index 7eef140..12ac184 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -7,7 +7,7 @@ use scope_chat::{ message::Message, }; use tokio::sync::RwLock; - +use scope_chat::reaction::{ReactionList, ReactionOperation}; use super::message::{message, MessageGroup}; #[derive(Clone, Copy)] @@ -115,6 +115,22 @@ where }); } + pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: (String, ReactionOperation)) { + self.cache.update(cx, |borrow: &mut Vec>>, cx| { + for item in borrow.iter_mut() { + if let Element::Resolved(Some(haystack)) = item { + if haystack.get_identifier() == reaction.0 { + let reactions = haystack.get_reactions(); + reactions.apply(reaction.1); + + cx.notify(); + return; + } + } + } + }); + } + fn list_state(&self, cx: &mut gpui::ViewContext) -> ListState { let bounds_model = self.bounds_flags.clone(); diff --git a/src/ui/src/channel/mod.rs b/src/ui/src/channel/mod.rs index f61e279..600aaeb 100644 --- a/src/ui/src/channel/mod.rs +++ b/src/ui/src/channel/mod.rs @@ -13,7 +13,8 @@ pub struct ChannelView { impl ChannelView { pub fn create(ctx: &mut gpui::ViewContext<'_, ChannelView>, channel: C) -> Self { - let channel_listener = channel.get_receiver(); + let channel_message_listener = channel.get_message_receiver(); + let channel_reaction_listener = channel.get_reaction_receiver(); let c2 = channel.clone(); @@ -28,7 +29,7 @@ impl ChannelView { loop { let (sender, receiver) = catty::oneshot(); - let mut l = channel_listener.resubscribe(); + let mut l = channel_message_listener.resubscribe(); tokio::spawn(async move { sender.send(l.recv().await).unwrap(); @@ -45,6 +46,32 @@ impl ChannelView { }) .detach(); + + let async_model = list_view.clone(); + let mut async_ctx = ctx.to_async(); + ctx + .foreground_executor() + .spawn(async move { + loop { + let (sender, receiver) = catty::oneshot(); + + let mut l = channel_reaction_listener.resubscribe(); + + tokio::spawn(async move { + sender.send(l.recv().await).unwrap(); + }); + + let reaction = receiver.await.unwrap().unwrap(); + async_model + .update(&mut async_ctx, |data, ctx| { + data.update_reaction(ctx, reaction); + ctx.notify(); + }) + .unwrap(); + } + }) + .detach(); + let message_input = ctx.new_view(|cx| { let mut input = components::input::TextInput::new(cx); From e66d5c8ee04987ea6becfbd6505691c7c5c61cc1 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Thu, 28 Nov 2024 23:41:48 +0100 Subject: [PATCH 04/18] refactor: move out reaction list to own file --- src/discord/src/message/mod.rs | 3 +- src/discord/src/message/reaction.rs | 68 +---------------------- src/discord/src/message/reaction_list.rs | 69 ++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 68 deletions(-) create mode 100644 src/discord/src/message/reaction_list.rs diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 956e9e1..3c0ef14 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -1,4 +1,4 @@ -use crate::message::reaction::DiscordReactionList; +use crate::message::reaction_list::DiscordReactionList; use crate::snowflake::Snowflake; use author::{DiscordMessageAuthor, DisplayName}; use chrono::{DateTime, Utc}; @@ -12,6 +12,7 @@ use serenity::all::Nonce; pub mod author; pub mod content; pub mod reaction; +pub mod reaction_list; #[derive(Clone, Debug)] pub struct DiscordMessage { diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 4b46e7d..0fe7ae9 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,5 +1,5 @@ use gpui::Rgba; -use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; +use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; use serenity::all::ReactionType; use MessageReactionType::Burst; use scope_chat::reaction::MessageReactionType::Normal; @@ -138,72 +138,6 @@ impl MessageReaction for DiscordMessageReaction { } } -#[derive(Clone, Debug, Default)] -pub struct DiscordReactionList { - reactions: Vec, -} - -impl DiscordReactionList { - pub fn new(reactions: Vec) -> Self { - DiscordReactionList { reactions } - } -} - -impl ReactionList for DiscordReactionList { - fn get_reactions(&self) -> &Vec { - &self.reactions - } - - fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction> { - self.reactions.iter().find(|reaction| reaction.get_emoji() == *emoji) - } - - fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize) { - if let Some(reaction) = self.reactions.iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { - reaction.increment(kind, user_is_self, by); - if reaction.get_count(None) == 0 { - self.reactions.retain(|reaction| reaction.get_emoji() != *emoji); - } - } else if by > 0 { - let mut reaction = DiscordMessageReaction { - data: ReactionData::Local { - count_normal: 0, - count_burst: 0, - me: None, - emoji: emoji.clone(), - burst_colours: vec![], - }, - }; - - reaction.increment(kind, user_is_self, by); - self.reactions.push(reaction); - } - } - - fn apply(&mut self, operation: ReactionOperation) { - match operation { - ReactionOperation::Add(emoji, ty) => { - self.increment(&emoji, ty, false, 1); - } - ReactionOperation::AddSelf(emoji, ty) => { - self.increment(&emoji, ty, true, 1); - } - ReactionOperation::Remove(emoji) => { - self.increment(&emoji, Normal, false, -1); - } - ReactionOperation::RemoveSelf(emoji) => { - self.increment(&emoji, Normal, true, -1); - } - ReactionOperation::RemoveAll => { - self.reactions.clear(); - } - ReactionOperation::RemoveEmoji(emoji) => { - self.reactions.retain(|reaction| reaction.get_emoji() != emoji); - } - } - } -} - pub fn discord_reaction_to_emoji(reaction: &serenity::all::ReactionType) -> ReactionEmoji { match reaction { ReactionType::Custom { animated, id, name } => ReactionEmoji::Custom { diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs new file mode 100644 index 0000000..3a4e456 --- /dev/null +++ b/src/discord/src/message/reaction_list.rs @@ -0,0 +1,69 @@ +use crate::message::reaction::{DiscordMessageReaction, ReactionData}; +use scope_chat::reaction::MessageReactionType::Normal; +use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; + +#[derive(Clone, Debug, Default)] +pub struct DiscordReactionList { + reactions: Vec, +} + +impl DiscordReactionList { + pub fn new(reactions: Vec) -> Self { + DiscordReactionList { reactions } + } +} + +impl ReactionList for DiscordReactionList { + fn get_reactions(&self) -> &Vec { + &self.reactions + } + + fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction> { + self.reactions.iter().find(|reaction| reaction.get_emoji() == *emoji) + } + + fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize) { + if let Some(reaction) = self.reactions.iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { + reaction.increment(kind, user_is_self, by); + if reaction.get_count(None) == 0 { + self.reactions.retain(|reaction| reaction.get_emoji() != *emoji); + } + } else if by > 0 { + let mut reaction = DiscordMessageReaction { + data: ReactionData::Local { + count_normal: 0, + count_burst: 0, + me: None, + emoji: emoji.clone(), + burst_colours: vec![], + }, + }; + + reaction.increment(kind, user_is_self, by); + self.reactions.push(reaction); + } + } + + fn apply(&mut self, operation: ReactionOperation) { + match operation { + ReactionOperation::Add(emoji, ty) => { + self.increment(&emoji, ty, false, 1); + } + ReactionOperation::AddSelf(emoji, ty) => { + self.increment(&emoji, ty, true, 1); + } + ReactionOperation::Remove(emoji) => { + self.increment(&emoji, Normal, false, -1); + } + ReactionOperation::RemoveSelf(emoji) => { + self.increment(&emoji, Normal, true, -1); + } + ReactionOperation::RemoveAll => { + self.reactions.clear(); + } + ReactionOperation::RemoveEmoji(emoji) => { + self.reactions.retain(|reaction| reaction.get_emoji() != emoji); + } + } + } +} From f05123e5140d0837458855fe8dc866a6d172be0d Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Fri, 29 Nov 2024 00:24:02 +0100 Subject: [PATCH 05/18] feat: show reactions in the UI --- Cargo.lock | 1 + src/chat/src/channel.rs | 4 +-- src/chat/src/reaction.rs | 8 ++++-- src/discord/Cargo.toml | 1 + src/discord/src/channel/mod.rs | 6 ++-- src/discord/src/client.rs | 6 ++-- src/discord/src/message/mod.rs | 8 ++++-- src/discord/src/message/reaction.rs | 36 ++++++++++++++++++++---- src/discord/src/message/reaction_list.rs | 16 ++++++++++- src/ui/src/channel/message_list.rs | 4 +-- 10 files changed, 68 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3930e71..9716adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5479,6 +5479,7 @@ dependencies = [ "scope-chat", "serenity", "tokio", + "ui", ] [[package]] diff --git a/src/chat/src/channel.rs b/src/chat/src/channel.rs index 53564c1..77fe577 100644 --- a/src/chat/src/channel.rs +++ b/src/chat/src/channel.rs @@ -1,13 +1,13 @@ use tokio::sync::broadcast; +use crate::reaction::ReactionEvent; use crate::{async_list::AsyncList, message::Message}; -use crate::reaction::ReactionOperation; pub trait Channel: AsyncList + Send + Sync + Clone { type Message: Message; fn get_message_receiver(&self) -> broadcast::Receiver; - fn get_reaction_receiver(&self) -> broadcast::Receiver<(String, ReactionOperation)>; + fn get_reaction_receiver(&self) -> broadcast::Receiver; fn send_message(&self, content: String, nonce: String) -> Self::Message; } diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index dc51193..31eeaa3 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,5 +1,7 @@ use std::fmt::Debug; -use gpui::Rgba; +use gpui::{IntoElement, Rgba}; + +pub type ReactionEvent = (String, ReactionOperation); #[derive(Copy, Clone, Debug)] pub enum MessageReactionType { @@ -13,7 +15,7 @@ pub enum ReactionEmoji { Custom { url: String, animated: bool, name: Option }, } -pub trait MessageReaction: Debug { +pub trait MessageReaction: IntoElement { fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; fn get_emoji(&self) -> ReactionEmoji; @@ -31,7 +33,7 @@ pub enum ReactionOperation { RemoveEmoji(ReactionEmoji), } -pub trait ReactionList { +pub trait ReactionList: IntoElement { fn get_reactions(&self) -> &Vec; fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction>; fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize); diff --git a/src/discord/Cargo.toml b/src/discord/Cargo.toml index 116c759..074107c 100644 --- a/src/discord/Cargo.toml +++ b/src/discord/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] gpui.workspace = true +components.workspace = true scope-chat = { version = "0.1.0", path = "../chat" } serenity = { git = "https://github.com/scopeclient/serenity", version = "0.12" } tokio = "1.41.1" diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index 9eea7cd..fbf306d 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -6,7 +6,7 @@ use crate::{ snowflake::Snowflake, }; use scope_backend_cache::async_list::{refcacheslice::Exists, AsyncListCache}; -use scope_chat::reaction::ReactionOperation; +use scope_chat::reaction::ReactionEvent; use scope_chat::{ async_list::{AsyncList, AsyncListIndex, AsyncListItem, AsyncListResult}, channel::Channel, @@ -17,7 +17,7 @@ use tokio::sync::{broadcast, Mutex, Semaphore}; pub struct DiscordChannel { channel_id: Snowflake, message_receiver: broadcast::Receiver, - reaction_receiver: broadcast::Receiver<(String, ReactionOperation)>, + reaction_receiver: broadcast::Receiver, client: Arc, cache: Arc>>, blocker: Semaphore, @@ -49,7 +49,7 @@ impl Channel for DiscordChannel { self.message_receiver.resubscribe() } - fn get_reaction_receiver(&self) -> broadcast::Receiver<(String, ReactionOperation)> { + fn get_reaction_receiver(&self) -> broadcast::Receiver { self.reaction_receiver.resubscribe() } diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index c09cf2d..9930c19 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -11,7 +11,7 @@ use crate::{ }, snowflake::Snowflake, }; -use scope_chat::reaction::{MessageReactionType, ReactionOperation}; +use scope_chat::reaction::{MessageReactionType, ReactionEvent, ReactionOperation}; use serenity::all::Reaction; use serenity::{ all::{Cache, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, Http, Message, MessageId, Ready}, @@ -31,7 +31,7 @@ struct SerenityClient { #[derive(Default)] pub struct DiscordClient { channel_message_event_handlers: RwLock>>>, - channel_reaction_event_handlers: RwLock>>>, + channel_reaction_event_handlers: RwLock>>>, client: OnceLock, user: OnceLock, channels: RwLock>>, @@ -70,7 +70,7 @@ impl DiscordClient { self.channel_message_event_handlers.write().await.entry(channel).or_default().push(sender); } - pub async fn add_channel_reaction_sender(&self, channel: Snowflake, sender: broadcast::Sender<(String, ReactionOperation)>) { + pub async fn add_channel_reaction_sender(&self, channel: Snowflake, sender: broadcast::Sender) { self.channel_reaction_event_handlers.write().await.entry(channel).or_default().push(sender); } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 3c0ef14..607561d 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -3,7 +3,7 @@ use crate::snowflake::Snowflake; use author::{DiscordMessageAuthor, DisplayName}; use chrono::{DateTime, Utc}; use content::DiscordMessageContent; -use gpui::{Element, IntoElement}; +use gpui::{div, Element, IntoElement, ParentElement}; use scope_chat::message::MessageAuthor; use scope_chat::reaction::ReactionList; use scope_chat::{async_list::AsyncListItem, message::Message}; @@ -26,7 +26,7 @@ pub struct DiscordMessage { impl DiscordMessage { pub fn from_serenity(msg: &serenity::all::Message) -> Self { - let reactions = msg.reactions.iter().map(|r| reaction::DiscordMessageReaction::from_message(r)).collect::>(); + let reactions = msg.reactions.iter().map(reaction::DiscordMessageReaction::from_message).collect::>(); if !reactions.is_empty() { println!("Reactions: {:?}", reactions); } @@ -57,7 +57,9 @@ impl Message for DiscordMessage { } fn get_content(&self) -> impl Element { - self.content.clone().into_element() + div() + .child(self.content.clone().into_element()) + .child(self.reactions.clone()) } fn get_identifier(&self) -> String { diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 0fe7ae9..619158c 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,8 +1,10 @@ -use gpui::Rgba; +use components::theme::ActiveTheme; +use gpui::{div, img, px, AnyElement, IntoElement, ParentElement, RenderOnce, Rgba, Styled, WindowContext}; +use gpui::prelude::FluentBuilder; +use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; use serenity::all::ReactionType; use MessageReactionType::Burst; -use scope_chat::reaction::MessageReactionType::Normal; #[derive(Clone, Debug)] pub enum ReactionData { @@ -32,7 +34,7 @@ impl ReactionData { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, IntoElement)] pub struct DiscordMessageReaction { pub data: ReactionData, } @@ -84,7 +86,7 @@ impl MessageReaction for DiscordMessageReaction { None } } - ReactionData::Local { me, .. } => me.clone(), + ReactionData::Local { me, .. } => *me, } } @@ -138,6 +140,30 @@ impl MessageReaction for DiscordMessageReaction { } } +impl DiscordMessageReaction { + fn render_emoji(emoji: &ReactionEmoji) -> AnyElement { + match emoji { + ReactionEmoji::Simple(character) => div().text_size(px(12f32)).child(character.clone()).into_any_element(), + ReactionEmoji::Custom { url, .. } => img(url.clone()).w(px(16f32)).h(px(16f32)).into_any_element(), + } + } +} + +impl RenderOnce for DiscordMessageReaction { + fn render(self, cx: &mut WindowContext) -> impl IntoElement { + let emoji = self.get_emoji(); + let theme = cx.theme(); + div() + .p_1() + .border_1() + .border_color(theme.border) + .when(self.get_self_reaction().is_some(), |s| s.border_color(theme.accent)) + .bg(theme.panel) + .rounded_md() + .child(Self::render_emoji(&emoji)) + } +} + pub fn discord_reaction_to_emoji(reaction: &serenity::all::ReactionType) -> ReactionEmoji { match reaction { ReactionType::Custom { animated, id, name } => ReactionEmoji::Custom { @@ -151,4 +177,4 @@ pub fn discord_reaction_to_emoji(reaction: &serenity::all::ReactionType) -> Reac ReactionEmoji::Simple("❓".to_string()) } } -} \ No newline at end of file +} diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 3a4e456..2bb7659 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -1,8 +1,9 @@ use crate::message::reaction::{DiscordMessageReaction, ReactionData}; +use gpui::{div, IntoElement, ParentElement, RenderOnce, Styled, WindowContext}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, IntoElement)] pub struct DiscordReactionList { reactions: Vec, } @@ -67,3 +68,16 @@ impl ReactionList for DiscordReactionList { } } } + +impl RenderOnce for DiscordReactionList { + fn render(self, _cx: &mut WindowContext) -> impl IntoElement { + if self.reactions.is_empty() { + return div(); + } + + div() + .flex() + .gap_2() + .children(self.reactions) + } +} diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index 12ac184..ffefff7 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -7,7 +7,7 @@ use scope_chat::{ message::Message, }; use tokio::sync::RwLock; -use scope_chat::reaction::{ReactionList, ReactionOperation}; +use scope_chat::reaction::{ReactionEvent, ReactionList}; use super::message::{message, MessageGroup}; #[derive(Clone, Copy)] @@ -115,7 +115,7 @@ where }); } - pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: (String, ReactionOperation)) { + pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: ReactionEvent) { self.cache.update(cx, |borrow: &mut Vec>>, cx| { for item in borrow.iter_mut() { if let Element::Resolved(Some(haystack)) = item { From 4ce6ef0c365b62c552c8eb3cb40f47b3e9178e26 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Fri, 29 Nov 2024 16:42:50 +0100 Subject: [PATCH 06/18] feat: show reaction count in the UI --- src/discord/src/message/reaction.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 619158c..d212c72 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -21,14 +21,14 @@ pub enum ReactionData { impl ReactionData { fn count(&self) -> u64 { match self { - ReactionData::Message(reaction) => reaction.count, + ReactionData::Message(reaction) => reaction.count_details.normal, ReactionData::Local { count_normal, .. } => *count_normal, } } fn count_burst(&self) -> u64 { match self { - ReactionData::Message(reaction) => reaction.count, + ReactionData::Message(reaction) => reaction.count_details.burst, ReactionData::Local { count_burst, .. } => *count_burst, } } @@ -154,13 +154,19 @@ impl RenderOnce for DiscordMessageReaction { let emoji = self.get_emoji(); let theme = cx.theme(); div() - .p_1() + .px_1() + .py_px() .border_1() .border_color(theme.border) .when(self.get_self_reaction().is_some(), |s| s.border_color(theme.accent)) .bg(theme.panel) .rounded_md() + .flex() + .justify_center() + .items_center() + .gap_1() .child(Self::render_emoji(&emoji)) + .child(self.get_count(None).to_string()) } } From 8265f2d4f0da34c4ac6bafc0f6aaf058d6b15a23 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Sun, 1 Dec 2024 20:35:35 +0100 Subject: [PATCH 07/18] wip: handle reaction click --- src/chat/src/reaction.rs | 13 +++++++++++-- src/discord/src/message/reaction.rs | 7 ++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index 31eeaa3..e69cf07 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use gpui::{IntoElement, Rgba}; pub type ReactionEvent = (String, ReactionOperation); @@ -9,12 +9,21 @@ pub enum MessageReactionType { Burst, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, PartialEq)] pub enum ReactionEmoji { Simple(String), Custom { url: String, animated: bool, name: Option }, } +impl Debug for ReactionEmoji { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ReactionEmoji::Simple(s) => write!(f, "{}", s), + ReactionEmoji::Custom { name, .. } => write!(f, ":{}:", name.clone().unwrap_or("".to_string())), + } + } +} + pub trait MessageReaction: IntoElement { fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index d212c72..5557a00 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,5 +1,5 @@ use components::theme::ActiveTheme; -use gpui::{div, img, px, AnyElement, IntoElement, ParentElement, RenderOnce, Rgba, Styled, WindowContext}; +use gpui::{div, img, px, AnyElement, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, StatefulInteractiveElement, Styled, WindowContext}; use gpui::prelude::FluentBuilder; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; @@ -152,6 +152,7 @@ impl DiscordMessageReaction { impl RenderOnce for DiscordMessageReaction { fn render(self, cx: &mut WindowContext) -> impl IntoElement { let emoji = self.get_emoji(); + let async_emoji = emoji.clone(); let theme = cx.theme(); div() .px_1() @@ -162,6 +163,10 @@ impl RenderOnce for DiscordMessageReaction { .bg(theme.panel) .rounded_md() .flex() + .id("reaction") + .on_click(move |_, _| { + eprintln!("Reaction {:?} clicked", async_emoji); + }) .justify_center() .items_center() .gap_1() From 97f0cae71df935dadac37eb72a3b57c19bddff01 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Sun, 1 Dec 2024 20:59:44 +0100 Subject: [PATCH 08/18] fix: cleanup merge (-shown reactions) --- src/chat/src/channel.rs | 13 ++++--- src/chat/src/reaction.rs | 2 +- src/discord/src/channel/mod.rs | 11 ++---- src/discord/src/client.rs | 45 ++++++++---------------- src/discord/src/message/mod.rs | 15 +++----- src/discord/src/message/reaction_list.rs | 8 +++++ src/discord/src/snowflake.rs | 5 --- src/ui/src/channel/message_list.rs | 4 +-- src/ui/src/channel/mod.rs | 2 +- 9 files changed, 42 insertions(+), 63 deletions(-) diff --git a/src/chat/src/channel.rs b/src/chat/src/channel.rs index f39604b..2992c43 100644 --- a/src/chat/src/channel.rs +++ b/src/chat/src/channel.rs @@ -1,16 +1,17 @@ use std::{fmt::Debug, sync::Arc}; use tokio::sync::broadcast; - +use tokio::sync::broadcast::Receiver; use crate::reaction::ReactionEvent; use crate::{async_list::AsyncList, message::Message}; + pub trait Channel: AsyncList + Send + Sync + Clone { type Message: Message; type Identifier: Sized + Copy + Clone + Debug + Eq + PartialEq; fn get_message_receiver(&self) -> broadcast::Receiver; - fn get_reaction_receiver(&self) -> broadcast::Receiver; + fn get_reaction_receiver(&self) -> broadcast::Receiver>; fn send_message(&self, content: String, nonce: String) -> Self::Message; @@ -25,8 +26,12 @@ impl Channel for Arc { (**self).get_identifier() } - fn get_receiver(&self) -> broadcast::Receiver { - (**self).get_receiver() + fn get_message_receiver(&self) -> Receiver { + (**self).get_message_receiver() + } + + fn get_reaction_receiver(&self) -> Receiver> { + (**self).get_reaction_receiver() } fn send_message(&self, content: String, nonce: String) -> Self::Message { diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index e69cf07..1f4240f 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,7 +1,7 @@ use std::fmt::{Debug, Formatter}; use gpui::{IntoElement, Rgba}; -pub type ReactionEvent = (String, ReactionOperation); +pub type ReactionEvent = (T, ReactionOperation); #[derive(Copy, Clone, Debug)] pub enum MessageReactionType { diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index 6d5665e..0bd14cd 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -1,10 +1,5 @@ use std::sync::{Arc, OnceLock}; -use crate::{ - client::DiscordClient, - message::{content::DiscordMessageContent, DiscordMessage}, - snowflake::Snowflake, -}; use chrono::Utc; use scope_backend_cache::async_list::{refcacheslice::Exists, AsyncListCache}; use scope_chat::reaction::ReactionEvent; @@ -24,7 +19,7 @@ use crate::{ pub struct DiscordChannel { channel: Arc, message_receiver: broadcast::Receiver, - reaction_receiver: broadcast::Receiver, + reaction_receiver: broadcast::Receiver>, client: Arc, cache: Arc>>, blocker: Semaphore, @@ -58,7 +53,7 @@ impl Channel for DiscordChannel { self.message_receiver.resubscribe() } - fn get_reaction_receiver(&self) -> broadcast::Receiver { + fn get_reaction_receiver(&self) -> broadcast::Receiver> { self.reaction_receiver.resubscribe() } @@ -82,7 +77,7 @@ impl Channel for DiscordChannel { list_item_id: Snowflake::random(), }, content: OnceLock::new(), - reactions: Default::default() + reactions: Default::default(), } } diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index c0da15c..0c6d3dc 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -3,24 +3,11 @@ use std::{ sync::{Arc, OnceLock, Weak}, }; -use crate::{ - channel::DiscordChannel, - message::{ - author::{DiscordMessageAuthor, DisplayName}, - DiscordMessage, - }, - snowflake::Snowflake, -}; -use scope_chat::reaction::{MessageReactionType, ReactionEvent, ReactionOperation}; -use serenity::all::Reaction; -use serenity::{ - all::{Cache, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, Http, Message, MessageId, Ready}, - async_trait, -}; -use tokio::sync::{broadcast, RwLock}; use crate::message::reaction::discord_reaction_to_emoji; use atomic_refcell::AtomicRefCell; use dashmap::DashMap; +use scope_chat::reaction::{MessageReactionType, ReactionEvent, ReactionOperation}; +use serenity::all::Reaction; use serenity::{ all::{ Cache, CacheHttp, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, GuildId, Http, Member, Message, MessageId, @@ -53,7 +40,7 @@ impl CacheHttp for SerenityClient { #[derive(Default)] pub struct DiscordClient { channel_message_event_handlers: RwLock>>>, - channel_reaction_event_handlers: RwLock>>>, + channel_reaction_event_handlers: RwLock>>>>, client: OnceLock, user: OnceLock>, channels: RwLock>>, @@ -108,7 +95,7 @@ impl DiscordClient { self.channel_message_event_handlers.write().await.entry(channel).or_default().push(sender); } - pub async fn add_channel_reaction_sender(&self, channel: Snowflake, sender: broadcast::Sender) { + pub async fn add_channel_reaction_sender(&self, channel: ChannelId, sender: broadcast::Sender>) { self.channel_reaction_event_handlers.write().await.entry(channel).or_default().push(sender); } @@ -152,10 +139,10 @@ impl DiscordClient { Some(channel_id.message(self.discord().http.clone(), message_id).await.unwrap()) } - async fn send_reaction_operation(&self, channel_id: Snowflake, message_id: MessageId, operation: ReactionOperation) { + async fn send_reaction_operation(&self, channel_id: ChannelId, message_id: MessageId, operation: ReactionOperation) { if let Some(vec) = self.channel_reaction_event_handlers.read().await.get(&channel_id) { for sender in vec { - let _ = sender.send((message_id.to_string(), operation.clone())); + let _ = sender.send((message_id.into(), operation.clone())); } } } @@ -194,8 +181,6 @@ impl EventHandler for DiscordClient { } async fn reaction_add(&self, _: Context, reaction: Reaction) { - let channel_snowflake = reaction.channel_id.into(); - let ty = if reaction.burst { MessageReactionType::Burst } else { @@ -203,38 +188,36 @@ impl EventHandler for DiscordClient { }; let emoji = discord_reaction_to_emoji(&reaction.emoji); + let me_id = self.user.get().expect("User not ready").id; - let operation = if reaction.member.is_none_or(|member| member.user.id.get() == self.user().id.parse::().unwrap()) { + let operation = if reaction.member.is_none_or(|member| member.user.id == me_id) { ReactionOperation::AddSelf(emoji, ty) } else { ReactionOperation::Add(emoji, ty) }; - self.send_reaction_operation(channel_snowflake, reaction.message_id, operation).await; + self.send_reaction_operation(reaction.channel_id, reaction.message_id, operation).await; } async fn reaction_remove(&self, _: Context, reaction: Reaction) { - let channel_snowflake = reaction.channel_id.into(); - let emoji = discord_reaction_to_emoji(&reaction.emoji); + let me_id = self.user.get().expect("User not ready").id; - let operation = if reaction.member.is_none_or(|member| member.user.id.get() == self.user().id.parse::().unwrap()) { + let operation = if reaction.member.is_none_or(|member| member.user.id == me_id) { ReactionOperation::RemoveSelf(emoji) } else { ReactionOperation::Remove(emoji) }; - self.send_reaction_operation(channel_snowflake, reaction.message_id, operation).await; + self.send_reaction_operation(reaction.channel_id, reaction.message_id, operation).await; } async fn reaction_remove_all(&self, _: Context, channel_id: ChannelId, removed_from_message_id: MessageId) { - let channel_snowflake = channel_id.into(); - self.send_reaction_operation(channel_snowflake, removed_from_message_id, ReactionOperation::RemoveAll).await; + self.send_reaction_operation(channel_id, removed_from_message_id, ReactionOperation::RemoveAll).await; } async fn reaction_remove_emoji(&self, _: Context, removed_reactions: Reaction) { - let channel_snowflake = removed_reactions.channel_id.into(); let emoji = discord_reaction_to_emoji(&removed_reactions.emoji); - self.send_reaction_operation(channel_snowflake, removed_reactions.message_id, ReactionOperation::RemoveEmoji(emoji)).await; + self.send_reaction_operation(removed_reactions.channel_id, removed_reactions.message_id, ReactionOperation::RemoveEmoji(emoji)).await; } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 737d5b1..4cc3ab7 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -1,12 +1,10 @@ use std::sync::{Arc, OnceLock}; use crate::message::reaction_list::DiscordReactionList; -use author::{DiscordMessageAuthor, DisplayName}; +use author::DiscordMessageAuthor; use chrono::{DateTime, Utc}; use content::DiscordMessageContent; use gpui::{View, VisualContext, WindowContext}; -use gpui::{div, Element, IntoElement, ParentElement}; -use scope_chat::message::MessageAuthor; use scope_chat::reaction::ReactionList; use scope_chat::{async_list::AsyncListItem, message::Message}; use serenity::all::{ModelError, Nonce}; @@ -35,7 +33,7 @@ pub struct DiscordMessage { pub channel: Arc, pub data: DiscordMessageData, pub content: OnceLock>, - pub reactions: Option, + pub reactions: DiscordReactionList, } impl DiscordMessage { @@ -48,16 +46,11 @@ impl DiscordMessage { } .unwrap(); - let reactions = msg.reactions.iter().map(reaction::DiscordMessageReaction::from_message).collect::>(); - if !reactions.is_empty() { - println!("Reactions: {:?}", reactions); - } - Self { client, channel, + reactions: (&msg.reactions).into(), data: DiscordMessageData::Received(msg, member), - content: OnceLock::new(), } } @@ -71,8 +64,8 @@ impl DiscordMessage { Self { client, channel, + reactions: (&msg.reactions).into(), data: DiscordMessageData::Received(msg, member), - content: OnceLock::new(), } } diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 2bb7659..b486e4c 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -14,6 +14,14 @@ impl DiscordReactionList { } } +impl From<&Vec> for DiscordReactionList { + fn from(reactions: &Vec) -> Self { + DiscordReactionList { + reactions: reactions.iter().map(DiscordMessageReaction::from_message).collect(), + } + } +} + impl ReactionList for DiscordReactionList { fn get_reactions(&self) -> &Vec { &self.reactions diff --git a/src/discord/src/snowflake.rs b/src/discord/src/snowflake.rs index 9c72080..dbbe57b 100644 --- a/src/discord/src/snowflake.rs +++ b/src/discord/src/snowflake.rs @@ -40,8 +40,3 @@ impl From for Snowflake { } } -impl From for Snowflake where T: Into { - fn from(content: T) -> Self { - Snowflake { content: content.into() } - } -} diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index 296f6cd..eac29be 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -116,11 +116,11 @@ where }); } - pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: ReactionEvent) { + pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: ReactionEvent) { self.cache.update(cx, |borrow: &mut Vec>>, cx| { for item in borrow.iter_mut() { if let Element::Resolved(Some(haystack)) = item { - if haystack.get_identifier() == reaction.0 { + if haystack.get_identifier() == Some(reaction.0) { let reactions = haystack.get_reactions(); reactions.apply(reaction.1); diff --git a/src/ui/src/channel/mod.rs b/src/ui/src/channel/mod.rs index c6cfbf1..e08bef5 100644 --- a/src/ui/src/channel/mod.rs +++ b/src/ui/src/channel/mod.rs @@ -14,7 +14,7 @@ pub struct ChannelView { } impl ChannelView { - pub fn create(ctx: &mut gpui::ViewContext<'_, ChannelView>, channel: Arc) -> Self { + pub fn create(ctx: &mut gpui::ViewContext<'_, ChannelView>, channel: Arc) -> Self where ::Identifier: Send { let channel_message_listener = channel.get_message_receiver(); let channel_reaction_listener = channel.get_reaction_receiver(); From 113d9c697d7bad07f24fa62d328ede0d6f59d460 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Mon, 2 Dec 2024 18:41:18 +0100 Subject: [PATCH 09/18] fix: show reactions again --- src/discord/src/channel/mod.rs | 2 +- src/discord/src/message/content.rs | 16 +++++++++++++--- src/discord/src/message/mod.rs | 30 +++++++++++++++++------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index 0bd14cd..a5f8155 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -75,9 +75,9 @@ impl Channel for DiscordChannel { content, sent_time: Utc::now(), list_item_id: Snowflake::random(), + reactions: Default::default(), }, content: OnceLock::new(), - reactions: Default::default(), } } diff --git a/src/discord/src/message/content.rs b/src/discord/src/message/content.rs index 07df573..5ce485f 100644 --- a/src/discord/src/message/content.rs +++ b/src/discord/src/message/content.rs @@ -1,3 +1,4 @@ +use crate::message::reaction_list::DiscordReactionList; use gpui::{div, IntoElement, ParentElement, Render, Styled, ViewContext}; use serenity::all::Message; @@ -5,23 +6,32 @@ use serenity::all::Message; pub struct DiscordMessageContent { pub content: String, pub is_pending: bool, + pub reactions: DiscordReactionList, } impl DiscordMessageContent { pub fn pending(content: String) -> DiscordMessageContent { - DiscordMessageContent { content, is_pending: true } + DiscordMessageContent { + content, + is_pending: true, + reactions: Default::default(), + } } - pub fn received(message: &Message) -> DiscordMessageContent { + pub fn received(message: &Message, reactions: &DiscordReactionList) -> DiscordMessageContent { DiscordMessageContent { content: message.content.clone(), is_pending: false, + reactions: reactions.clone(), } } } impl Render for DiscordMessageContent { fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { - div().opacity(if self.is_pending { 0.25 } else { 1.0 }).child(self.content.clone()) + div() + .opacity(if self.is_pending { 0.25 } else { 1.0 }) + .child(self.content.clone()) + .child(self.reactions.clone()) } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 4cc3ab7..4c5b131 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -23,8 +23,9 @@ pub enum DiscordMessageData { content: String, sent_time: DateTime, list_item_id: Snowflake, + reactions: DiscordReactionList }, - Received(Arc, Option>), + Received(Arc, Option>, DiscordReactionList), } #[derive(Clone)] @@ -33,7 +34,6 @@ pub struct DiscordMessage { pub channel: Arc, pub data: DiscordMessageData, pub content: OnceLock>, - pub reactions: DiscordReactionList, } impl DiscordMessage { @@ -46,11 +46,12 @@ impl DiscordMessage { } .unwrap(); + let reactions = (&msg.reactions).into(); + Self { client, channel, - reactions: (&msg.reactions).into(), - data: DiscordMessageData::Received(msg, member), + data: DiscordMessageData::Received(msg, member, reactions), content: OnceLock::new(), } } @@ -61,11 +62,11 @@ impl DiscordMessage { channel: Arc, member: Option>, ) -> Self { + let reactions = (&msg.reactions).into(); Self { client, channel, - reactions: (&msg.reactions).into(), - data: DiscordMessageData::Received(msg, member), + data: DiscordMessageData::Received(msg, member, reactions), content: OnceLock::new(), } } @@ -118,7 +119,7 @@ impl Message for DiscordMessage { }, }, - DiscordMessageData::Received(message, member) => DiscordMessageAuthor { + DiscordMessageData::Received(message, member, ..) => DiscordMessageAuthor { client: self.client.clone(), data: match member { None => author::DiscordMessageAuthorData::NonMemberAuthor(message.clone()), @@ -135,7 +136,7 @@ impl Message for DiscordMessage { .get_or_init(|| { let content = match &self.data { DiscordMessageData::Pending { content, .. } => DiscordMessageContent::pending(content.clone()), - DiscordMessageData::Received(message, _) => DiscordMessageContent::received(message), + DiscordMessageData::Received(message, _, reactions) => DiscordMessageContent::received(message, reactions), }; cx.new_view(|_cx| content) @@ -145,7 +146,7 @@ impl Message for DiscordMessage { fn get_identifier(&self) -> Option { match &self.data { - DiscordMessageData::Received(message, _) => Some(message.id.into()), + DiscordMessageData::Received(message, ..) => Some(message.id.into()), DiscordMessageData::Pending { .. } => None, } } @@ -153,7 +154,7 @@ impl Message for DiscordMessage { fn get_nonce(&self) -> impl PartialEq { match &self.data { DiscordMessageData::Pending { nonce, .. } => NonceState::Fixed(nonce), - DiscordMessageData::Received(message, _) => NonceState::Discord(&message.nonce), + DiscordMessageData::Received(message, ..) => NonceState::Discord(&message.nonce), } } @@ -169,12 +170,15 @@ impl Message for DiscordMessage { fn get_timestamp(&self) -> Option> { match &self.data { DiscordMessageData::Pending { sent_time, .. } => Some(*sent_time), - DiscordMessageData::Received(message, _) => DateTime::from_timestamp_millis(message.timestamp.timestamp_millis()), + DiscordMessageData::Received(message, ..) => DateTime::from_timestamp_millis(message.timestamp.timestamp_millis()), } } fn get_reactions(&mut self) -> &mut impl ReactionList { - &mut self.reactions + match &mut self.data { + DiscordMessageData::Pending { reactions, .. } => reactions, + DiscordMessageData::Received(_, _, reactions) => reactions, + } } } @@ -184,7 +188,7 @@ impl AsyncListItem for DiscordMessage { fn get_list_identifier(&self) -> Self::Identifier { match &self.data { DiscordMessageData::Pending { list_item_id, .. } => *list_item_id, - DiscordMessageData::Received(message, _) => message.id.into(), + DiscordMessageData::Received(message, ..) => message.id.into(), } } } From e8b3cea62bc769467e0d98b63ef00818815034fc Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Tue, 28 Jan 2025 23:21:47 +0100 Subject: [PATCH 10/18] feat: update GPUI and refactor to make it happen --- Cargo.lock | 1331 +++++++++++++--------- src/chat/src/message.rs | 6 +- src/chat/src/reaction.rs | 3 +- src/discord/src/channel/mod.rs | 1 - src/discord/src/client.rs | 17 +- src/discord/src/message/author.rs | 6 +- src/discord/src/message/content.rs | 17 +- src/discord/src/message/mod.rs | 19 +- src/discord/src/message/reaction.rs | 58 +- src/discord/src/message/reaction_list.rs | 45 +- src/ui/src/app.rs | 22 +- src/ui/src/app_state.rs | 4 +- src/ui/src/channel/message.rs | 4 +- src/ui/src/channel/message_list.rs | 37 +- src/ui/src/channel/mod.rs | 67 +- src/ui/src/main.rs | 14 +- 16 files changed, 975 insertions(+), 676 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d28f10..22d804a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,19 +113,20 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -147,7 +148,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -193,9 +194,9 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" dependencies = [ "async-fs 2.1.2", "async-net 2.0.0", @@ -206,16 +207,16 @@ dependencies = [ "serde", "serde_repr", "url", - "zbus", + "zbus 5.3.1", ] [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -252,8 +253,8 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.2.0", - "futures-lite 2.5.0", + "fastrand 2.3.0", + "futures-lite 2.6.0", "slab", ] @@ -277,7 +278,7 @@ checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ "async-lock 3.4.0", "blocking", - "futures-lite 2.5.0", + "futures-lite 2.6.0", ] [[package]] @@ -294,7 +295,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2 0.4.10", "waker-fn", @@ -310,10 +311,10 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "parking", "polling 3.7.4", - "rustix 0.38.40", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -334,7 +335,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -358,7 +359,7 @@ checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io 2.4.0", "blocking", - "futures-lite 2.5.0", + "futures-lite 2.6.0", ] [[package]] @@ -374,7 +375,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.40", + "rustix 0.38.44", "windows-sys 0.48.0", ] @@ -391,9 +392,9 @@ dependencies = [ "async-task", "blocking", "cfg-if", - "event-listener 5.3.1", - "futures-lite 2.5.0", - "rustix 0.38.40", + "event-listener 5.4.0", + "futures-lite 2.6.0", + "rustix 0.38.44", "tracing", ] @@ -405,7 +406,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -420,7 +421,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.40", + "rustix 0.38.44", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -434,20 +435,20 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "atk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", "glib", @@ -456,9 +457,9 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", @@ -530,9 +531,12 @@ dependencies = [ [[package]] name = "base62" -version = "2.0.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fa474cf7492f9a299ba6019fb99ec673e1739556d48e8a90eabaea282ef0e4" +checksum = "10e52a7bcb1d6beebee21fb5053af9e3cbb7a7ed1a4909e534040e676437ab1f" +dependencies = [ + "rustversion", +] [[package]] name = "base64" @@ -552,7 +556,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -563,23 +567,23 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "bit-set" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bit_field" @@ -595,9 +599,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitstream-io" @@ -607,16 +611,14 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "blade-graphics" -version = "0.5.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +version = "0.6.0" +source = "git+https://github.com/scopeclient/blade?branch=gpui-stable#091a8401033847bb9b6ace3fcf70448d069621c5" dependencies = [ "ash", "ash-window", - "bitflags 2.6.0", - "block", + "bitflags 2.8.0", "bytemuck", "codespan-reporting", - "core-graphics-types 0.1.3", "glow", "gpu-alloc", "gpu-alloc-ash", @@ -625,10 +627,14 @@ dependencies = [ "khronos-egl", "libloading", "log", - "metal", "mint", "naga", - "objc", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "objc2-ui-kit", "raw-window-handle", "slab", "wasm-bindgen", @@ -638,17 +644,17 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +source = "git+https://github.com/scopeclient/blade?branch=gpui-stable#091a8401033847bb9b6ace3fcf70448d069621c5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "blade-util" -version = "0.1.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +version = "0.2.0" +source = "git+https://github.com/scopeclient/blade?branch=gpui-stable#091a8401033847bb9b6ace3fcf70448d069621c5" dependencies = [ "blade-graphics", "bytemuck", @@ -698,15 +704,15 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "piper", ] [[package]] name = "bstr" -version = "1.11.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "serde", @@ -720,9 +726,9 @@ checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytecount" @@ -732,22 +738,22 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -764,9 +770,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cairo-rs" @@ -774,7 +780,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cairo-sys-rs", "glib", "libc", @@ -799,10 +805,10 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "log", "polling 3.7.4", - "rustix 0.38.40", + "rustix 0.38.44", "slab", "thiserror 1.0.69", ] @@ -814,7 +820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ "calloop", - "rustix 0.38.40", + "rustix 0.38.44", "wayland-backend", "wayland-client", ] @@ -830,9 +836,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -870,27 +876,27 @@ dependencies = [ [[package]] name = "cbindgen" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" +checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" dependencies = [ "heck 0.4.1", - "indexmap 2.6.0", + "indexmap 2.7.1", "log", "proc-macro2", "quote", "serde", "serde_json", - "syn 2.0.87", + "syn 2.0.96", "tempfile", "toml 0.8.19", ] [[package]] name = "cc" -version = "1.2.1" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -942,9 +948,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -999,7 +1005,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "cocoa-foundation 0.2.0", "core-foundation 0.10.0", @@ -1029,7 +1035,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "core-foundation 0.10.0", "core-graphics-types 0.2.0", @@ -1050,9 +1056,10 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ - "rustc-hash 1.1.0", + "indexmap 2.7.1", + "rustc-hash 2.1.0", ] [[package]] @@ -1080,7 +1087,7 @@ dependencies = [ [[package]] name = "command_attr" version = "0.5.3" -source = "git+https://github.com/scopeclient/serenity#a24c41552b3bab4e8fc391efed29fa8c3e1c164d" +source = "git+https://github.com/scopeclient/serenity#8aa434be893b121029d03fcaa267a980bb77139c" dependencies = [ "proc-macro2", "quote", @@ -1177,7 +1184,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types", @@ -1201,7 +1208,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.10.0", "libc", ] @@ -1232,7 +1239,7 @@ name = "cosmic-text" version = "0.11.2" source = "git+https://github.com/pop-os/cosmic-text?rev=542b20c#542b20ca4376a3b5de5fa629db1a4ace44e18e0c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "fontdb 0.18.0", "log", "rangemap", @@ -1251,9 +1258,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1269,18 +1276,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1297,24 +1304,24 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -1351,17 +1358,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1394,9 +1401,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" [[package]] name = "data-url" @@ -1424,13 +1431,13 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "proc-macro2", "quote", @@ -1497,7 +1504,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1574,9 +1581,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "embed-resource" -version = "2.5.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" +checksum = "4762ce03154ba57ebaeee60cc631901ceae4f18219cbb874e464347471594742" dependencies = [ "cc", "memchr", @@ -1603,9 +1610,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", "serde", @@ -1613,20 +1620,20 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -1634,9 +1641,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -1663,12 +1670,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1682,9 +1689,9 @@ dependencies = [ [[package]] name = "etagere" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e2f1e3be19fb10f549be8c1bf013e8675b4066c445e36eb76d2ebb2f54ee495" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" dependencies = [ "euclid", "svg_fmt", @@ -1718,9 +1725,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1729,11 +1736,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] @@ -1763,15 +1770,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1819,6 +1826,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "flume" version = "0.11.1" @@ -1842,7 +1855,7 @@ name = "font-kit" version = "0.14.1" source = "git+https://github.com/zed-industries/font-kit?rev=40391b7#40391b7c0041d8a8572af2afa3de32ae088f0120" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "core-foundation 0.9.4", "core-graphics 0.23.2", @@ -1924,7 +1937,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2028,11 +2041,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -2047,7 +2060,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2091,9 +2104,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -2132,9 +2145,9 @@ dependencies = [ [[package]] name = "gdk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -2149,9 +2162,9 @@ dependencies = [ [[package]] name = "gdkx11" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ "gdk", "gdkx11-sys", @@ -2163,9 +2176,9 @@ dependencies = [ [[package]] name = "gdkx11-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ "gdk-sys", "glib-sys", @@ -2272,7 +2285,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "futures-channel", "futures-core", "futures-executor", @@ -2300,7 +2313,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2315,9 +2328,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" @@ -2372,7 +2385,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "gpu-alloc-types", ] @@ -2393,13 +2406,13 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2433,9 +2446,10 @@ dependencies = [ "gpui_macros", "http_client", "image", - "itertools 0.13.0", + "itertools 0.14.0", "linkme", "log", + "lyon", "media", "metal", "num_cpus", @@ -2459,7 +2473,7 @@ dependencies = [ "serde_json", "slotmap", "smallvec", - "smol", + "smol 2.0.2", "strum", "sum_tree", "taffy", @@ -2484,7 +2498,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "proc-macro2", "quote", @@ -2499,9 +2513,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" [[package]] name = "gtk" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", "cairo-rs", @@ -2520,9 +2534,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -2538,15 +2552,15 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2561,7 +2575,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -2579,8 +2593,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -2611,9 +2625,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -2702,18 +2716,18 @@ checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", - "itoa 1.0.11", + "itoa 1.0.14", ] [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", - "itoa 1.0.11", + "itoa 1.0.14", ] [[package]] @@ -2734,7 +2748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -2745,7 +2759,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2753,13 +2767,13 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bytes", "derive_more", "futures", - "http 1.1.0", + "http 1.2.0", "log", "serde", "serde_json", @@ -2768,9 +2782,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "httpdate" @@ -2786,9 +2800,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -2799,9 +2813,9 @@ dependencies = [ "http-body 0.4.6", "httparse", "httpdate", - "itoa 1.0.11", + "itoa 1.0.14", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -2810,18 +2824,18 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "httparse", - "itoa 1.0.11", + "itoa 1.0.14", "pin-project-lite", "smallvec", "tokio", @@ -2836,7 +2850,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.31", + "hyper 0.14.32", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", @@ -2844,19 +2858,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.5.1", + "http 1.2.0", + "hyper 1.6.0", "hyper-util", - "rustls 0.23.17", + "rustls 0.23.21", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", ] @@ -2869,11 +2883,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.6.0", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.5.8", "tokio", "tower-service", "tracing", @@ -3017,7 +3031,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3082,9 +3096,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" dependencies = [ "byteorder-lite", "quick-error", @@ -3114,12 +3128,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", + "serde", ] [[package]] @@ -3149,7 +3164,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3165,9 +3180,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-docker" @@ -3221,6 +3236,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -3229,9 +3253,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "javascriptcore-rs" @@ -3295,10 +3319,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -3358,15 +3383,15 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libfuzzer-sys" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" dependencies = [ "arbitrary", "cc", @@ -3374,9 +3399,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -3394,7 +3419,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", ] @@ -3421,7 +3446,7 @@ checksum = "edbe595006d355eaf9ae11db92707d4338cd2384d16866131cc1afdbdd35d8d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3432,15 +3457,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -3454,9 +3479,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" dependencies = [ "serde", "value-bag", @@ -3471,6 +3496,58 @@ dependencies = [ "imgref", ] +[[package]] +name = "lyon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13c9be19d257c7d37e70608ed858e8eab4b2afcea2e3c9a622e892acbf43c08" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af69edc087272df438b3ee436c4bb6d7c04aa8af665cfd398feae627dbd8570" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e0b8aec2f58586f6eef237985b9a9b7cb3a3aff4417c575075cf95bf925252e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + [[package]] name = "mac" version = "0.1.1" @@ -3529,11 +3606,12 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bindgen", "core-foundation 0.9.4", + "ctor", "foreign-types", "metal", "objc", @@ -3565,11 +3643,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "core-graphics-types 0.1.3", "foreign-types", @@ -3617,9 +3695,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", "simd-adler32", @@ -3633,11 +3711,10 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -3645,17 +3722,17 @@ dependencies = [ [[package]] name = "naga" -version = "22.1.0" +version = "23.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" +checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", - "indexmap 2.6.0", + "indexmap 2.7.1", "log", "rustc-hash 1.1.0", "spirv", @@ -3679,7 +3756,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "jni-sys", "log", "ndk-sys", @@ -3709,7 +3786,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -3812,7 +3889,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3853,6 +3930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3883,7 +3961,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3920,7 +3998,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -3936,7 +4014,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -3960,7 +4038,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3992,9 +4070,9 @@ dependencies = [ [[package]] name = "objc2-encode" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" @@ -4002,7 +4080,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -4026,7 +4104,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -4038,7 +4116,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -4061,7 +4139,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-cloud-kit", @@ -4093,7 +4171,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -4106,7 +4184,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-app-kit", @@ -4115,9 +4193,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -4131,8 +4209,7 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oo7" version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc6ce4692fbfd044ce22ca07dcab1a30fa12432ca2aa5b1294eca50d3332a24" +source = "git+https://github.com/zed-industries/oo7?branch=avoid-crypto-panic#9d5d5fcd7e4e0add9b420ffb58f67661b0b37568" dependencies = [ "aes", "async-fs 2.1.2", @@ -4144,7 +4221,7 @@ dependencies = [ "cipher", "digest", "endi", - "futures-lite 2.5.0", + "futures-lite 2.6.0", "futures-util", "hkdf", "hmac", @@ -4156,16 +4233,16 @@ dependencies = [ "serde", "sha2", "subtle", - "zbus", + "zbus 4.4.0", "zeroize", - "zvariant", + "zvariant 4.2.0", ] [[package]] name = "open" -version = "5.3.1" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", @@ -4174,9 +4251,9 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "option-ext" @@ -4256,9 +4333,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pathfinder_geometry" @@ -4395,29 +4472,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -4432,7 +4509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.2.0", + "fastrand 2.3.0", "futures-io", ] @@ -4444,9 +4521,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.14" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -4481,7 +4558,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.40", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -4532,12 +4609,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4600,9 +4677,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -4623,7 +4700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4632,7 +4709,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "memchr", "unicase", ] @@ -4677,10 +4754,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.17", - "socket2 0.5.7", - "thiserror 2.0.3", + "rustc-hash 2.1.0", + "rustls 0.23.21", + "socket2 0.5.8", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -4695,11 +4772,11 @@ dependencies = [ "getrandom 0.2.15", "rand 0.8.5", "ring", - "rustc-hash 2.0.0", - "rustls 0.23.17", + "rustc-hash 2.1.0", + "rustls 0.23.21", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -4707,23 +4784,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.5.7", + "socket2 0.5.8", "tracing", "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -4914,9 +4991,9 @@ dependencies = [ [[package]] name = "read-fonts" -version = "0.22.5" +version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a04b892cb6f91951f144c33321843790c8574c825aafdb16d815fd7183b5229" +checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" dependencies = [ "bytemuck", "font-types", @@ -4924,11 +5001,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -4945,7 +5022,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "derive_refineable", ] @@ -4993,7 +5070,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -5034,11 +5111,11 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper 1.6.0", + "hyper-rustls 0.27.5", "hyper-util", "ipnet", "js-sys", @@ -5048,7 +5125,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.17", + "rustls 0.23.21", "rustls-native-certs", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -5058,7 +5135,7 @@ dependencies = [ "sync_wrapper 1.0.2", "system-configuration 0.6.1", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-socks", "tokio-util", "tower-service", @@ -5073,7 +5150,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bytes", @@ -5082,7 +5159,7 @@ dependencies = [ "log", "reqwest 0.12.8", "serde", - "smol", + "smol 2.0.2", "tokio", ] @@ -5150,7 +5227,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.87", + "syn 2.0.96", "walkdir", ] @@ -5193,7 +5270,7 @@ dependencies = [ "serde", "serde_json", "serde_yml", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5233,9 +5310,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -5248,9 +5325,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" dependencies = [ "bitflags 1.3.2", "errno", @@ -5262,15 +5339,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", ] [[package]] @@ -5301,9 +5378,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.17" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "once_cell", "ring", @@ -5315,12 +5392,11 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -5346,9 +5422,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" dependencies = [ "web-time", ] @@ -5376,9 +5452,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rustybuzz" @@ -5386,7 +5462,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "bytemuck", "libm", "smallvec", @@ -5403,7 +5479,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "bytemuck", "core_maths", "log", @@ -5417,9 +5493,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -5446,6 +5522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", + "indexmap 2.7.1", "schemars_derive", "serde", "serde_json", @@ -5460,7 +5537,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5567,12 +5644,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.9.4", + "bitflags 2.8.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -5580,9 +5657,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -5610,14 +5687,14 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "serde", @@ -5625,18 +5702,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -5652,13 +5729,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5669,7 +5746,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5683,12 +5760,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ - "indexmap 2.6.0", - "itoa 1.0.11", + "indexmap 2.7.1", + "itoa 1.0.14", "memchr", "ryu", "serde", @@ -5702,7 +5779,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5721,7 +5798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.11", + "itoa 1.0.14", "ryu", "serde", ] @@ -5732,8 +5809,8 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" dependencies = [ - "indexmap 2.6.0", - "itoa 1.0.11", + "indexmap 2.7.1", + "itoa 1.0.14", "libyml", "log", "memchr", @@ -5746,12 +5823,12 @@ dependencies = [ [[package]] name = "serenity" version = "0.12.4" -source = "git+https://github.com/scopeclient/serenity#a24c41552b3bab4e8fc391efed29fa8c3e1c164d" +source = "git+https://github.com/scopeclient/serenity#8aa434be893b121029d03fcaa267a980bb77139c" dependencies = [ "arrayvec", "async-trait", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "chrono", "command_attr", @@ -5849,9 +5926,9 @@ dependencies = [ [[package]] name = "simplecss" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" dependencies = [ "log", ] @@ -5934,6 +6011,23 @@ dependencies = [ "futures-lite 1.13.0", ] +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-fs 2.1.2", + "async-io 2.4.0", + "async-lock 3.4.0", + "async-net 2.0.0", + "async-process 2.3.0", + "blocking", + "futures-lite 2.6.0", +] + [[package]] name = "socket2" version = "0.4.10" @@ -5946,9 +6040,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5995,7 +6089,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -6047,24 +6141,24 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6076,7 +6170,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "arrayvec", "log", @@ -6085,15 +6179,15 @@ dependencies = [ [[package]] name = "sval" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" +checksum = "d4c2f18f53c889ec3dfe1c08b20fd51406d09b14bf18b366416718763ccff05a" [[package]] name = "sval_buffer" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +checksum = "4b8cb1bb48d0bed828b908e6b99e7ab8c7244994dc27948a2e31d42e8c4d77c1" dependencies = [ "sval", "sval_ref", @@ -6101,40 +6195,40 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +checksum = "ba574872d4ad653071a9db76c49656082db83a37cd5f559874273d36b4a02b9d" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +checksum = "944450b2dbbf8aae98537776b399b23d72b19243ee42522cfd110305f3c9ba5a" dependencies = [ - "itoa 1.0.11", + "itoa 1.0.14", "ryu", "sval", ] [[package]] name = "sval_json" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +checksum = "411bbd543c413796ccfbaa44f6676e20032b6c69e4996cb6c3e6ef30c79b96d1" dependencies = [ - "itoa 1.0.11", + "itoa 1.0.14", "ryu", "sval", ] [[package]] name = "sval_nested" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +checksum = "f30582d2a90869b380f8260559138c1b68ac3e0765520959f22a1a1fdca31769" dependencies = [ "sval", "sval_buffer", @@ -6143,18 +6237,18 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +checksum = "762d3fbf3c0869064b7c93808c67ad2ed0292dde9b060ac282817941d4707dff" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.13.2" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +checksum = "752d307438c6a6a3d095a2fecf6950cfb946d301a5bd6b57f047db4f6f8d97b9" dependencies = [ "serde", "sval", @@ -6169,9 +6263,9 @@ checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" [[package]] name = "svgtypes" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ "kurbo", "siphasher 1.0.1", @@ -6201,9 +6295,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -6233,7 +6327,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6262,7 +6356,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -6333,7 +6427,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6344,14 +6438,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.2.0", + "fastrand 2.3.0", + "getrandom 0.2.15", "once_cell", - "rustix 0.38.40", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -6392,11 +6487,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.11", ] [[package]] @@ -6407,18 +6502,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6434,12 +6529,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", - "itoa 1.0.11", + "itoa 1.0.14", "num-conv", "powerfmt", "serde", @@ -6455,9 +6550,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -6510,9 +6605,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -6525,9 +6620,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -6536,20 +6631,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.5.8", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6575,12 +6670,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.17", - "rustls-pki-types", + "rustls 0.23.21", "tokio", ] @@ -6609,14 +6703,14 @@ dependencies = [ "tokio", "tokio-rustls 0.25.0", "tungstenite", - "webpki-roots 0.26.6", + "webpki-roots 0.26.7", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -6664,7 +6758,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -6677,7 +6771,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "toml_datetime", "winnow 0.5.40", ] @@ -6688,11 +6782,11 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow 0.6.25", ] [[package]] @@ -6703,9 +6797,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -6715,20 +6809,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -6774,7 +6868,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "rand 0.8.5", @@ -6806,9 +6900,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typesize" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549e54551d85ba6718a95333d9bc4367f69793d7aba638de30f8d25a1f554a1d" +checksum = "20304d891be0766f52123746c721d1190b953e874f9eccf29067a64c1a0ae16c" dependencies = [ "chrono", "dashmap 5.5.3", @@ -6824,13 +6918,13 @@ dependencies = [ [[package]] name = "typesize-derive" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9fc0ad9e03a2b0c2e2a0eafaecccef2121829e1ab6ce9c9d790e6c6766bd1c" +checksum = "536b6812192bda8551cfa0e52524e328c6a951b48e66529ee4522d6c721243d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6847,7 +6941,7 @@ dependencies = [ [[package]] name = "ui" version = "0.1.0" -source = "git+https://github.com/scopeclient/components#8d6194fc22392cfa840d3a811b903455526203e6" +source = "git+https://github.com/scopeclient/components#c248dfb2630b1240ab4b7d69e29e401fdfd5209b" dependencies = [ "anyhow", "chrono", @@ -6863,7 +6957,7 @@ dependencies = [ "serde", "serde_json", "smallvec", - "smol", + "smol 1.3.0", "unicode-segmentation", "usvg", "uuid", @@ -6872,15 +6966,15 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-bidi-mirroring" @@ -6908,9 +7002,9 @@ checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-linebreak" @@ -6962,9 +7056,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -7026,20 +7120,24 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#76d3936511b02af10b3a7c591069049172006176" +source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", - "async-fs 1.6.0", + "async-fs 2.1.2", "collections", "dirs 4.0.0", + "dunce", "futures", "futures-lite 1.13.0", "globset", + "itertools 0.14.0", + "libc", "log", "regex", "rust-embed", "serde", "serde_json", + "smol 2.0.2", "take-until", "tendril", "unicase", @@ -7047,9 +7145,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom 0.2.15", "serde", @@ -7180,47 +7278,48 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7228,22 +7327,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -7266,7 +7368,7 @@ checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.40", + "rustix 0.38.44", "scoped-tls", "smallvec", "wayland-sys", @@ -7278,8 +7380,8 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", - "rustix 0.38.40", + "bitflags 2.8.0", + "rustix 0.38.44", "wayland-backend", "wayland-scanner", ] @@ -7290,7 +7392,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" dependencies = [ - "rustix 0.38.40", + "rustix 0.38.44", "wayland-client", "xcursor", ] @@ -7301,7 +7403,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7313,7 +7415,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7345,9 +7447,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -7415,18 +7517,18 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -7444,14 +7546,14 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "webview2-com-sys" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +checksum = "7a82bce72db6e5ee83c68b5de1e2cd6ea195b9fbff91cb37df5884cbe3222df4" dependencies = [ "thiserror 1.0.69", "windows", @@ -7535,7 +7637,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -7546,7 +7648,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -7654,20 +7756,36 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows-version" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +checksum = "c12476c23a74725c539b24eae8bfc0dac4029c39cdb561d9f23616accd4ae26d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -7688,6 +7806,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7706,6 +7830,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7724,12 +7854,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7748,6 +7890,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7766,6 +7914,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -7784,6 +7938,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7802,6 +7962,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -7813,9 +7979,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] @@ -7863,9 +8029,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.47.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553ca1ce149982123962fac2506aa75b8b76288779a77e72b12fa2fc34938647" +checksum = "a2e33c08b174442ff80d5c791020696f9f8b4e4a87b8cfc7494aad6167ec44e1" dependencies = [ "base64 0.22.1", "block2", @@ -7876,7 +8042,7 @@ dependencies = [ "gdkx11", "gtk", "html5ever", - "http 1.1.0", + "http 1.2.0", "javascriptcore-rs", "jni", "kuchikiki", @@ -7893,7 +8059,7 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 1.0.69", + "thiserror 2.0.11", "url", "webkit2gtk", "webkit2gtk-sys", @@ -7944,7 +8110,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "rustix 0.38.40", + "rustix 0.38.44", "x11rb-protocol", ] @@ -7996,7 +8162,7 @@ name = "xim-parser" version = "0.2.1" source = "git+https://github.com/XDeme1/xim-rs?rev=d50d461764c2213655cd9cf65a0ea94c70d3c4fd#d50d461764c2213655cd9cf65a0ea94c70d3c4fd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -8041,9 +8207,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -8053,13 +8219,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -8080,7 +8246,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.3.1", + "event-listener 5.4.0", "futures-core", "futures-sink", "futures-util", @@ -8096,9 +8262,45 @@ dependencies = [ "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2494e4b3f44d8363eef79a8a75fc0649efb710eef65a66b5e688a5eb4afe678a" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs 2.1.2", + "async-io 2.4.0", + "async-lock 3.4.0", + "async-process 2.3.0", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.0", + "futures-core", + "futures-util", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.6.25", + "xdg-home", + "zbus_macros 5.3.1", + "zbus_names 4.1.1", + "zvariant 5.2.0", ] [[package]] @@ -8110,8 +8312,23 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", - "zvariant_utils", + "syn 2.0.96", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_macros" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445efc01929302aee95e2b25bbb62a301ea8a6369466e4278e58e7d1dfb23631" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.96", + "zbus_names 4.1.1", + "zvariant 5.2.0", + "zvariant_utils 3.1.0", ] [[package]] @@ -8122,7 +8339,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_names" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.6.25", + "zvariant 5.2.0", ] [[package]] @@ -8149,27 +8378,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -8190,7 +8419,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -8212,7 +8441,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -8232,9 +8461,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] @@ -8244,13 +8473,28 @@ name = "zvariant" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", "url", - "zvariant_derive", + "winnow 0.6.25", + "zvariant_derive 5.2.0", + "zvariant_utils 3.1.0", ] [[package]] @@ -8262,8 +8506,21 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.87", - "zvariant_utils", + "syn 2.0.96", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_derive" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.96", + "zvariant_utils 3.1.0", ] [[package]] @@ -8274,5 +8531,19 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "zvariant_utils" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.96", + "winnow 0.6.25", ] diff --git a/src/chat/src/message.rs b/src/chat/src/message.rs index 103a63b..a175cc6 100644 --- a/src/chat/src/message.rs +++ b/src/chat/src/message.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use chrono::{DateTime, Utc}; -use gpui::{IntoElement, Render, View, WindowContext}; +use gpui::{IntoElement, Render, Entity, App}; use crate::async_list::AsyncListItem; use crate::reaction::ReactionList; @@ -12,12 +12,12 @@ pub trait Message: Clone + AsyncListItem + Send { type Content: Render; fn get_author(&self) -> Self::Author; - fn get_content(&self, cx: &mut WindowContext) -> View; + fn get_content(&self, cx: &mut App) -> Entity; fn get_identifier(&self) -> Option<::Identifier>; fn get_nonce(&self) -> impl PartialEq; fn should_group(&self, previous: &Self) -> bool; fn get_timestamp(&self) -> Option>; - fn get_reactions(&mut self) -> &mut impl ReactionList; + fn get_reactions(&mut self) -> Option<&mut impl ReactionList>; } #[derive(Debug, Clone, Copy)] diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index 1f4240f..db29e57 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -12,7 +12,7 @@ pub enum MessageReactionType { #[derive(Clone, PartialEq)] pub enum ReactionEmoji { Simple(String), - Custom { url: String, animated: bool, name: Option }, + Custom { url: String, animated: bool, name: Option, id: u64 }, } impl Debug for ReactionEmoji { @@ -25,6 +25,7 @@ impl Debug for ReactionEmoji { } pub trait MessageReaction: IntoElement { + fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; fn get_emoji(&self) -> ReactionEmoji; diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index a5f8155..a7181b3 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -75,7 +75,6 @@ impl Channel for DiscordChannel { content, sent_time: Utc::now(), list_item_id: Snowflake::random(), - reactions: Default::default(), }, content: OnceLock::new(), } diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index 0c6d3dc..cb7fd19 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -6,8 +6,8 @@ use std::{ use crate::message::reaction::discord_reaction_to_emoji; use atomic_refcell::AtomicRefCell; use dashmap::DashMap; -use scope_chat::reaction::{MessageReactionType, ReactionEvent, ReactionOperation}; -use serenity::all::Reaction; +use scope_chat::reaction::{MessageReactionType, ReactionEmoji, ReactionEvent, ReactionOperation}; +use serenity::all::{EmojiId, Reaction, ReactionType}; use serenity::{ all::{ Cache, CacheHttp, ChannelId, Context, CreateMessage, EventHandler, GatewayIntents, GetMessages, GuildId, Http, Member, Message, MessageId, @@ -146,6 +146,19 @@ impl DiscordClient { } } } + + pub async fn add_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { + let reaction_type = match emoji { + ReactionEmoji::Simple(c) => ReactionType::Unicode(c), + ReactionEmoji::Custom { name, animated, id, .. } => ReactionType::Custom { + id: EmojiId::new(id), + animated, + name, + }, + }; + + channel_id.create_reaction(self.discord().http.clone(), message_id, reaction_type).await.unwrap(); + } } #[async_trait] diff --git a/src/discord/src/message/author.rs b/src/discord/src/message/author.rs index fbf3f2b..77c975a 100644 --- a/src/discord/src/message/author.rs +++ b/src/discord/src/message/author.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui::{div, img, IntoElement, ParentElement, RenderOnce, SharedString, Styled, WindowContext}; +use gpui::{div, img, App, IntoElement, ParentElement, RenderOnce, SharedString, Styled, Window}; use scope_chat::message::{IconRenderConfig, MessageAuthor}; use url::Url; @@ -72,7 +72,7 @@ impl MessageAuthor for DiscordMessageAuthor { pub struct DisplayName(pub SharedString); impl RenderOnce for DisplayName { - fn render(self, _: &mut WindowContext) -> impl IntoElement { + fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { div().text_sm().child(self.0) } } @@ -81,7 +81,7 @@ impl RenderOnce for DisplayName { pub struct DisplayIcon(pub String, pub IconRenderConfig); impl RenderOnce for DisplayIcon { - fn render(self, _: &mut WindowContext) -> impl IntoElement { + fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement { let mut url = Url::parse(&self.0).unwrap(); let mut query_params = querystring::querify(url.query().unwrap_or("")); diff --git a/src/discord/src/message/content.rs b/src/discord/src/message/content.rs index 5ce485f..888d117 100644 --- a/src/discord/src/message/content.rs +++ b/src/discord/src/message/content.rs @@ -1,12 +1,13 @@ use crate::message::reaction_list::DiscordReactionList; -use gpui::{div, IntoElement, ParentElement, Render, Styled, ViewContext}; +use gpui::prelude::FluentBuilder; +use gpui::{div, Context, IntoElement, ParentElement, Render, Styled, Window}; use serenity::all::Message; #[derive(Clone, Debug)] pub struct DiscordMessageContent { pub content: String, pub is_pending: bool, - pub reactions: DiscordReactionList, + pub reactions: Option, } impl DiscordMessageContent { @@ -14,7 +15,7 @@ impl DiscordMessageContent { DiscordMessageContent { content, is_pending: true, - reactions: Default::default(), + reactions: None, } } @@ -22,16 +23,16 @@ impl DiscordMessageContent { DiscordMessageContent { content: message.content.clone(), is_pending: false, - reactions: reactions.clone(), + reactions: Some(reactions.clone()), } } } impl Render for DiscordMessageContent { - fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { div() - .opacity(if self.is_pending { 0.25 } else { 1.0 }) - .child(self.content.clone()) - .child(self.reactions.clone()) + .opacity(if self.is_pending { 0.25 } else { 1.0 }) + .child(self.content.clone()) + .when_some(self.reactions.clone(), |d, reactions| d.child(reactions)) } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 4c5b131..131b7da 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -4,7 +4,7 @@ use crate::message::reaction_list::DiscordReactionList; use author::DiscordMessageAuthor; use chrono::{DateTime, Utc}; use content::DiscordMessageContent; -use gpui::{View, VisualContext, WindowContext}; +use gpui::{App, AppContext, Entity}; use scope_chat::reaction::ReactionList; use scope_chat::{async_list::AsyncListItem, message::Message}; use serenity::all::{ModelError, Nonce}; @@ -23,7 +23,6 @@ pub enum DiscordMessageData { content: String, sent_time: DateTime, list_item_id: Snowflake, - reactions: DiscordReactionList }, Received(Arc, Option>, DiscordReactionList), } @@ -33,7 +32,7 @@ pub struct DiscordMessage { pub client: Arc, pub channel: Arc, pub data: DiscordMessageData, - pub content: OnceLock>, + pub content: OnceLock>, } impl DiscordMessage { @@ -46,7 +45,7 @@ impl DiscordMessage { } .unwrap(); - let reactions = (&msg.reactions).into(); + let reactions = DiscordReactionList::new_serenity(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); Self { client, @@ -62,7 +61,7 @@ impl DiscordMessage { channel: Arc, member: Option>, ) -> Self { - let reactions = (&msg.reactions).into(); + let reactions = DiscordReactionList::new_serenity(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); Self { client, channel, @@ -130,7 +129,7 @@ impl Message for DiscordMessage { } // TODO: want reviewer discussion. I'm really stretching the abilities of gpui here and im not sure if this is the right way to do this. - fn get_content(&self, cx: &mut WindowContext) -> View { + fn get_content(&self, cx: &mut App) -> Entity { self .content .get_or_init(|| { @@ -139,7 +138,7 @@ impl Message for DiscordMessage { DiscordMessageData::Received(message, _, reactions) => DiscordMessageContent::received(message, reactions), }; - cx.new_view(|_cx| content) + cx.new(|_| content) }) .clone() } @@ -174,10 +173,10 @@ impl Message for DiscordMessage { } } - fn get_reactions(&mut self) -> &mut impl ReactionList { + fn get_reactions(&mut self) -> Option<&mut impl ReactionList> { match &mut self.data { - DiscordMessageData::Pending { reactions, .. } => reactions, - DiscordMessageData::Received(_, _, reactions) => reactions, + DiscordMessageData::Pending { .. } => None, + DiscordMessageData::Received(_, _, reactions) => Some(reactions), } } } diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 5557a00..a8a663e 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,9 +1,10 @@ use components::theme::ActiveTheme; -use gpui::{div, img, px, AnyElement, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, StatefulInteractiveElement, Styled, WindowContext}; use gpui::prelude::FluentBuilder; +use gpui::{div, img, px, AnyElement, App, IntoElement, ParentElement, RenderOnce, Rgba, Styled}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; use serenity::all::ReactionType; +use std::fmt::Debug; use MessageReactionType::Burst; #[derive(Clone, Debug)] @@ -46,7 +47,7 @@ impl DiscordMessageReaction { } } - fn use_local(&mut self) { + fn swap_to_local(&mut self) { let (count_normal, count_burst) = match &self.data { ReactionData::Message(reaction) => (reaction.count_details.normal, reaction.count_details.burst), ReactionData::Local { @@ -64,6 +65,12 @@ impl DiscordMessageReaction { burst_colours, } } + fn render_emoji(emoji: &ReactionEmoji) -> AnyElement { + match emoji { + ReactionEmoji::Simple(character) => div().text_size(px(12f32)).child(character.clone()).into_any_element(), + ReactionEmoji::Custom { url, .. } => img(url.clone()).w(px(16f32)).h(px(16f32)).into_any_element(), + } + } } impl MessageReaction for DiscordMessageReaction { @@ -114,7 +121,7 @@ impl MessageReaction for DiscordMessageReaction { } fn increment(&mut self, kind: MessageReactionType, user_is_self: bool, by: isize) { - self.use_local(); + self.swap_to_local(); match kind { Burst => { if let ReactionData::Local { count_burst, .. } = &mut self.data { @@ -140,47 +147,34 @@ impl MessageReaction for DiscordMessageReaction { } } -impl DiscordMessageReaction { - fn render_emoji(emoji: &ReactionEmoji) -> AnyElement { - match emoji { - ReactionEmoji::Simple(character) => div().text_size(px(12f32)).child(character.clone()).into_any_element(), - ReactionEmoji::Custom { url, .. } => img(url.clone()).w(px(16f32)).h(px(16f32)).into_any_element(), - } - } -} - impl RenderOnce for DiscordMessageReaction { - fn render(self, cx: &mut WindowContext) -> impl IntoElement { + fn render(self, _: &mut gpui::Window, cx: &mut App) -> impl IntoElement { let emoji = self.get_emoji(); - let async_emoji = emoji.clone(); let theme = cx.theme(); div() - .px_1() - .py_px() - .border_1() - .border_color(theme.border) - .when(self.get_self_reaction().is_some(), |s| s.border_color(theme.accent)) - .bg(theme.panel) - .rounded_md() - .flex() - .id("reaction") - .on_click(move |_, _| { - eprintln!("Reaction {:?} clicked", async_emoji); - }) - .justify_center() - .items_center() - .gap_1() - .child(Self::render_emoji(&emoji)) - .child(self.get_count(None).to_string()) + .px_1() + .py_px() + .border_1() + .border_color(theme.border) + .when(self.get_self_reaction().is_some(), |s| s.border_color(theme.accent)) + .bg(theme.panel) + .rounded_md() + .flex() + .justify_center() + .items_center() + .gap_1() + .child(Self::render_emoji(&emoji)) + .child(self.get_count(None).to_string()) } } -pub fn discord_reaction_to_emoji(reaction: &serenity::all::ReactionType) -> ReactionEmoji { +pub fn discord_reaction_to_emoji(reaction: &ReactionType) -> ReactionEmoji { match reaction { ReactionType::Custom { animated, id, name } => ReactionEmoji::Custom { url: format!("https://cdn.discordapp.com/emojis/{}.png", id), animated: *animated, name: name.clone(), + id: id.get(), }, ReactionType::Unicode(character) => ReactionEmoji::Simple(character.clone()), ty => { diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index b486e4c..30b63e3 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -1,23 +1,41 @@ +use crate::client::DiscordClient; use crate::message::reaction::{DiscordMessageReaction, ReactionData}; -use gpui::{div, IntoElement, ParentElement, RenderOnce, Styled, WindowContext}; +use gpui::{div, App, IntoElement, ParentElement, RenderOnce, Styled}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; +use serenity::all::{ChannelId, MessageId}; +use std::fmt::Debug; +use std::sync::Arc; -#[derive(Clone, Debug, Default, IntoElement)] +#[derive(Clone, IntoElement)] pub struct DiscordReactionList { reactions: Vec, + message_id: MessageId, + channel_id: ChannelId, + client: Arc, } impl DiscordReactionList { - pub fn new(reactions: Vec) -> Self { - DiscordReactionList { reactions } + pub fn new(reactions: Vec, channel_id: ChannelId, message_id: MessageId, client: Arc) -> Self { + DiscordReactionList { + reactions, + message_id, + channel_id, + client, + } } -} -impl From<&Vec> for DiscordReactionList { - fn from(reactions: &Vec) -> Self { + pub fn new_serenity( + reactions: Vec, + channel_id: ChannelId, + message_id: MessageId, + client: Arc, + ) -> Self { DiscordReactionList { reactions: reactions.iter().map(DiscordMessageReaction::from_message).collect(), + message_id, + channel_id, + client, } } } @@ -78,14 +96,17 @@ impl ReactionList for DiscordReactionList { } impl RenderOnce for DiscordReactionList { - fn render(self, _cx: &mut WindowContext) -> impl IntoElement { + fn render(self, _: &mut gpui::Window, _: &mut App) -> impl IntoElement { if self.reactions.is_empty() { return div(); } - div() - .flex() - .gap_2() - .children(self.reactions) + div().flex().gap_2().children(self.reactions) + } +} + +impl Debug for DiscordReactionList { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_set().entries(self.reactions.iter()).finish() } } diff --git a/src/ui/src/app.rs b/src/ui/src/app.rs index 1fdff2f..9652691 100644 --- a/src/ui/src/app.rs +++ b/src/ui/src/app.rs @@ -1,33 +1,29 @@ use components::theme::ActiveTheme; -use gpui::{div, img, rgb, Context, Model, ParentElement, Render, Styled, View, ViewContext, VisualContext}; +use gpui::{div, img, rgb, AppContext, Context, Entity, IntoElement, ParentElement, Render, Styled, Window}; use scope_backend_discord::{channel::DiscordChannel, client::DiscordClient, snowflake::Snowflake}; use crate::channel::ChannelView; pub struct App { - channel: Model>>>, + channel: Entity>>>, } impl App { - pub fn new(ctx: &mut ViewContext<'_, Self>) -> App { + pub fn new(window: &mut Window, cx: &mut gpui::App) -> App { let token = dotenv::var("DISCORD_TOKEN").expect("Must provide DISCORD_TOKEN in .env"); let demo_channel_id = dotenv::var("DEMO_CHANNEL_ID").expect("Must provide DEMO_CHANNEL_ID in .env"); - let mut context = ctx.to_async(); - - let channel = ctx.new_model(|_| None); + let channel = cx.new(|_| None); let async_channel = channel.clone(); - - ctx - .foreground_executor() - .spawn(async move { + window + .spawn(cx, |mut cx| async move { let client = DiscordClient::new(token).await; let channel = client.channel(Snowflake(demo_channel_id.parse().unwrap())).await; - let view = context.new_view(|cx| ChannelView::::create(cx, channel)).unwrap(); + let view = cx.update(|window, cx| cx.new(|cx| ChannelView::::create(window, cx, channel))).unwrap(); async_channel - .update(&mut context, |a, b| { + .update(&mut cx, |a, b| { *a = Some(view); b.notify() }) @@ -40,7 +36,7 @@ impl App { } impl Render for App { - fn render(&mut self, cx: &mut gpui::ViewContext) -> impl gpui::IntoElement { + fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement { let mut content = div().w_full().h_full(); if let Some(channel) = self.channel.read(cx).as_ref() { diff --git a/src/ui/src/app_state.rs b/src/ui/src/app_state.rs index 24332ff..59823e8 100644 --- a/src/ui/src/app_state.rs +++ b/src/ui/src/app_state.rs @@ -1,6 +1,6 @@ use std::sync::Weak; -use gpui::{AppContext, Global}; +use gpui::{App, Global}; pub struct AppState {} @@ -9,7 +9,7 @@ struct GlobalAppState(); impl Global for GlobalAppState {} impl AppState { - pub fn set_global(_app_state: Weak, cx: &mut AppContext) { + pub fn set_global(_app_state: Weak, cx: &mut App) { cx.set_global(GlobalAppState()); } } diff --git a/src/ui/src/channel/message.rs b/src/ui/src/channel/message.rs index 76869a6..293eb4e 100644 --- a/src/ui/src/channel/message.rs +++ b/src/ui/src/channel/message.rs @@ -1,5 +1,5 @@ use chrono::Local; -use gpui::{div, prelude::FluentBuilder, rgb, IntoElement, ParentElement, Styled, WindowContext}; +use gpui::{div, prelude::FluentBuilder, rgb, IntoElement, ParentElement, Styled}; use scope_chat::message::{IconRenderConfig, Message, MessageAuthor}; #[derive(Clone)] @@ -42,7 +42,7 @@ impl MessageGroup { } } -pub fn message_group(group: MessageGroup, cx: &mut WindowContext) -> impl IntoElement { +pub fn message_group(group: MessageGroup, cx: &mut gpui::App) -> impl IntoElement { div() .flex() .flex_row() diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index eac29be..b4ccc64 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use gpui::{div, list, rgb, Context, IntoElement, ListAlignment, ListState, Model, ParentElement, Pixels, Render, Styled, ViewContext}; +use gpui::{div, list, rgb, AppContext, Context, Entity, IntoElement, ListAlignment, ListState, ParentElement, Pixels, Render, Styled}; use scope_chat::{ async_list::{AsyncListIndex, AsyncListItem}, channel::Channel, @@ -34,14 +34,14 @@ where C::Content: 'static, { list: Arc>, - cache: Model>>>, + cache: Entity>>>, overdraw: Pixels, // top, bottom - bounds_flags: Model, + bounds_flags: Entity, - list_state: Model>, - list_state_dirty: Model>, + list_state: Entity>, + list_state_dirty: Entity>, } pub enum StartAt { @@ -53,10 +53,10 @@ impl MessageListComponent where T: 'static, { - pub fn create(cx: &mut ViewContext, list: T, overdraw: Pixels) -> Self { - let cache = cx.new_model(|_| Default::default()); - let list_state = cx.new_model(|_| None); - let list_state_dirty = cx.new_model(|_| None); + pub fn create(cx: &mut Context, list: T, overdraw: Pixels) -> Self { + let cache = cx.new(|_| Default::default()); + let list_state = cx.new(|_| None); + let list_state_dirty = cx.new(|_| None); let lsc = list_state.clone(); @@ -84,13 +84,13 @@ where list: Arc::new(RwLock::new(list)), cache, overdraw, - bounds_flags: cx.new_model(|_| BoundFlags { before: false, after: false }), + bounds_flags: cx.new(|_| BoundFlags { before: false, after: false }), list_state, list_state_dirty, } } - pub fn append_message(&mut self, cx: &mut ViewContext, message: T::Message) { + pub fn append_message(&mut self, cx: &mut Context, message: T::Message) { self.cache.update(cx, |borrow, cx| { for item in borrow.iter_mut() { if let Element::Resolved(Some(haystack)) = item { @@ -116,13 +116,14 @@ where }); } - pub fn update_reaction(&mut self, cx: &mut ViewContext, reaction: ReactionEvent) { + pub fn update_reaction(&mut self, cx: &mut Context, reaction: ReactionEvent) { self.cache.update(cx, |borrow: &mut Vec>>, cx| { for item in borrow.iter_mut() { if let Element::Resolved(Some(haystack)) = item { if haystack.get_identifier() == Some(reaction.0) { - let reactions = haystack.get_reactions(); - reactions.apply(reaction.1); + if let Some(reactions) = haystack.get_reactions() { + reactions.apply(reaction.1); + } cx.notify(); return; @@ -132,7 +133,7 @@ where }); } - fn list_state(&self, cx: &mut gpui::ViewContext) -> ListState { + fn list_state(&self, cx: &mut gpui::Context) -> ListState { let bounds_model = self.bounds_flags.clone(); let list_state_dirty = *self.list_state_dirty.read(cx); @@ -189,7 +190,7 @@ where if len == 0 { 1 } else { len + 2 }, ListAlignment::Bottom, self.overdraw, - move |idx, cx| { + move |idx, _, cx| { if len == 0 { cx.update_model(&bounds_model, |v, _| v.after = true); @@ -238,7 +239,7 @@ where new_list_state } - fn update(&mut self, cx: &mut gpui::ViewContext) { + fn update(&mut self, cx: &mut gpui::Context) { let mut dirty = None; let mut flags = *self.bounds_flags.read(cx); @@ -372,7 +373,7 @@ where } impl Render for MessageListComponent { - fn render(&mut self, cx: &mut gpui::ViewContext) -> impl gpui::IntoElement { + fn render(&mut self, _: &mut gpui::Window, cx: &mut Context) -> impl gpui::IntoElement { self.update(cx); let ls = if let Some(v) = self.list_state.read(cx).clone() { diff --git a/src/ui/src/channel/mod.rs b/src/ui/src/channel/mod.rs index e08bef5..9a3d567 100644 --- a/src/ui/src/channel/mod.rs +++ b/src/ui/src/channel/mod.rs @@ -4,23 +4,26 @@ pub mod message_list; use std::sync::Arc; use components::input::{InputEvent, TextInput}; -use gpui::{div, ParentElement, Pixels, Render, Styled, View, VisualContext}; +use gpui::{div, AppContext, Entity, ParentElement, Pixels, Render, Styled}; use message_list::MessageListComponent; use scope_chat::channel::Channel; pub struct ChannelView { - list_view: View>>, - message_input: View, + list_view: Entity>>, + message_input: Entity, } impl ChannelView { - pub fn create(ctx: &mut gpui::ViewContext<'_, ChannelView>, channel: Arc) -> Self where ::Identifier: Send { + pub fn create(window: &mut gpui::Window, ctx: &mut gpui::Context<'_, ChannelView>, channel: Arc) -> Self + where + ::Identifier: Send, + { let channel_message_listener = channel.get_message_receiver(); let channel_reaction_listener = channel.get_reaction_receiver(); let c2 = channel.clone(); - let list_view = ctx.new_view(|cx| MessageListComponent::create(cx, channel, Pixels(30.))); + let list_view = ctx.new(|cx| MessageListComponent::create(cx, channel, Pixels(30.))); let async_model = list_view.clone(); let mut async_ctx = ctx.to_async(); @@ -51,42 +54,42 @@ impl ChannelView { }) .detach(); - let async_model = list_view.clone(); let mut async_ctx = ctx.to_async(); ctx - .foreground_executor() - .spawn(async move { - loop { - let (sender, receiver) = catty::oneshot(); - - let mut l = channel_reaction_listener.resubscribe(); - - tokio::spawn(async move { - sender.send(l.recv().await).unwrap(); - }); - - let reaction = receiver.await.unwrap().unwrap(); - async_model - .update(&mut async_ctx, |data, ctx| { - data.update_reaction(ctx, reaction); - ctx.notify(); - }) - .unwrap(); - } - }) - .detach(); + .foreground_executor() + .spawn(async move { + loop { + let (sender, receiver) = catty::oneshot(); + + let mut l = channel_reaction_listener.resubscribe(); + + tokio::spawn(async move { + sender.send(l.recv().await).unwrap(); + }); + + let reaction = receiver.await.unwrap().unwrap(); + async_model + .update(&mut async_ctx, |data, ctx| { + data.update_reaction(ctx, reaction); + ctx.notify(); + }) + .unwrap(); + } + }) + .detach(); - let message_input = ctx.new_view(|cx| { - let mut input = components::input::TextInput::new(cx); + let message_input = ctx.new(|cx| { + let mut input = TextInput::new(window, cx); - input.set_size(components::Size::Large, cx); + input.set_size(components::Size::Large, window, cx); input }); let async_model = list_view.clone(); + let mut async_window = window.to_async(ctx); ctx .subscribe(&message_input, move |_, text_input, input_event, ctx| { if let InputEvent::PressEnter = input_event { @@ -96,7 +99,7 @@ impl ChannelView { } text_input.update(ctx, |text_input, cx| { - text_input.set_text("", cx); + async_window.update(|window, _| text_input.set_text("", window, cx)).unwrap() }); let nonce = random_string::generate(20, random_string::charsets::ALPHANUMERIC); @@ -128,7 +131,7 @@ impl ChannelView { } impl Render for ChannelView { - fn render(&mut self, _: &mut gpui::ViewContext) -> impl gpui::IntoElement { + fn render(&mut self, _: &mut gpui::Window, _: &mut gpui::Context) -> impl gpui::IntoElement { div() .flex() .flex_col() diff --git a/src/ui/src/main.rs b/src/ui/src/main.rs index 4282e3b..80b01d9 100644 --- a/src/ui/src/main.rs +++ b/src/ui/src/main.rs @@ -26,7 +26,7 @@ impl AssetSource for Assets { } } -fn init(_: Arc, cx: &mut AppContext) -> Result<()> { +fn init(_: Arc, cx: &mut App) -> Result<()> { components::init(cx); if cfg!(target_os = "macos") { @@ -50,10 +50,10 @@ async fn main() { let app_state = Arc::new(AppState {}); - App::new().with_assets(Assets).with_http_client(Arc::new(reqwest_client::ReqwestClient::new())).run(move |cx: &mut AppContext| { - AppState::set_global(Arc::downgrade(&app_state), cx); + Application::new().with_assets(Assets).with_http_client(Arc::new(reqwest_client::ReqwestClient::new())).run(move |app: &mut App| { + AppState::set_global(Arc::downgrade(&app_state), app); - if let Err(e) = init(app_state.clone(), cx) { + if let Err(e) = init(app_state.clone(), app) { log::error!("{}", e); return; } @@ -64,8 +64,8 @@ async fn main() { theme.title_bar = hsl(335.0, 97.0, 61.0); theme.background = hsl(225.0, 12.0, 10.0); - cx.set_global(theme); - cx.refresh(); + app.set_global(theme); + app.refresh_windows(); let opts = WindowOptions { window_decorations: Some(WindowDecorations::Client), @@ -78,6 +78,6 @@ async fn main() { ..Default::default() }; - cx.open_window(opts, |cx| cx.new_view(crate::app::App::new)).unwrap(); + app.open_window(opts, |window: &mut Window, cx| cx.new(|cx| app::App::new(window, cx))).unwrap(); }); } From 72b25299fc8f2d5128c3273bd7339a074e08d1ef Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 09:26:40 +0100 Subject: [PATCH 11/18] feat: add / remove reactions --- Cargo.lock | 25 ++++---- Cargo.toml | 1 + src/chat/Cargo.toml | 1 + src/chat/src/reaction.rs | 10 +-- src/discord/Cargo.toml | 2 +- src/discord/src/channel/mod.rs | 2 +- src/discord/src/client.rs | 15 +++-- src/discord/src/message/content.rs | 5 +- src/discord/src/message/mod.rs | 28 ++++---- src/discord/src/message/reaction.rs | 38 +++++++++-- src/discord/src/message/reaction_list.rs | 81 ++++++++++++------------ src/ui/src/app.rs | 9 ++- src/ui/src/channel/mod.rs | 16 ++--- 13 files changed, 141 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22d804a..a30adf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -1056,7 +1056,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "indexmap 2.7.1", "rustc-hash 2.1.0", @@ -1437,7 +1437,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "proc-macro2", "quote", @@ -2412,7 +2412,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2498,7 +2498,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "proc-macro2", "quote", @@ -2767,7 +2767,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bytes", @@ -3606,7 +3606,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bindgen", @@ -5022,7 +5022,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "derive_refineable", ] @@ -5150,7 +5150,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "bytes", @@ -5595,6 +5595,7 @@ dependencies = [ name = "scope-chat" version = "0.1.0" dependencies = [ + "atomic_refcell", "chrono", "gpui", "tokio", @@ -5694,7 +5695,7 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "serde", @@ -6170,7 +6171,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "arrayvec", "log", @@ -7120,7 +7121,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/scopeclient/zed.git?branch=feature/export-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" +source = "git+https://github.com/scopeclient/zed.git?branch=feature%2Fexport-platform-window#82a13d8638fe45c7d13fef49726e2fa466923816" dependencies = [ "anyhow", "async-fs 2.1.2", diff --git a/Cargo.toml b/Cargo.toml index 9409fce..eb55298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ gpui = { git = "https://github.com/scopeclient/zed.git", branch = "feature/expor ] } components = { package = "ui", git = "https://github.com/scopeclient/components", version = "0.1.0" } reqwest_client = { git = "https://github.com/scopeclient/zed.git", branch = "feature/export-platform-window", version = "0.1.0" } +atomic_refcell = "0.1.13" diff --git a/src/chat/Cargo.toml b/src/chat/Cargo.toml index a8ceb6a..bf975e7 100644 --- a/src/chat/Cargo.toml +++ b/src/chat/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" tokio = "1.41.1" chrono.workspace = true gpui.workspace = true +atomic_refcell.workspace = true diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index db29e57..a5433d7 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,5 +1,7 @@ use std::fmt::{Debug, Formatter}; -use gpui::{IntoElement, Rgba}; +use std::sync::Arc; +use atomic_refcell::AtomicRefCell; +use gpui::{App, IntoElement, Rgba}; pub type ReactionEvent = (T, ReactionOperation); @@ -43,9 +45,9 @@ pub enum ReactionOperation { RemoveEmoji(ReactionEmoji), } -pub trait ReactionList: IntoElement { - fn get_reactions(&self) -> &Vec; - fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction>; +pub trait ReactionList { + fn get_reactions(&self) -> &Arc>>; fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize); fn apply(&mut self, operation: ReactionOperation); + fn get_content(&self, cx: &mut App) -> impl IntoElement; } diff --git a/src/discord/Cargo.toml b/src/discord/Cargo.toml index 0d7c6f0..fb394e9 100644 --- a/src/discord/Cargo.toml +++ b/src/discord/Cargo.toml @@ -14,7 +14,7 @@ scope-backend-cache = { version = "0.1.0", path = "../cache" } url = "2.5.3" querystring = "1.1.0" catty = "0.1.5" -atomic_refcell = "0.1.13" +atomic_refcell.workspace = true rand = "0.8.5" dashmap = "6.1.0" log = "0.4.22" diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index a7181b3..44f4c73 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -76,7 +76,7 @@ impl Channel for DiscordChannel { sent_time: Utc::now(), list_item_id: Snowflake::random(), }, - content: OnceLock::new(), + content: Arc::new(OnceLock::new()), } } diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index cb7fd19..2e23400 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -56,7 +56,6 @@ impl DiscordClient { let client = Arc::new_cyclic(|weak| DiscordClient { ready_notifier: AtomicRefCell::new(Some(sender)), weak: weak.clone(), - ..Default::default() }); @@ -147,18 +146,26 @@ impl DiscordClient { } } - pub async fn add_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { - let reaction_type = match emoji { + fn emoji_to_serenity(emoji: &ReactionEmoji) -> ReactionType { + match emoji.clone() { ReactionEmoji::Simple(c) => ReactionType::Unicode(c), ReactionEmoji::Custom { name, animated, id, .. } => ReactionType::Custom { id: EmojiId::new(id), animated, name, }, - }; + } + } + pub async fn add_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { + let reaction_type = Self::emoji_to_serenity(&emoji); channel_id.create_reaction(self.discord().http.clone(), message_id, reaction_type).await.unwrap(); } + + pub async fn remove_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { + let reaction_type = Self::emoji_to_serenity(&emoji); + channel_id.delete_reaction(self.discord().http.clone(), message_id, None, reaction_type).await.unwrap(); + } } #[async_trait] diff --git a/src/discord/src/message/content.rs b/src/discord/src/message/content.rs index 888d117..e4a20d5 100644 --- a/src/discord/src/message/content.rs +++ b/src/discord/src/message/content.rs @@ -2,6 +2,7 @@ use crate::message::reaction_list::DiscordReactionList; use gpui::prelude::FluentBuilder; use gpui::{div, Context, IntoElement, ParentElement, Render, Styled, Window}; use serenity::all::Message; +use scope_chat::reaction::ReactionList; #[derive(Clone, Debug)] pub struct DiscordMessageContent { @@ -29,10 +30,10 @@ impl DiscordMessageContent { } impl Render for DiscordMessageContent { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { div() .opacity(if self.is_pending { 0.25 } else { 1.0 }) .child(self.content.clone()) - .when_some(self.reactions.clone(), |d, reactions| d.child(reactions)) + .when_some(self.reactions.clone(), |d, reactions| d.child(reactions.get_content(cx))) } } diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 131b7da..f7785e2 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -24,7 +24,11 @@ pub enum DiscordMessageData { sent_time: DateTime, list_item_id: Snowflake, }, - Received(Arc, Option>, DiscordReactionList), + Received( + Arc, + Option>, + DiscordReactionList, + ), } #[derive(Clone)] @@ -32,7 +36,7 @@ pub struct DiscordMessage { pub client: Arc, pub channel: Arc, pub data: DiscordMessageData, - pub content: OnceLock>, + pub content: Arc>>, } impl DiscordMessage { @@ -45,13 +49,13 @@ impl DiscordMessage { } .unwrap(); - let reactions = DiscordReactionList::new_serenity(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); + let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); Self { client, channel, data: DiscordMessageData::Received(msg, member, reactions), - content: OnceLock::new(), + content: Arc::new(OnceLock::new()), } } @@ -61,12 +65,12 @@ impl DiscordMessage { channel: Arc, member: Option>, ) -> Self { - let reactions = DiscordReactionList::new_serenity(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); + let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); Self { client, channel, data: DiscordMessageData::Received(msg, member, reactions), - content: OnceLock::new(), + content: Arc::new(OnceLock::new()), } } } @@ -129,18 +133,18 @@ impl Message for DiscordMessage { } // TODO: want reviewer discussion. I'm really stretching the abilities of gpui here and im not sure if this is the right way to do this. + // Additional Context to this discussion: the OnceLock in context CANNOT be cloned because it messes with the internal gpui entityids + // and recreates the entity on every interaction making it impossible to interact with the message content in any way. Right now, I'm + // using an arc for this, but this feels like a band-aid solution. I do agree that this should be refactored out at some point. fn get_content(&self, cx: &mut App) -> Entity { self .content .get_or_init(|| { - let content = match &self.data { + cx.new(|_| match &self.data { DiscordMessageData::Pending { content, .. } => DiscordMessageContent::pending(content.clone()), DiscordMessageData::Received(message, _, reactions) => DiscordMessageContent::received(message, reactions), - }; - - cx.new(|_| content) - }) - .clone() + }) + }).clone() } fn get_identifier(&self) -> Option { diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index a8a663e..165d181 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,10 +1,12 @@ +use crate::client::DiscordClient; use components::theme::ActiveTheme; use gpui::prelude::FluentBuilder; -use gpui::{div, img, px, AnyElement, App, IntoElement, ParentElement, RenderOnce, Rgba, Styled}; +use gpui::{div, img, px, AnyElement, App, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, StatefulInteractiveElement, Styled}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; -use serenity::all::ReactionType; +use serenity::all::{ChannelId, MessageId, ReactionType}; use std::fmt::Debug; +use std::sync::Arc; use MessageReactionType::Burst; #[derive(Clone, Debug)] @@ -35,15 +37,21 @@ impl ReactionData { } } -#[derive(Clone, Debug, IntoElement)] +#[derive(Clone, IntoElement)] pub struct DiscordMessageReaction { pub data: ReactionData, + pub(crate) client: Arc, + pub(crate) message_id: MessageId, + pub(crate) channel_id: ChannelId, } impl DiscordMessageReaction { - pub fn from_message(reaction: &serenity::all::MessageReaction) -> Self { + pub fn new(reaction: &serenity::all::MessageReaction, client: Arc, message_id: MessageId, channel_id: ChannelId) -> Self { DiscordMessageReaction { data: ReactionData::Message(reaction.clone()), + client, + message_id, + channel_id, } } @@ -71,6 +79,18 @@ impl DiscordMessageReaction { ReactionEmoji::Custom { url, .. } => img(url.clone()).w(px(16f32)).h(px(16f32)).into_any_element(), } } + + fn handle_click(&self, app: &App) { + let reaction = self.clone(); + let had_reaction = reaction.get_self_reaction().is_some(); + app.spawn(|_| async move { + if had_reaction { + reaction.client.remove_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; + } else { + reaction.client.add_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; + } + }).detach(); + } } impl MessageReaction for DiscordMessageReaction { @@ -165,6 +185,10 @@ impl RenderOnce for DiscordMessageReaction { .gap_1() .child(Self::render_emoji(&emoji)) .child(self.get_count(None).to_string()) + .id("reaction") + .on_click(move |_, _, app| { + self.handle_click(app); + }) } } @@ -183,3 +207,9 @@ pub fn discord_reaction_to_emoji(reaction: &ReactionType) -> ReactionEmoji { } } } + +impl Debug for DiscordMessageReaction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DiscordMessageReaction").field("data", &self.data).finish() + } +} diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 30b63e3..566dee6 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -1,59 +1,44 @@ use crate::client::DiscordClient; use crate::message::reaction::{DiscordMessageReaction, ReactionData}; -use gpui::{div, App, IntoElement, ParentElement, RenderOnce, Styled}; +use gpui::{div, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, RenderOnce, Styled, Window}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; use serenity::all::{ChannelId, MessageId}; use std::fmt::Debug; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; +use atomic_refcell::{AtomicRef, AtomicRefCell}; -#[derive(Clone, IntoElement)] +#[derive(Clone)] pub struct DiscordReactionList { - reactions: Vec, + reactions: Arc>>, message_id: MessageId, channel_id: ChannelId, client: Arc, + entity: Arc>> } impl DiscordReactionList { - pub fn new(reactions: Vec, channel_id: ChannelId, message_id: MessageId, client: Arc) -> Self { + pub fn new(reactions: Vec, channel_id: ChannelId, message_id: MessageId, client: Arc) -> Self { DiscordReactionList { - reactions, - message_id, - channel_id, - client, - } - } - - pub fn new_serenity( - reactions: Vec, - channel_id: ChannelId, - message_id: MessageId, - client: Arc, - ) -> Self { - DiscordReactionList { - reactions: reactions.iter().map(DiscordMessageReaction::from_message).collect(), + reactions: Arc::new(AtomicRefCell::new(reactions.iter().map(|reaction| DiscordMessageReaction::new(reaction, client.clone(), message_id.clone(), channel_id.clone())).collect())), message_id, channel_id, client, + entity: Arc::new(OnceLock::new()) } } } impl ReactionList for DiscordReactionList { - fn get_reactions(&self) -> &Vec { + fn get_reactions(&self) -> &Arc>> { &self.reactions } - fn get_reaction(&self, emoji: &ReactionEmoji) -> Option<&impl MessageReaction> { - self.reactions.iter().find(|reaction| reaction.get_emoji() == *emoji) - } - fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize) { - if let Some(reaction) = self.reactions.iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { + if let Some(reaction) = self.reactions.borrow_mut().iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { reaction.increment(kind, user_is_self, by); if reaction.get_count(None) == 0 { - self.reactions.retain(|reaction| reaction.get_emoji() != *emoji); + self.reactions.borrow_mut().retain(|reaction| reaction.get_emoji() != *emoji); } } else if by > 0 { let mut reaction = DiscordMessageReaction { @@ -64,10 +49,13 @@ impl ReactionList for DiscordReactionList { emoji: emoji.clone(), burst_colours: vec![], }, + client: self.client.clone(), + message_id: self.message_id.clone(), + channel_id: self.channel_id.clone(), }; reaction.increment(kind, user_is_self, by); - self.reactions.push(reaction); + self.reactions.borrow_mut().push(reaction); } } @@ -86,27 +74,42 @@ impl ReactionList for DiscordReactionList { self.increment(&emoji, Normal, true, -1); } ReactionOperation::RemoveAll => { - self.reactions.clear(); + self.reactions.borrow_mut().clear(); } ReactionOperation::RemoveEmoji(emoji) => { - self.reactions.retain(|reaction| reaction.get_emoji() != emoji); + self.reactions.borrow_mut().retain(|reaction| reaction.get_emoji() != emoji); } } } -} - -impl RenderOnce for DiscordReactionList { - fn render(self, _: &mut gpui::Window, _: &mut App) -> impl IntoElement { - if self.reactions.is_empty() { - return div(); - } - div().flex().gap_2().children(self.reactions) + fn get_content(&self, cx: &mut App) -> impl IntoElement { + self.entity.get_or_init(|| cx.new(|cx| RenderableReactionList::new(self.clone()))).clone() } } impl Debug for DiscordReactionList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_set().entries(self.reactions.iter()).finish() + f.debug_set().entries(self.reactions.borrow().iter()).finish() + } +} + +struct RenderableReactionList { + list: DiscordReactionList, +} + +impl RenderableReactionList { + fn new(list: DiscordReactionList) -> Self { + Self { list } + } +} + +impl Render for RenderableReactionList { + fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement { + if self.list.reactions.borrow().is_empty() { + return div(); + } + + let reactions: AtomicRef> = self.list.reactions.borrow(); + div().flex().gap_2().children(reactions.clone()) } } diff --git a/src/ui/src/app.rs b/src/ui/src/app.rs index 9652691..487e2cb 100644 --- a/src/ui/src/app.rs +++ b/src/ui/src/app.rs @@ -46,6 +46,13 @@ impl Render for App { let title_bar = components::TitleBar::new() .child(div().flex().flex_row().text_color(rgb(0xFFFFFF)).gap_2().child(img("brand/scope-round-200.png").w_6().h_6()).child("Scope")); - div().bg(cx.theme().background).w_full().h_full().flex().flex_col().child(title_bar).child(content) + div() + .bg(cx.theme().background) + .w_full() + .h_full() + .flex() + .flex_col() + .child(title_bar) + .child(content) } } diff --git a/src/ui/src/channel/mod.rs b/src/ui/src/channel/mod.rs index 9a3d567..3be064c 100644 --- a/src/ui/src/channel/mod.rs +++ b/src/ui/src/channel/mod.rs @@ -59,20 +59,12 @@ impl ChannelView { ctx .foreground_executor() .spawn(async move { + let mut l = channel_reaction_listener.resubscribe(); loop { - let (sender, receiver) = catty::oneshot(); - - let mut l = channel_reaction_listener.resubscribe(); - - tokio::spawn(async move { - sender.send(l.recv().await).unwrap(); - }); - - let reaction = receiver.await.unwrap().unwrap(); + let reaction = l.recv().await.unwrap(); async_model - .update(&mut async_ctx, |data, ctx| { - data.update_reaction(ctx, reaction); - ctx.notify(); + .update(&mut async_ctx, |data, cx| { + data.update_reaction(cx, reaction); }) .unwrap(); } From 0ed05329ec71e58e8f4e791d16ecbb71ef443c1f Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 12:18:01 +0100 Subject: [PATCH 12/18] chore: cleanup --- src/discord/src/message/reaction_list.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 566dee6..fa5fb12 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -1,6 +1,6 @@ use crate::client::DiscordClient; use crate::message::reaction::{DiscordMessageReaction, ReactionData}; -use gpui::{div, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, RenderOnce, Styled, Window}; +use gpui::{div, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, Styled, Window}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; use serenity::all::{ChannelId, MessageId}; @@ -83,7 +83,7 @@ impl ReactionList for DiscordReactionList { } fn get_content(&self, cx: &mut App) -> impl IntoElement { - self.entity.get_or_init(|| cx.new(|cx| RenderableReactionList::new(self.clone()))).clone() + self.entity.get_or_init(|| cx.new(|_| RenderableReactionList::new(self.clone()))).clone() } } @@ -104,7 +104,7 @@ impl RenderableReactionList { } impl Render for RenderableReactionList { - fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement { + fn render(&mut self, _: &mut Window, _: &mut Context<'_, Self>) -> impl IntoElement { if self.list.reactions.borrow().is_empty() { return div(); } From 3a283fef43638700b96f45906459f048b6c558d6 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 14:30:38 +0100 Subject: [PATCH 13/18] feat: show who reacted to a message --- src/chat/src/reaction.rs | 15 +++++--- src/discord/src/client.rs | 14 +++++++ src/discord/src/message/reaction.rs | 48 +++++++++++++++++++----- src/discord/src/message/reaction_list.rs | 22 ++++++++--- src/ui/src/channel/message_list.rs | 2 +- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/chat/src/reaction.rs b/src/chat/src/reaction.rs index a5433d7..efcd5b1 100644 --- a/src/chat/src/reaction.rs +++ b/src/chat/src/reaction.rs @@ -1,7 +1,7 @@ -use std::fmt::{Debug, Formatter}; -use std::sync::Arc; use atomic_refcell::AtomicRefCell; use gpui::{App, IntoElement, Rgba}; +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; pub type ReactionEvent = (T, ReactionOperation); @@ -14,7 +14,12 @@ pub enum MessageReactionType { #[derive(Clone, PartialEq)] pub enum ReactionEmoji { Simple(String), - Custom { url: String, animated: bool, name: Option, id: u64 }, + Custom { + url: String, + animated: bool, + name: Option, + id: u64, + }, } impl Debug for ReactionEmoji { @@ -27,7 +32,6 @@ impl Debug for ReactionEmoji { } pub trait MessageReaction: IntoElement { - fn get_count(&self, kind: Option) -> u64; fn get_self_reaction(&self) -> Option; fn get_emoji(&self) -> ReactionEmoji; @@ -43,11 +47,12 @@ pub enum ReactionOperation { RemoveSelf(ReactionEmoji), RemoveAll, RemoveEmoji(ReactionEmoji), + SetMembers(ReactionEmoji, Vec), } pub trait ReactionList { fn get_reactions(&self) -> &Arc>>; fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize); - fn apply(&mut self, operation: ReactionOperation); + fn apply(&mut self, operation: ReactionOperation, app: &mut App); fn get_content(&self, cx: &mut App) -> impl IntoElement; } diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index 2e23400..3bec39b 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -157,14 +157,28 @@ impl DiscordClient { } } + pub async fn load_users_reacting_to(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { + let reactions = channel_id.reaction_users(self.discord().http.clone(), message_id, Self::emoji_to_serenity(&emoji), Some(5), None).await; + if reactions.is_err() {return;} + let reactions = reactions.unwrap().iter().map(|user|user.name.clone()).collect(); + + self.send_reaction_operation(channel_id, message_id, ReactionOperation::SetMembers(emoji, reactions)).await; + } + pub async fn add_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { let reaction_type = Self::emoji_to_serenity(&emoji); channel_id.create_reaction(self.discord().http.clone(), message_id, reaction_type).await.unwrap(); + + // Refresh reactions in UI + self.load_users_reacting_to(channel_id, message_id, emoji).await; } pub async fn remove_reaction(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { let reaction_type = Self::emoji_to_serenity(&emoji); channel_id.delete_reaction(self.discord().http.clone(), message_id, None, reaction_type).await.unwrap(); + + // Refresh reactions in UI + self.load_users_reacting_to(channel_id, message_id, emoji).await; } } diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 165d181..1f0f9ac 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -1,12 +1,16 @@ use crate::client::DiscordClient; use components::theme::ActiveTheme; +use components::tooltip::Tooltip; use gpui::prelude::FluentBuilder; -use gpui::{div, img, px, AnyElement, App, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, StatefulInteractiveElement, Styled}; +use gpui::{ + div, img, px, AnyElement, App, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, + StatefulInteractiveElement, Styled, +}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; use serenity::all::{ChannelId, MessageId, ReactionType}; use std::fmt::Debug; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use MessageReactionType::Burst; #[derive(Clone, Debug)] @@ -43,6 +47,7 @@ pub struct DiscordMessageReaction { pub(crate) client: Arc, pub(crate) message_id: MessageId, pub(crate) channel_id: ChannelId, + pub(crate) users: Arc>>>, } impl DiscordMessageReaction { @@ -52,6 +57,7 @@ impl DiscordMessageReaction { client, message_id, channel_id, + users: Arc::new(Mutex::new(None)), } } @@ -83,13 +89,15 @@ impl DiscordMessageReaction { fn handle_click(&self, app: &App) { let reaction = self.clone(); let had_reaction = reaction.get_self_reaction().is_some(); - app.spawn(|_| async move { - if had_reaction { - reaction.client.remove_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; - } else { - reaction.client.add_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; - } - }).detach(); + app + .spawn(|_| async move { + if had_reaction { + reaction.client.remove_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; + } else { + reaction.client.add_reaction(reaction.channel_id, reaction.message_id, reaction.get_emoji()).await; + } + }) + .detach(); } } @@ -171,6 +179,12 @@ impl RenderOnce for DiscordMessageReaction { fn render(self, _: &mut gpui::Window, cx: &mut App) -> impl IntoElement { let emoji = self.get_emoji(); let theme = cx.theme(); + + let channel = self.channel_id.clone(); + let message = self.message_id.clone(); + let closure_emoji = self.get_emoji(); + let client = self.client.clone(); + let users = self.users.clone(); div() .px_1() .py_px() @@ -189,6 +203,22 @@ impl RenderOnce for DiscordMessageReaction { .on_click(move |_, _, app| { self.handle_click(app); }) + .tooltip(move |win, cx| { + let guard = users.lock().unwrap(); + let text = if guard.is_some() { + guard.as_ref().clone().unwrap().join(", ") + } else { + "Loading...".to_string() + }; + Tooltip::new(text, win, cx) + }) + .on_hover(move |_, _, _| { + let client = client.clone(); + let emoji = closure_emoji.clone(); + tokio::spawn(async move { + client.load_users_reacting_to(channel, message, emoji).await; + }); + }) } } diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index fa5fb12..2ae17ac 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -1,12 +1,12 @@ use crate::client::DiscordClient; use crate::message::reaction::{DiscordMessageReaction, ReactionData}; +use atomic_refcell::{AtomicRef, AtomicRefCell}; use gpui::{div, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, Styled, Window}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji, ReactionList, ReactionOperation}; use serenity::all::{ChannelId, MessageId}; use std::fmt::Debug; -use std::sync::{Arc, OnceLock}; -use atomic_refcell::{AtomicRef, AtomicRefCell}; +use std::sync::{Arc, Mutex, OnceLock}; #[derive(Clone)] pub struct DiscordReactionList { @@ -14,17 +14,19 @@ pub struct DiscordReactionList { message_id: MessageId, channel_id: ChannelId, client: Arc, - entity: Arc>> + entity: Arc>>, } impl DiscordReactionList { pub fn new(reactions: Vec, channel_id: ChannelId, message_id: MessageId, client: Arc) -> Self { DiscordReactionList { - reactions: Arc::new(AtomicRefCell::new(reactions.iter().map(|reaction| DiscordMessageReaction::new(reaction, client.clone(), message_id.clone(), channel_id.clone())).collect())), + reactions: Arc::new(AtomicRefCell::new( + reactions.iter().map(|reaction| DiscordMessageReaction::new(reaction, client.clone(), message_id.clone(), channel_id.clone())).collect(), + )), message_id, channel_id, client, - entity: Arc::new(OnceLock::new()) + entity: Arc::new(OnceLock::new()), } } } @@ -52,6 +54,7 @@ impl ReactionList for DiscordReactionList { client: self.client.clone(), message_id: self.message_id.clone(), channel_id: self.channel_id.clone(), + users: Arc::new(Mutex::new(None)), }; reaction.increment(kind, user_is_self, by); @@ -59,7 +62,7 @@ impl ReactionList for DiscordReactionList { } } - fn apply(&mut self, operation: ReactionOperation) { + fn apply(&mut self, operation: ReactionOperation, cx: &mut App) { match operation { ReactionOperation::Add(emoji, ty) => { self.increment(&emoji, ty, false, 1); @@ -79,6 +82,13 @@ impl ReactionList for DiscordReactionList { ReactionOperation::RemoveEmoji(emoji) => { self.reactions.borrow_mut().retain(|reaction| reaction.get_emoji() != emoji); } + ReactionOperation::SetMembers(emoji, members) => { + if let Some(reaction) = self.reactions.borrow_mut().iter_mut().find(|reaction| reaction.get_emoji() == emoji) { + let mut reactions = reaction.users.lock().unwrap(); + reactions.replace(members); + self.entity.get().as_ref().map(|entity| cx.notify(entity.entity_id())); + } + } } } diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index b4ccc64..3a0042d 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -122,7 +122,7 @@ where if let Element::Resolved(Some(haystack)) = item { if haystack.get_identifier() == Some(reaction.0) { if let Some(reactions) = haystack.get_reactions() { - reactions.apply(reaction.1); + reactions.apply(reaction.1, cx); } cx.notify(); From 8674c51462e4eeecdf87950c3d970b6264f09022 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 16:10:38 +0100 Subject: [PATCH 14/18] chore: make clippy happy --- src/discord/src/message/mod.rs | 4 ++-- src/discord/src/message/reaction.rs | 6 +++--- src/discord/src/message/reaction_list.rs | 10 ++++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index f7785e2..98ba694 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -49,7 +49,7 @@ impl DiscordMessage { } .unwrap(); - let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); + let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id, client.clone()); Self { client, @@ -65,7 +65,7 @@ impl DiscordMessage { channel: Arc, member: Option>, ) -> Self { - let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id.clone(), client.clone()); + let reactions = DiscordReactionList::new(msg.reactions.clone(), channel.id(), msg.id, client.clone()); Self { client, channel, diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 1f0f9ac..073ac89 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -180,8 +180,8 @@ impl RenderOnce for DiscordMessageReaction { let emoji = self.get_emoji(); let theme = cx.theme(); - let channel = self.channel_id.clone(); - let message = self.message_id.clone(); + let channel = self.channel_id; + let message = self.message_id; let closure_emoji = self.get_emoji(); let client = self.client.clone(); let users = self.users.clone(); @@ -206,7 +206,7 @@ impl RenderOnce for DiscordMessageReaction { .tooltip(move |win, cx| { let guard = users.lock().unwrap(); let text = if guard.is_some() { - guard.as_ref().clone().unwrap().join(", ") + guard.as_ref().unwrap().join(", ") } else { "Loading...".to_string() }; diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 2ae17ac..1a76ebb 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -21,7 +21,7 @@ impl DiscordReactionList { pub fn new(reactions: Vec, channel_id: ChannelId, message_id: MessageId, client: Arc) -> Self { DiscordReactionList { reactions: Arc::new(AtomicRefCell::new( - reactions.iter().map(|reaction| DiscordMessageReaction::new(reaction, client.clone(), message_id.clone(), channel_id.clone())).collect(), + reactions.iter().map(|reaction| DiscordMessageReaction::new(reaction, client.clone(), message_id, channel_id)).collect(), )), message_id, channel_id, @@ -52,8 +52,8 @@ impl ReactionList for DiscordReactionList { burst_colours: vec![], }, client: self.client.clone(), - message_id: self.message_id.clone(), - channel_id: self.channel_id.clone(), + message_id: self.message_id, + channel_id: self.channel_id, users: Arc::new(Mutex::new(None)), }; @@ -86,7 +86,9 @@ impl ReactionList for DiscordReactionList { if let Some(reaction) = self.reactions.borrow_mut().iter_mut().find(|reaction| reaction.get_emoji() == emoji) { let mut reactions = reaction.users.lock().unwrap(); reactions.replace(members); - self.entity.get().as_ref().map(|entity| cx.notify(entity.entity_id())); + if let Some(entity) = self.entity.get() { + cx.notify(entity.entity_id()); + } } } } From bf4143a45576f27424a77f41621c333b6eb7f6f3 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 16:22:08 +0100 Subject: [PATCH 15/18] feat: add ellipsis when more than 5 users reacted --- src/discord/src/client.rs | 2 +- src/discord/src/message/reaction.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index 3bec39b..b06aa61 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -158,7 +158,7 @@ impl DiscordClient { } pub async fn load_users_reacting_to(&self, channel_id: ChannelId, message_id: MessageId, emoji: ReactionEmoji) { - let reactions = channel_id.reaction_users(self.discord().http.clone(), message_id, Self::emoji_to_serenity(&emoji), Some(5), None).await; + let reactions = channel_id.reaction_users(self.discord().http.clone(), message_id, Self::emoji_to_serenity(&emoji), Some(6), None).await; if reactions.is_err() {return;} let reactions = reactions.unwrap().iter().map(|user|user.name.clone()).collect(); diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index 073ac89..e950045 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -2,10 +2,7 @@ use crate::client::DiscordClient; use components::theme::ActiveTheme; use components::tooltip::Tooltip; use gpui::prelude::FluentBuilder; -use gpui::{ - div, img, px, AnyElement, App, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, - StatefulInteractiveElement, Styled, -}; +use gpui::{div, img, px, AnyElement, App, InteractiveElement, IntoElement, ParentElement, RenderOnce, Rgba, StatefulInteractiveElement, Styled}; use scope_chat::reaction::MessageReactionType::Normal; use scope_chat::reaction::{MessageReaction, MessageReactionType, ReactionEmoji}; use serenity::all::{ChannelId, MessageId, ReactionType}; @@ -206,7 +203,13 @@ impl RenderOnce for DiscordMessageReaction { .tooltip(move |win, cx| { let guard = users.lock().unwrap(); let text = if guard.is_some() { - guard.as_ref().unwrap().join(", ") + let vec = guard.as_ref().unwrap(); + let first5 = vec.iter().take(5).cloned().collect::>().join(", "); + if vec.len() > 5 { + format!("{}...", first5) + } else { + first5 + } } else { "Loading...".to_string() }; From f6f6ed8c4e447d8533880473b60059c405754a46 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 29 Jan 2025 16:24:45 +0100 Subject: [PATCH 16/18] chore: make clippy happy --- src/discord/src/message/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index 98ba694..5a4f0a4 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -80,7 +80,7 @@ enum NonceState<'r> { Discord(&'r Option), } -impl<'r> PartialEq for NonceState<'r> { +impl PartialEq for NonceState<'_> { fn eq(&self, other: &Self) -> bool { match (self, other) { // comparing anything with `None` means they are not equal From 9956f254058e29a3748e6b1d0ba01db959821803 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Thu, 30 Jan 2025 00:06:14 +0100 Subject: [PATCH 17/18] fix: error with deleting reactions --- src/discord/src/message/reaction_list.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/discord/src/message/reaction_list.rs b/src/discord/src/message/reaction_list.rs index 1a76ebb..a03fd38 100644 --- a/src/discord/src/message/reaction_list.rs +++ b/src/discord/src/message/reaction_list.rs @@ -37,10 +37,11 @@ impl ReactionList for DiscordReactionList { } fn increment(&mut self, emoji: &ReactionEmoji, kind: MessageReactionType, user_is_self: bool, by: isize) { - if let Some(reaction) = self.reactions.borrow_mut().iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { + let mut reactions = self.reactions.borrow_mut(); + if let Some(reaction) = reactions.iter_mut().find(|reaction| reaction.get_emoji() == *emoji) { reaction.increment(kind, user_is_self, by); if reaction.get_count(None) == 0 { - self.reactions.borrow_mut().retain(|reaction| reaction.get_emoji() != *emoji); + reactions.retain(|reaction| reaction.get_emoji() != *emoji); } } else if by > 0 { let mut reaction = DiscordMessageReaction { @@ -58,7 +59,7 @@ impl ReactionList for DiscordReactionList { }; reaction.increment(kind, user_is_self, by); - self.reactions.borrow_mut().push(reaction); + reactions.push(reaction); } } From 54239a109b9a9154a2566ff23c7bf4ed26185175 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 5 Feb 2025 15:30:39 +0100 Subject: [PATCH 18/18] feat: support twemoji Twemoji support can be enabled/disabled using the `twemoji` feature (enabled per default) --- Cargo.lock | 8 ++++++ assets/opensource-licenses/twemoji-assets | 21 +++++++++++++++ src/discord/Cargo.toml | 5 ++++ src/discord/src/message/reaction.rs | 8 +++++- src/ui/Cargo.toml | 5 +++- src/ui/src/assets.rs | 31 +++++++++++++++++++++++ src/ui/src/main.rs | 21 +++------------ 7 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 assets/opensource-licenses/twemoji-assets create mode 100644 src/ui/src/assets.rs diff --git a/Cargo.lock b/Cargo.lock index a30adf5..e6ddf48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5558,6 +5558,7 @@ dependencies = [ "scope-chat", "scope-util", "tokio", + "twemoji-assets", "ui", ] @@ -5587,6 +5588,7 @@ dependencies = [ "scope-chat", "serenity", "tokio", + "twemoji-assets", "ui", "url", ] @@ -6881,6 +6883,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "twemoji-assets" +version = "1.3.0+15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d025c23299766dbecaea9047ddf23e51ab324a42d8b1662aaa3347ff58161c" + [[package]] name = "typeid" version = "1.0.2" diff --git a/assets/opensource-licenses/twemoji-assets b/assets/opensource-licenses/twemoji-assets new file mode 100644 index 0000000..3d4cb1e --- /dev/null +++ b/assets/opensource-licenses/twemoji-assets @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Tim 'Piepmatz' Hesse + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/discord/Cargo.toml b/src/discord/Cargo.toml index fb394e9..434cc3a 100644 --- a/src/discord/Cargo.toml +++ b/src/discord/Cargo.toml @@ -3,6 +3,9 @@ name = "scope-backend-discord" version = "0.1.0" edition = "2021" +[features] +twemoji = ["twemoji-assets"] + [dependencies] gpui.workspace = true components.workspace = true @@ -18,3 +21,5 @@ atomic_refcell.workspace = true rand = "0.8.5" dashmap = "6.1.0" log = "0.4.22" + +twemoji-assets = { version = "1.3.0", optional = true } \ No newline at end of file diff --git a/src/discord/src/message/reaction.rs b/src/discord/src/message/reaction.rs index e950045..3ae0d3f 100644 --- a/src/discord/src/message/reaction.rs +++ b/src/discord/src/message/reaction.rs @@ -78,7 +78,13 @@ impl DiscordMessageReaction { } fn render_emoji(emoji: &ReactionEmoji) -> AnyElement { match emoji { - ReactionEmoji::Simple(character) => div().text_size(px(12f32)).child(character.clone()).into_any_element(), + ReactionEmoji::Simple(character) => { + if cfg!(feature = "twemoji") { + img(format!("twemoji/{}", character)).w(px(16f32)).h(px(16f32)).into_any_element() + } else { + div().text_size(px(12f32)).child(character.clone()).into_any_element() + } + } ReactionEmoji::Custom { url, .. } => img(url.clone()).w(px(16f32)).h(px(16f32)).into_any_element(), } } diff --git a/src/ui/Cargo.toml b/src/ui/Cargo.toml index 0093a22..a70988c 100644 --- a/src/ui/Cargo.toml +++ b/src/ui/Cargo.toml @@ -29,6 +29,9 @@ rust-embed = "8.5.0" chrono.workspace = true catty = "0.1.5" +twemoji-assets = { version = "1.3.0", optional = true } + [features] -default = ["gpui/x11"] +default = ["gpui/x11", "twemoji"] wayland = ["gpui/wayland"] +twemoji = ["scope-backend-discord/twemoji", "twemoji-assets"] diff --git a/src/ui/src/assets.rs b/src/ui/src/assets.rs new file mode 100644 index 0000000..edeadc5 --- /dev/null +++ b/src/ui/src/assets.rs @@ -0,0 +1,31 @@ +use gpui::http_client::anyhow; +use gpui::{AssetSource, SharedString}; + +#[cfg(feature = "twemoji")] +use twemoji_assets::svg::SvgTwemojiAsset; + +#[derive(rust_embed::RustEmbed)] +#[folder = "../../assets"] +pub(crate) struct Assets; + +impl AssetSource for Assets { + fn load(&self, path: &str) -> gpui::Result>> { + #[cfg(feature = "twemoji")] + if path.starts_with("twemoji/") { + let path = path.strip_prefix("twemoji/").unwrap(); + + let data = SvgTwemojiAsset::from_emoji(path) + .map(|f| f.data.0) + .map(str::as_bytes) + .map(std::borrow::Cow::Borrowed); + + return Ok(data); + } + + Self::get(path).map(|f| Some(f.data)).ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path)) + } + + fn list(&self, path: &str) -> gpui::Result> { + Ok(Self::iter().filter_map(|p| if p.starts_with(path) { Some(p.into()) } else { None }).collect()) + } +} diff --git a/src/ui/src/main.rs b/src/ui/src/main.rs index 80b01d9..5f25480 100644 --- a/src/ui/src/main.rs +++ b/src/ui/src/main.rs @@ -1,30 +1,15 @@ pub mod actions; pub mod app; pub mod app_state; +mod assets; pub mod channel; pub mod menu; -use std::sync::Arc; - use app_state::AppState; use components::theme::{hsl, Theme, ThemeColor, ThemeMode}; use gpui::*; -use http_client::anyhow; use menu::app_menus; - -#[derive(rust_embed::RustEmbed)] -#[folder = "../../assets"] -struct Assets; - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - Self::get(path).map(|f| Some(f.data)).ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path)) - } - - fn list(&self, path: &str) -> Result> { - Ok(Self::iter().filter_map(|p| if p.starts_with(path) { Some(p.into()) } else { None }).collect()) - } -} +use std::sync::Arc; fn init(_: Arc, cx: &mut App) -> Result<()> { components::init(cx); @@ -50,7 +35,7 @@ async fn main() { let app_state = Arc::new(AppState {}); - Application::new().with_assets(Assets).with_http_client(Arc::new(reqwest_client::ReqwestClient::new())).run(move |app: &mut App| { + Application::new().with_assets(assets::Assets).with_http_client(Arc::new(reqwest_client::ReqwestClient::new())).run(move |app: &mut App| { AppState::set_global(Arc::downgrade(&app_state), app); if let Err(e) = init(app_state.clone(), app) {