2022-02-14 07:53:21 +00:00
# include "chatmessage.hpp"
2024-01-19 01:30:54 +00:00
# include <unordered_map>
# include <gtkmm/flowbox.h>
# include "abaddon.hpp"
2023-03-16 00:19:54 +00:00
# include "constants.hpp"
2021-01-17 04:49:27 +00:00
# include "lazyimage.hpp"
2023-03-16 00:19:54 +00:00
# include "misc/chatutil.hpp"
2024-01-19 01:30:54 +00:00
# include "util.hpp"
2020-08-26 05:46:43 +00:00
2021-07-05 05:57:55 +00:00
ChatMessageItemContainer : : ChatMessageItemContainer ( )
: m_main ( Gtk : : ORIENTATION_VERTICAL ) {
add ( m_main ) ;
2020-08-28 22:21:08 +00:00
2020-11-06 05:20:31 +00:00
m_link_menu_copy = Gtk : : manage ( new Gtk : : MenuItem ( " Copy Link " ) ) ;
m_link_menu_copy - > signal_activate ( ) . connect ( sigc : : mem_fun ( * this , & ChatMessageItemContainer : : on_link_menu_copy ) ) ;
m_link_menu . append ( * m_link_menu_copy ) ;
m_link_menu . show_all ( ) ;
2020-08-30 06:00:56 +00:00
}
2021-06-10 19:27:32 +00:00
ChatMessageItemContainer * ChatMessageItemContainer : : FromMessage ( const Message & data ) {
2020-09-26 03:02:13 +00:00
auto * container = Gtk : : manage ( new ChatMessageItemContainer ) ;
2021-06-10 19:27:32 +00:00
container - > ID = data . ID ;
container - > ChannelID = data . ChannelID ;
2020-08-30 06:00:56 +00:00
2021-06-10 19:27:32 +00:00
if ( data . Nonce . has_value ( ) )
container - > Nonce = * data . Nonce ;
2021-04-03 06:40:37 +00:00
2022-04-06 02:01:53 +00:00
if ( ! data . Content . empty ( ) | | data . Type ! = MessageType : : DEFAULT ) {
2021-06-10 19:27:32 +00:00
container - > m_text_component = container - > CreateTextComponent ( data ) ;
2021-07-05 05:57:55 +00:00
container - > m_main . add ( * container - > m_text_component ) ;
2020-09-24 06:43:27 +00:00
}
2020-08-30 06:00:56 +00:00
2021-06-10 19:27:32 +00:00
if ( ( data . MessageReference . has_value ( ) | | data . Interaction . has_value ( ) ) & & data . Type ! = MessageType : : CHANNEL_FOLLOW_ADD ) {
auto * widget = container - > CreateReplyComponent ( data ) ;
2021-07-26 06:47:20 +00:00
if ( widget ! = nullptr ) {
container - > m_main . add ( * widget ) ;
container - > m_main . child_property_position ( * widget ) = 0 ; // eek
}
2021-01-01 07:39:07 +00:00
}
2022-02-03 03:46:55 +00:00
if ( ! data . Embeds . empty ( ) ) {
2022-02-20 06:20:19 +00:00
container - > m_embed_component = container - > CreateEmbedsComponent ( data . Embeds ) ;
2022-02-03 03:46:55 +00:00
container - > m_main . add ( * container - > m_embed_component ) ;
2020-09-26 03:02:13 +00:00
}
2020-08-30 06:00:56 +00:00
2020-09-30 04:12:38 +00:00
// i dont think attachments can be edited
// also this can definitely be done much better holy shit
2021-06-10 19:27:32 +00:00
for ( const auto & a : data . Attachments ) {
2020-12-26 08:57:08 +00:00
if ( IsURLViewableImage ( a . ProxyURL ) & & a . Width . has_value ( ) & & a . Height . has_value ( ) ) {
auto * widget = container - > CreateImageComponent ( a . ProxyURL , a . URL , * a . Width , * a . Height ) ;
2023-12-14 02:59:45 +00:00
if ( a . Description . has_value ( ) ) {
widget - > set_tooltip_text ( * a . Description ) ;
}
2021-07-05 05:57:55 +00:00
container - > m_main . add ( * widget ) ;
2020-09-30 04:12:38 +00:00
} else {
auto * widget = container - > CreateAttachmentComponent ( a ) ;
2021-07-05 05:57:55 +00:00
container - > m_main . add ( * widget ) ;
2020-09-30 04:12:38 +00:00
}
}
2020-11-02 22:36:10 +00:00
// only 1?
2021-07-01 06:03:41 +00:00
/*
DEPRECATED
2021-06-10 19:27:32 +00:00
if ( data . Stickers . has_value ( ) ) {
const auto & sticker = data . Stickers . value ( ) [ 0 ] ;
2020-11-06 07:36:08 +00:00
// todo: lottie, proper apng
2020-11-02 22:36:10 +00:00
if ( sticker . FormatType = = StickerFormatType : : PNG | | sticker . FormatType = = StickerFormatType : : APNG ) {
auto * widget = container - > CreateStickerComponent ( sticker ) ;
container - > m_main - > add ( * widget ) ;
}
2021-07-01 06:03:41 +00:00
} */
if ( data . StickerItems . has_value ( ) ) {
auto * widget = container - > CreateStickersComponent ( * data . StickerItems ) ;
2021-07-05 05:57:55 +00:00
container - > m_main . add ( * widget ) ;
2020-11-02 22:36:10 +00:00
}
2022-04-06 02:01:53 +00:00
if ( data . Reactions . has_value ( ) & & ! data . Reactions - > empty ( ) ) {
2021-06-10 19:27:32 +00:00
container - > m_reactions_component = container - > CreateReactionsComponent ( data ) ;
2021-07-05 05:57:55 +00:00
container - > m_main . add ( * container - > m_reactions_component ) ;
2020-12-15 06:51:49 +00:00
}
2020-09-26 03:02:13 +00:00
container - > UpdateAttributes ( ) ;
2020-09-20 05:12:54 +00:00
2020-09-26 03:02:13 +00:00
return container ;
2020-09-20 05:12:54 +00:00
}
2020-09-30 04:12:38 +00:00
// this doesnt rly make sense
2020-09-26 03:02:13 +00:00
void ChatMessageItemContainer : : UpdateContent ( ) {
2020-11-24 01:34:09 +00:00
const auto data = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetMessage ( ID ) ;
2020-09-26 03:02:13 +00:00
if ( m_text_component ! = nullptr )
2020-10-05 06:09:15 +00:00
UpdateTextComponent ( m_text_component ) ;
2020-08-31 00:24:02 +00:00
2020-11-07 04:57:05 +00:00
if ( m_embed_component ! = nullptr ) {
2020-09-26 03:02:13 +00:00
delete m_embed_component ;
2020-11-07 04:57:05 +00:00
m_embed_component = nullptr ;
}
2020-08-29 05:14:07 +00:00
2022-02-20 06:20:19 +00:00
if ( ! data - > Embeds . empty ( ) ) {
m_embed_component = CreateEmbedsComponent ( data - > Embeds ) ;
2021-07-05 05:57:55 +00:00
m_main . add ( * m_embed_component ) ;
2022-02-20 06:20:19 +00:00
m_embed_component - > show_all ( ) ;
2020-09-26 03:02:13 +00:00
}
2020-08-31 00:24:02 +00:00
}
2020-12-15 06:51:49 +00:00
void ChatMessageItemContainer : : UpdateReactions ( ) {
2021-10-27 07:01:53 +00:00
if ( m_reactions_component ! = nullptr ) {
2020-12-15 06:51:49 +00:00
delete m_reactions_component ;
2021-10-27 07:01:53 +00:00
m_reactions_component = nullptr ;
}
2020-12-15 06:51:49 +00:00
const auto data = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetMessage ( ID ) ;
2022-04-06 02:01:53 +00:00
if ( data - > Reactions . has_value ( ) & & ! data - > Reactions - > empty ( ) ) {
2021-01-01 07:47:03 +00:00
m_reactions_component = CreateReactionsComponent ( * data ) ;
2020-12-15 06:51:49 +00:00
m_reactions_component - > show_all ( ) ;
2021-07-05 05:57:55 +00:00
m_main . add ( * m_reactions_component ) ;
2020-12-15 06:51:49 +00:00
}
}
2021-04-03 06:40:37 +00:00
void ChatMessageItemContainer : : SetFailed ( ) {
2021-05-07 08:09:07 +00:00
if ( m_text_component ! = nullptr ) {
m_text_component - > get_style_context ( ) - > remove_class ( " pending " ) ;
m_text_component - > get_style_context ( ) - > add_class ( " failed " ) ;
}
2021-04-03 06:40:37 +00:00
}
2020-09-26 03:02:13 +00:00
void ChatMessageItemContainer : : UpdateAttributes ( ) {
2020-11-24 01:34:09 +00:00
const auto data = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetMessage ( ID ) ;
if ( ! data . has_value ( ) ) return ;
2020-09-03 05:54:40 +00:00
2020-09-26 03:02:13 +00:00
const bool deleted = data - > IsDeleted ( ) ;
const bool edited = data - > IsEdited ( ) ;
if ( ! deleted & & ! edited ) return ;
2020-09-03 05:54:40 +00:00
2020-09-26 03:02:13 +00:00
if ( m_attrib_label = = nullptr ) {
m_attrib_label = Gtk : : manage ( new Gtk : : Label ) ;
m_attrib_label - > set_halign ( Gtk : : ALIGN_START ) ;
m_attrib_label - > show ( ) ;
2021-07-05 05:57:55 +00:00
m_main . add ( * m_attrib_label ) ; // todo: maybe insert markup into existing text widget's buffer if the circumstances are right (or pack horizontally)
2020-09-26 03:02:13 +00:00
}
2020-09-20 05:12:54 +00:00
2020-09-26 03:02:13 +00:00
if ( deleted )
m_attrib_label - > set_markup ( " <span color='#ff0000'>[deleted]</span> " ) ;
else if ( edited )
m_attrib_label - > set_markup ( " <span color='#999999'>[edited]</span> " ) ;
2020-09-03 05:54:40 +00:00
}
2022-04-06 02:01:53 +00:00
void ChatMessageItemContainer : : AddClickHandler ( Gtk : : Widget * widget , const std : : string & url ) {
2020-09-30 04:12:38 +00:00
// clang-format off
2022-09-25 05:44:09 +00:00
widget - > signal_button_release_event ( ) . connect ( [ url ] ( GdkEventButton * event ) - > bool {
if ( event - > type = = GDK_BUTTON_RELEASE & & event - > button = = GDK_BUTTON_PRIMARY ) {
2020-09-30 04:12:38 +00:00
LaunchBrowser ( url ) ;
2022-08-12 00:53:02 +00:00
return true ;
2020-09-30 04:12:38 +00:00
}
2022-08-12 00:53:02 +00:00
return false ;
2020-09-30 04:12:38 +00:00
} , false ) ;
// clang-format on
}
2021-06-10 19:27:32 +00:00
Gtk : : TextView * ChatMessageItemContainer : : CreateTextComponent ( const Message & data ) {
2020-09-26 03:02:13 +00:00
auto * tv = Gtk : : manage ( new Gtk : : TextView ) ;
2020-09-03 05:54:40 +00:00
2021-06-10 19:27:32 +00:00
if ( data . IsPending )
2021-04-03 06:40:37 +00:00
tv - > get_style_context ( ) - > add_class ( " pending " ) ;
2020-09-26 03:02:13 +00:00
tv - > get_style_context ( ) - > add_class ( " message-text " ) ;
2023-01-29 00:05:57 +00:00
tv - > set_can_focus ( true ) ;
tv - > set_cursor_visible ( false ) ;
2020-09-26 03:02:13 +00:00
tv - > set_editable ( false ) ;
tv - > set_wrap_mode ( Gtk : : WRAP_WORD_CHAR ) ;
tv - > set_halign ( Gtk : : ALIGN_FILL ) ;
tv - > set_hexpand ( true ) ;
2020-09-26 04:49:25 +00:00
2022-08-12 00:53:02 +00:00
tv - > signal_button_press_event ( ) . connect ( sigc : : mem_fun ( * this , & ChatMessageItemContainer : : OnTextViewButtonPress ) , false ) ;
2020-10-05 06:09:15 +00:00
UpdateTextComponent ( tv ) ;
return tv ;
}
void ChatMessageItemContainer : : UpdateTextComponent ( Gtk : : TextView * tv ) {
2020-11-24 01:34:09 +00:00
const auto data = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetMessage ( ID ) ;
if ( ! data . has_value ( ) ) return ;
2020-10-05 06:09:15 +00:00
2020-09-26 04:49:25 +00:00
auto b = tv - > get_buffer ( ) ;
2020-10-05 06:09:15 +00:00
b - > set_text ( " " ) ;
Gtk : : TextBuffer : : iterator s , e ;
2020-09-26 04:49:25 +00:00
b - > get_bounds ( s , e ) ;
switch ( data - > Type ) {
case MessageType : : DEFAULT :
2020-12-21 07:29:00 +00:00
case MessageType : : INLINE_REPLY :
2020-11-10 06:00:53 +00:00
b - > insert ( s , data - > Content ) ;
2023-03-16 00:19:54 +00:00
ChatUtil : : HandleRoleMentions ( b ) ;
ChatUtil : : HandleUserMentions ( b , ChannelID , false ) ;
2021-01-01 07:47:03 +00:00
HandleLinks ( * tv ) ;
2020-10-11 03:38:58 +00:00
HandleChannelMentions ( tv ) ;
2023-03-16 00:19:54 +00:00
ChatUtil : : HandleEmojis ( * tv ) ;
2020-09-26 04:49:25 +00:00
break ;
2021-04-18 03:58:09 +00:00
case MessageType : : USER_PREMIUM_GUILD_SUBSCRIPTION :
b - > insert_markup ( s , " <span color='#999999'><i>[boosted server]</i></span> " ) ;
break ;
2020-09-26 04:49:25 +00:00
case MessageType : : GUILD_MEMBER_JOIN :
b - > insert_markup ( s , " <span color='#999999'><i>[user joined]</i></span> " ) ;
break ;
case MessageType : : CHANNEL_PINNED_MESSAGE :
b - > insert_markup ( s , " <span color='#999999'><i>[message pinned]</i></span> " ) ;
break ;
2020-12-23 02:18:39 +00:00
case MessageType : : APPLICATION_COMMAND : {
if ( data - > Application . has_value ( ) ) {
static const auto regex = Glib : : Regex : : create ( R " (</(.*?):( \ d+)>) " ) ;
Glib : : MatchInfo match ;
2023-07-25 19:56:22 +00:00
Glib : : ustring string = data - > Content ;
if ( regex - > match ( string , match ) ) {
2020-12-23 02:18:39 +00:00
const auto cmd = match . fetch ( 1 ) ;
const auto app = data - > Application - > Name ;
b - > insert_markup ( s , " <i>used <span color='#697ec4'> " + cmd + " </span> with " + app + " </i> " ) ;
}
2021-04-04 08:09:56 +00:00
} else {
b - > insert ( s , data - > Content ) ;
2023-03-16 00:19:54 +00:00
ChatUtil : : HandleUserMentions ( b , ChannelID , false ) ;
2021-04-04 08:09:56 +00:00
HandleLinks ( * tv ) ;
HandleChannelMentions ( tv ) ;
2023-03-16 00:19:54 +00:00
ChatUtil : : HandleEmojis ( * tv ) ;
2020-12-23 02:18:39 +00:00
}
} break ;
2020-12-23 03:32:05 +00:00
case MessageType : : RECIPIENT_ADD : {
2022-04-06 02:01:53 +00:00
if ( data - > Mentions . empty ( ) ) break ;
2020-12-23 03:32:05 +00:00
const auto & adder = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
const auto & added = data - > Mentions [ 0 ] ;
2023-06-14 06:04:39 +00:00
b - > insert_markup ( s , " <i><span color='#999999'><span color='#eeeeee'> " + adder - > GetUsername ( ) + " </span> added <span color='#eeeeee'> " + added . GetUsername ( ) + " </span></span></i> " ) ;
2020-12-23 03:32:05 +00:00
} break ;
case MessageType : : RECIPIENT_REMOVE : {
2022-04-06 02:01:53 +00:00
if ( data - > Mentions . empty ( ) ) break ;
2020-12-23 03:32:05 +00:00
const auto & adder = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
const auto & added = data - > Mentions [ 0 ] ;
if ( adder - > ID = = added . ID )
2023-06-14 06:04:39 +00:00
b - > insert_markup ( s , " <i><span color='#999999'><span color='#eeeeee'> " + adder - > GetUsername ( ) + " </span> left</span></i> " ) ;
2020-12-23 03:32:05 +00:00
else
2023-06-14 06:04:39 +00:00
b - > insert_markup ( s , " <i><span color='#999999'><span color='#eeeeee'> " + adder - > GetUsername ( ) + " </span> removed <span color='#eeeeee'> " + added . GetUsername ( ) + " </span></span></i> " ) ;
2020-12-23 03:32:05 +00:00
} break ;
2021-04-24 07:29:06 +00:00
case MessageType : : CHANNEL_NAME_CHANGE : {
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
2023-06-14 01:28:18 +00:00
b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " changed the name to <b> " + Glib : : Markup : : escape_text ( data - > Content ) + " </b></span></i> " ) ;
2021-04-24 07:29:06 +00:00
} break ;
2021-04-24 07:46:57 +00:00
case MessageType : : CHANNEL_ICON_CHANGE : {
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
2023-06-14 01:28:18 +00:00
b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " changed the channel icon</span></i> " ) ;
2021-04-24 07:46:57 +00:00
} break ;
2021-04-24 07:59:21 +00:00
case MessageType : : USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 :
case MessageType : : USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 :
case MessageType : : USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 : {
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
const auto guild = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetGuild ( * data - > GuildID ) ;
2023-06-14 01:28:18 +00:00
b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " just boosted the server <b> " + Glib : : Markup : : escape_text ( data - > Content ) + " </b> times! " +
2021-04-24 07:59:21 +00:00
Glib : : Markup : : escape_text ( guild - > Name ) + " has achieved <b>Level " + std : : to_string ( static_cast < int > ( data - > Type ) - 8 ) + " !</b></span></i> " ) ; // oo cheeky me !!!
} break ;
2021-04-24 08:11:26 +00:00
case MessageType : : CHANNEL_FOLLOW_ADD : {
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
2023-06-14 01:28:18 +00:00
b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " has added <b> " + Glib : : Markup : : escape_text ( data - > Content ) + " </b> to this channel. Its most important updates will show up here.</span></i> " ) ;
2021-04-24 08:11:26 +00:00
} break ;
2021-04-24 08:23:28 +00:00
case MessageType : : CALL : {
b - > insert_markup ( s , " <span color='#999999'><i>[started a call]</i></span> " ) ;
} break ;
2021-04-29 02:00:40 +00:00
case MessageType : : GUILD_DISCOVERY_DISQUALIFIED : {
b - > insert_markup ( s , " <i><span color='#999999'>This server has been removed from Server Discovery because it no longer passes all the requirements.</span></i> " ) ;
} break ;
case MessageType : : GUILD_DISCOVERY_REQUALIFIED : {
b - > insert_markup ( s , " <i><span color='#999999'>This server is eligible for Server Discovery again and has been automatically relisted!</span></i> " ) ;
} break ;
case MessageType : : GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING : {
b - > insert_markup ( s , " <i><span color='#999999'>This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery.</span></i> " ) ;
} break ;
case MessageType : : GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING : {
b - > insert_markup ( s , " <i><span color='#999999'>This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery.</span></i> " ) ;
} break ;
2021-07-26 06:47:20 +00:00
case MessageType : : THREAD_CREATED : {
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( data - > Author . ID ) ;
2021-08-18 03:29:48 +00:00
if ( data - > MessageReference . has_value ( ) & & data - > MessageReference - > ChannelID . has_value ( ) ) {
2023-06-14 01:28:18 +00:00
auto iter = b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " started a thread: </span></i> " ) ;
2021-08-18 03:29:48 +00:00
auto tag = b - > create_tag ( ) ;
tag - > property_weight ( ) = Pango : : WEIGHT_BOLD ;
m_channel_tagmap [ tag ] = * data - > MessageReference - > ChannelID ;
b - > insert_with_tag ( iter , data - > Content , tag ) ;
} else {
2023-06-14 01:28:18 +00:00
b - > insert_markup ( s , " <i><span color='#999999'> " + author - > GetDisplayNameEscapedBold ( ) + " started a thread: </span><b> " + Glib : : Markup : : escape_text ( data - > Content ) + " </b></i> " ) ;
2021-08-18 03:29:48 +00:00
}
2021-07-26 06:47:20 +00:00
} break ;
2020-09-26 04:49:25 +00:00
default : break ;
}
2020-09-26 03:02:13 +00:00
}
2022-02-20 06:20:19 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateEmbedsComponent ( const std : : vector < EmbedData > & embeds ) {
auto * box = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_VERTICAL ) ) ;
for ( const auto & embed : embeds ) {
if ( IsEmbedImageOnly ( embed ) ) {
auto * widget = CreateImageComponent ( * embed . Thumbnail - > ProxyURL , * embed . Thumbnail - > URL , * embed . Thumbnail - > Width , * embed . Thumbnail - > Height ) ;
widget - > show ( ) ;
box - > add ( * widget ) ;
} else {
auto * widget = CreateEmbedComponent ( embed ) ;
widget - > show ( ) ;
box - > add ( * widget ) ;
}
}
return box ;
}
2020-12-26 08:57:08 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateEmbedComponent ( const EmbedData & embed ) {
2020-09-26 03:02:13 +00:00
Gtk : : EventBox * ev = Gtk : : manage ( new Gtk : : EventBox ) ;
ev - > set_can_focus ( true ) ;
2021-03-15 05:30:27 +00:00
Gtk : : Box * main = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_HORIZONTAL ) ) ;
Gtk : : Box * content = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_VERTICAL ) ) ;
if ( embed . Author . has_value ( ) & & ( embed . Author - > Name . has_value ( ) | | embed . Author - > ProxyIconURL . has_value ( ) ) ) {
auto * author_box = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_HORIZONTAL ) ) ;
content - > pack_start ( * author_box ) ;
constexpr static int AuthorIconSize = 20 ;
if ( embed . Author - > ProxyIconURL . has_value ( ) ) {
auto * author_img = Gtk : : manage ( new LazyImage ( * embed . Author - > ProxyIconURL , AuthorIconSize , AuthorIconSize ) ) ;
author_img - > set_halign ( Gtk : : ALIGN_START ) ;
author_img - > set_valign ( Gtk : : ALIGN_START ) ;
author_img - > set_margin_start ( 6 ) ;
author_img - > set_margin_end ( 6 ) ;
author_img - > get_style_context ( ) - > add_class ( " embed-author-icon " ) ;
author_box - > add ( * author_img ) ;
}
if ( embed . Author - > Name . has_value ( ) ) {
auto * author_lbl = Gtk : : manage ( new Gtk : : Label ) ;
author_lbl - > set_halign ( Gtk : : ALIGN_START ) ;
author_lbl - > set_valign ( Gtk : : ALIGN_CENTER ) ;
author_lbl - > set_line_wrap ( true ) ;
author_lbl - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
author_lbl - > set_hexpand ( false ) ;
author_lbl - > set_text ( * embed . Author - > Name ) ;
author_lbl - > get_style_context ( ) - > add_class ( " embed-author " ) ;
2023-01-29 00:05:57 +00:00
author_lbl - > set_selectable ( true ) ;
2021-03-15 05:30:27 +00:00
author_box - > add ( * author_lbl ) ;
}
2020-09-04 02:36:57 +00:00
}
2020-11-24 01:34:09 +00:00
if ( embed . Title . has_value ( ) ) {
2021-03-15 05:30:27 +00:00
auto * title_ev = Gtk : : manage ( new Gtk : : EventBox ) ;
2020-09-03 05:54:40 +00:00
auto * title_label = Gtk : : manage ( new Gtk : : Label ) ;
title_label - > set_use_markup ( true ) ;
2020-11-24 01:34:09 +00:00
title_label - > set_markup ( " <b> " + Glib : : Markup : : escape_text ( * embed . Title ) + " </b> " ) ;
2020-09-03 05:54:40 +00:00
title_label - > set_halign ( Gtk : : ALIGN_CENTER ) ;
2020-09-03 21:44:23 +00:00
title_label - > set_hexpand ( false ) ;
2020-09-09 22:32:45 +00:00
title_label - > get_style_context ( ) - > add_class ( " embed-title " ) ;
2020-09-26 03:02:13 +00:00
title_label - > set_single_line_mode ( false ) ;
title_label - > set_line_wrap ( true ) ;
title_label - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
title_label - > set_max_width_chars ( 50 ) ;
2023-01-29 00:05:57 +00:00
title_label - > set_selectable ( true ) ;
2021-03-15 05:30:27 +00:00
title_ev - > add ( * title_label ) ;
content - > pack_start ( * title_ev ) ;
if ( embed . URL . has_value ( ) ) {
AddPointerCursor ( * title_ev ) ;
auto url = * embed . URL ;
2022-09-25 05:44:09 +00:00
title_ev - > signal_button_release_event ( ) . connect ( [ url = std : : move ( url ) ] ( GdkEventButton * event ) - > bool {
if ( event - > type = = GDK_BUTTON_RELEASE & & event - > button = = GDK_BUTTON_PRIMARY ) {
2021-03-15 05:30:27 +00:00
LaunchBrowser ( url ) ;
return true ;
}
return false ;
} ) ;
2023-12-05 01:19:02 +00:00
const auto color = title_label - > get_style_context ( ) - > get_color ( Gtk : : STATE_FLAG_LINK ) ;
2021-03-15 05:30:27 +00:00
title_label - > override_color ( Gdk : : RGBA ( color ) ) ;
title_label - > set_markup ( " <b> " + Glib : : Markup : : escape_text ( * embed . Title ) + " </b> " ) ;
}
2020-09-03 05:54:40 +00:00
}
2020-12-26 08:57:08 +00:00
if ( ! embed . Provider . has_value ( ) | | embed . Provider - > Name ! = " YouTube " ) { // youtube link = no description
if ( embed . Description . has_value ( ) ) {
auto * desc_label = Gtk : : manage ( new Gtk : : Label ) ;
desc_label - > set_text ( * embed . Description ) ;
desc_label - > set_line_wrap ( true ) ;
desc_label - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
desc_label - > set_max_width_chars ( 50 ) ;
desc_label - > set_halign ( Gtk : : ALIGN_START ) ;
desc_label - > set_hexpand ( false ) ;
desc_label - > get_style_context ( ) - > add_class ( " embed-description " ) ;
2023-01-29 00:05:57 +00:00
desc_label - > set_selectable ( true ) ;
2021-03-15 05:30:27 +00:00
content - > pack_start ( * desc_label ) ;
2020-12-26 08:57:08 +00:00
}
2020-09-03 05:54:40 +00:00
}
2020-09-26 03:02:13 +00:00
// todo: handle inline fields
2022-04-06 02:01:53 +00:00
if ( embed . Fields . has_value ( ) & & ! embed . Fields - > empty ( ) ) {
2020-09-03 22:47:45 +00:00
auto * flow = Gtk : : manage ( new Gtk : : FlowBox ) ;
flow - > set_orientation ( Gtk : : ORIENTATION_HORIZONTAL ) ;
flow - > set_min_children_per_line ( 3 ) ;
2020-09-04 02:36:57 +00:00
flow - > set_max_children_per_line ( 3 ) ;
2020-09-03 22:47:45 +00:00
flow - > set_halign ( Gtk : : ALIGN_START ) ;
flow - > set_hexpand ( false ) ;
flow - > set_column_spacing ( 10 ) ;
2020-09-04 02:36:57 +00:00
flow - > set_selection_mode ( Gtk : : SELECTION_NONE ) ;
2021-03-15 05:30:27 +00:00
content - > pack_start ( * flow ) ;
2020-09-03 22:47:45 +00:00
2020-11-24 01:34:09 +00:00
for ( const auto & field : * embed . Fields ) {
2020-09-03 22:47:45 +00:00
auto * field_box = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_VERTICAL ) ) ;
auto * field_lbl = Gtk : : manage ( new Gtk : : Label ) ;
auto * field_val = Gtk : : manage ( new Gtk : : Label ) ;
field_box - > set_hexpand ( false ) ;
field_box - > set_halign ( Gtk : : ALIGN_START ) ;
2020-09-04 02:36:57 +00:00
field_box - > set_valign ( Gtk : : ALIGN_START ) ;
2020-09-03 22:47:45 +00:00
field_lbl - > set_hexpand ( false ) ;
field_lbl - > set_halign ( Gtk : : ALIGN_START ) ;
2020-09-04 02:36:57 +00:00
field_lbl - > set_valign ( Gtk : : ALIGN_START ) ;
2020-09-03 22:47:45 +00:00
field_val - > set_hexpand ( false ) ;
field_val - > set_halign ( Gtk : : ALIGN_START ) ;
2020-09-04 02:36:57 +00:00
field_val - > set_valign ( Gtk : : ALIGN_START ) ;
2020-09-03 22:47:45 +00:00
field_lbl - > set_use_markup ( true ) ;
field_lbl - > set_markup ( " <b> " + Glib : : Markup : : escape_text ( field . Name ) + " </b> " ) ;
field_lbl - > set_max_width_chars ( 20 ) ;
field_lbl - > set_line_wrap ( true ) ;
field_lbl - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
field_val - > set_text ( field . Value ) ;
field_val - > set_max_width_chars ( 20 ) ;
field_val - > set_line_wrap ( true ) ;
field_val - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
field_box - > pack_start ( * field_lbl ) ;
field_box - > pack_start ( * field_val ) ;
2020-09-09 22:32:45 +00:00
field_lbl - > get_style_context ( ) - > add_class ( " embed-field-title " ) ;
field_val - > get_style_context ( ) - > add_class ( " embed-field-value " ) ;
2023-01-29 00:05:57 +00:00
field_lbl - > set_selectable ( true ) ;
field_val - > set_selectable ( true ) ;
2020-09-03 22:47:45 +00:00
flow - > insert ( * field_box , - 1 ) ;
}
}
2021-03-15 05:30:27 +00:00
if ( embed . Image . has_value ( ) & & embed . Image - > ProxyURL . has_value ( ) ) {
2020-12-11 05:12:43 +00:00
int w = 0 , h = 0 ;
2024-01-12 02:50:13 +00:00
const int clamp_width = Abaddon : : Get ( ) . GetSettings ( ) . ImageEmbedClampWidth ;
const int clamp_height = Abaddon : : Get ( ) . GetSettings ( ) . ImageEmbedClampHeight ;
GetImageDimensions ( * embed . Image - > Width , * embed . Image - > Height , w , h , clamp_width , clamp_height ) ;
2021-03-15 05:30:27 +00:00
auto * img = Gtk : : manage ( new LazyImage ( * embed . Image - > ProxyURL , w , h , false ) ) ;
2021-01-17 04:49:27 +00:00
img - > set_halign ( Gtk : : ALIGN_CENTER ) ;
img - > set_margin_top ( 5 ) ;
img - > set_size_request ( w , h ) ;
2021-03-15 05:30:27 +00:00
content - > pack_start ( * img ) ;
2020-09-30 04:12:38 +00:00
}
2020-11-24 01:34:09 +00:00
if ( embed . Footer . has_value ( ) ) {
2020-09-04 02:36:57 +00:00
auto * footer_lbl = Gtk : : manage ( new Gtk : : Label ) ;
footer_lbl - > set_halign ( Gtk : : ALIGN_START ) ;
footer_lbl - > set_line_wrap ( true ) ;
footer_lbl - > set_line_wrap_mode ( Pango : : WRAP_WORD_CHAR ) ;
footer_lbl - > set_hexpand ( false ) ;
2020-11-24 01:34:09 +00:00
footer_lbl - > set_text ( embed . Footer - > Text ) ;
2020-09-04 02:36:57 +00:00
footer_lbl - > get_style_context ( ) - > add_class ( " embed-footer " ) ;
2023-01-29 00:05:57 +00:00
footer_lbl - > set_selectable ( true ) ;
2021-03-15 05:30:27 +00:00
content - > pack_start ( * footer_lbl ) ;
}
if ( embed . Thumbnail . has_value ( ) & & embed . Thumbnail - > ProxyURL . has_value ( ) ) {
int w , h ;
GetImageDimensions ( * embed . Thumbnail - > Width , * embed . Thumbnail - > Height , w , h , ThumbnailSize , ThumbnailSize ) ;
auto * thumbnail = Gtk : : manage ( new LazyImage ( * embed . Thumbnail - > ProxyURL , w , h , false ) ) ;
thumbnail - > set_size_request ( w , h ) ;
thumbnail - > set_margin_start ( 8 ) ;
main - > pack_end ( * thumbnail ) ;
2020-09-04 02:36:57 +00:00
}
2020-09-26 03:02:13 +00:00
auto style = main - > get_style_context ( ) ;
2020-09-03 05:54:40 +00:00
2020-11-24 01:34:09 +00:00
if ( embed . Color . has_value ( ) ) {
2020-09-03 05:54:40 +00:00
auto provider = Gtk : : CssProvider : : create ( ) ; // this seems wrong
2020-11-24 01:34:09 +00:00
std : : string css = " .embed { border-left: 2px solid # " + IntToCSSColor ( * embed . Color ) + " ; } " ;
2020-09-06 01:05:52 +00:00
provider - > load_from_data ( css ) ;
2020-09-03 05:54:40 +00:00
style - > add_provider ( provider , GTK_STYLE_PROVIDER_PRIORITY_APPLICATION ) ;
}
style - > add_class ( " embed " ) ;
2020-09-26 03:02:13 +00:00
main - > set_margin_bottom ( 8 ) ;
main - > set_hexpand ( false ) ;
main - > set_hexpand ( false ) ;
main - > set_halign ( Gtk : : ALIGN_START ) ;
main - > set_halign ( Gtk : : ALIGN_START ) ;
2021-03-15 05:30:27 +00:00
main - > pack_start ( * content ) ;
2020-09-03 05:54:40 +00:00
2020-09-26 03:02:13 +00:00
ev - > add ( * main ) ;
ev - > show_all ( ) ;
2020-09-03 05:54:40 +00:00
2020-09-26 03:02:13 +00:00
return ev ;
}
2020-09-03 05:54:40 +00:00
2020-12-26 08:57:08 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateImageComponent ( const std : : string & proxy_url , const std : : string & url , int inw , int inh ) {
2020-09-30 04:12:38 +00:00
int w , h ;
2024-01-12 02:50:13 +00:00
const int clamp_width = Abaddon : : Get ( ) . GetSettings ( ) . ImageEmbedClampWidth ;
const int clamp_height = Abaddon : : Get ( ) . GetSettings ( ) . ImageEmbedClampHeight ;
GetImageDimensions ( inw , inh , w , h , clamp_width , clamp_height ) ;
2020-09-30 04:12:38 +00:00
2020-11-06 07:36:08 +00:00
Gtk : : EventBox * ev = Gtk : : manage ( new Gtk : : EventBox ) ;
2021-01-17 04:49:27 +00:00
Gtk : : Image * widget = Gtk : : manage ( new LazyImage ( proxy_url , w , h , false ) ) ;
2020-11-06 07:36:08 +00:00
ev - > add ( * widget ) ;
2022-08-12 00:53:02 +00:00
ev - > set_halign ( Gtk : : ALIGN_START ) ;
2020-09-30 04:12:38 +00:00
widget - > set_halign ( Gtk : : ALIGN_START ) ;
widget - > set_size_request ( w , h ) ;
2020-12-26 08:57:08 +00:00
AddClickHandler ( ev , url ) ;
2020-09-30 04:12:38 +00:00
2022-08-12 00:53:02 +00:00
const auto on_button_press_event = [ this , url ] ( GdkEventButton * e ) - > bool {
if ( e - > type = = GDK_BUTTON_PRESS & & e - > button = = GDK_BUTTON_SECONDARY ) {
m_selected_link = url ;
m_link_menu . popup_at_pointer ( reinterpret_cast < GdkEvent * > ( e ) ) ;
return true ;
}
return false ;
} ;
ev - > signal_button_press_event ( ) . connect ( on_button_press_event , false ) ;
2020-11-06 07:36:08 +00:00
return ev ;
2020-09-30 04:12:38 +00:00
}
2020-11-06 07:36:08 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateAttachmentComponent ( const AttachmentData & data ) {
2020-09-30 04:12:38 +00:00
auto * ev = Gtk : : manage ( new Gtk : : EventBox ) ;
auto * btn = Gtk : : manage ( new Gtk : : Label ( data . Filename + " " + HumanReadableBytes ( data . Bytes ) ) ) ; // Gtk::LinkButton flat out doesn't work :D
2020-12-15 05:30:11 +00:00
ev - > set_hexpand ( false ) ;
ev - > set_halign ( Gtk : : ALIGN_START ) ;
2020-11-06 07:36:08 +00:00
ev - > get_style_context ( ) - > add_class ( " message-attachment-box " ) ;
2020-09-30 04:12:38 +00:00
ev - > add ( * btn ) ;
2020-11-06 07:36:08 +00:00
AddClickHandler ( ev , data . URL ) ;
2022-08-12 00:53:02 +00:00
const auto on_button_press_event = [ this , url = data . URL ] ( GdkEventButton * e ) - > bool {
if ( e - > type = = GDK_BUTTON_PRESS & & e - > button = = GDK_BUTTON_SECONDARY ) {
m_selected_link = url ;
m_link_menu . popup_at_pointer ( reinterpret_cast < GdkEvent * > ( e ) ) ;
return true ;
}
return false ;
} ;
ev - > signal_button_press_event ( ) . connect ( on_button_press_event , false ) ;
2020-11-06 07:36:08 +00:00
return ev ;
2020-09-30 04:12:38 +00:00
}
2021-07-01 06:03:41 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateStickersComponent ( const std : : vector < StickerItem > & data ) {
auto * box = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_VERTICAL ) ) ;
for ( const auto & sticker : data ) {
// no lottie
if ( sticker . FormatType ! = StickerFormatType : : PNG & & sticker . FormatType ! = StickerFormatType : : APNG ) continue ;
auto * ev = Gtk : : manage ( new Gtk : : EventBox ) ;
auto * img = Gtk : : manage ( new LazyImage ( sticker . GetURL ( ) , StickerComponentSize , StickerComponentSize , false ) ) ;
2022-02-27 05:52:52 +00:00
img - > set_halign ( Gtk : : ALIGN_START ) ;
2021-07-01 06:03:41 +00:00
img - > set_size_request ( StickerComponentSize , StickerComponentSize ) ; // should this go in LazyImage ?
img - > show ( ) ;
ev - > show ( ) ;
ev - > add ( * img ) ;
box - > add ( * ev ) ;
}
box - > show ( ) ;
return box ;
}
2021-01-01 07:47:03 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateReactionsComponent ( const Message & data ) {
2020-12-15 06:51:49 +00:00
auto * flow = Gtk : : manage ( new Gtk : : FlowBox ) ;
flow - > set_orientation ( Gtk : : ORIENTATION_HORIZONTAL ) ;
flow - > set_min_children_per_line ( 5 ) ;
flow - > set_max_children_per_line ( 20 ) ;
flow - > set_halign ( Gtk : : ALIGN_START ) ;
flow - > set_hexpand ( false ) ;
flow - > set_column_spacing ( 2 ) ;
flow - > set_selection_mode ( Gtk : : SELECTION_NONE ) ;
auto & imgr = Abaddon : : Get ( ) . GetImageManager ( ) ;
auto & emojis = Abaddon : : Get ( ) . GetEmojis ( ) ;
const auto & placeholder = imgr . GetPlaceholder ( 16 ) ;
2021-01-01 07:47:03 +00:00
for ( const auto & reaction : * data . Reactions ) {
2020-12-15 06:51:49 +00:00
auto * ev = Gtk : : manage ( new Gtk : : EventBox ) ;
auto * box = Gtk : : manage ( new Gtk : : Box ( Gtk : : ORIENTATION_HORIZONTAL ) ) ;
box - > get_style_context ( ) - > add_class ( " reaction-box " ) ;
ev - > add ( * box ) ;
flow - > add ( * ev ) ;
bool is_stock = ! reaction . Emoji . ID . IsValid ( ) ;
bool has_reacted = reaction . HasReactedWith ;
if ( has_reacted )
box - > get_style_context ( ) - > add_class ( " reacted " ) ;
ev - > signal_button_press_event ( ) . connect ( [ this , has_reacted , is_stock , reaction ] ( GdkEventButton * event ) - > bool {
if ( event - > type = = GDK_BUTTON_PRESS & & event - > button = = GDK_BUTTON_PRIMARY ) {
Glib : : ustring param ; // escaped in client
if ( is_stock )
param = reaction . Emoji . Name ;
else
param = std : : to_string ( reaction . Emoji . ID ) ;
if ( has_reacted )
m_signal_action_reaction_remove . emit ( param ) ;
else
m_signal_action_reaction_add . emit ( param ) ;
return true ;
}
return false ;
} ) ;
ev - > signal_realize ( ) . connect ( [ ev ] ( ) {
auto window = ev - > get_window ( ) ;
auto display = window - > get_display ( ) ;
auto cursor = Gdk : : Cursor : : create ( display , " pointer " ) ;
window - > set_cursor ( cursor ) ;
} ) ;
// image
if ( is_stock ) { // unicode/stock
2021-01-18 04:24:44 +00:00
const auto shortcode = emojis . GetShortCodeForPattern ( reaction . Emoji . Name ) ;
2022-04-06 02:01:53 +00:00
if ( ! shortcode . empty ( ) )
2021-01-18 04:24:44 +00:00
ev - > set_tooltip_text ( shortcode ) ;
2020-12-15 06:51:49 +00:00
const auto & pb = emojis . GetPixBuf ( reaction . Emoji . Name ) ;
2020-12-15 07:45:37 +00:00
Gtk : : Image * img ;
if ( pb )
img = Gtk : : manage ( new Gtk : : Image ( pb - > scale_simple ( 16 , 16 , Gdk : : INTERP_BILINEAR ) ) ) ;
else
img = Gtk : : manage ( new Gtk : : Image ( placeholder ) ) ;
2020-12-15 06:51:49 +00:00
img - > set_can_focus ( false ) ;
box - > add ( * img ) ;
} else { // custom
2021-01-18 04:24:44 +00:00
ev - > set_tooltip_text ( reaction . Emoji . Name ) ;
2023-11-05 00:54:15 +00:00
auto * img = Gtk : : make_managed < LazyImage > ( reaction . Emoji . GetURL ( ) , 16 , 16 ) ;
if ( reaction . Emoji . IsEmojiAnimated ( ) & & Abaddon : : Get ( ) . GetSettings ( ) . ShowAnimations ) {
img - > SetURL ( reaction . Emoji . GetURL ( " gif " ) ) ;
img - > SetAnimated ( true ) ;
}
2020-12-15 06:51:49 +00:00
img - > set_can_focus ( false ) ;
box - > add ( * img ) ;
}
auto * lbl = Gtk : : manage ( new Gtk : : Label ( std : : to_string ( reaction . Count ) ) ) ;
lbl - > set_margin_left ( 5 ) ;
lbl - > get_style_context ( ) - > add_class ( " reaction-count " ) ;
box - > add ( * lbl ) ;
}
return flow ;
}
2021-01-01 07:47:03 +00:00
Gtk : : Widget * ChatMessageItemContainer : : CreateReplyComponent ( const Message & data ) {
2021-07-26 06:47:20 +00:00
if ( data . Type = = MessageType : : THREAD_CREATED ) return nullptr ;
2021-01-01 07:39:07 +00:00
auto * box = Gtk : : manage ( new Gtk : : Box ) ;
auto * lbl = Gtk : : manage ( new Gtk : : Label ) ;
lbl - > set_single_line_mode ( true ) ;
lbl - > set_line_wrap ( false ) ;
lbl - > set_use_markup ( true ) ;
lbl - > set_ellipsize ( Pango : : ELLIPSIZE_END ) ;
lbl - > get_style_context ( ) - > add_class ( " message-text " ) ; // good idea?
lbl - > get_style_context ( ) - > add_class ( " message-reply " ) ;
box - > add ( * lbl ) ;
2021-04-04 08:09:56 +00:00
const auto & discord = Abaddon : : Get ( ) . GetDiscordClient ( ) ;
const auto get_author_markup = [ & ] ( Snowflake author_id , Snowflake guild_id = Snowflake : : Invalid ) - > std : : string {
if ( guild_id . IsValid ( ) ) {
const auto role_id = discord . GetMemberHoistedRole ( guild_id , author_id , true ) ;
if ( role_id . IsValid ( ) ) {
const auto role = discord . GetRole ( role_id ) ;
if ( role . has_value ( ) ) {
const auto author = discord . GetUser ( author_id ) ;
2022-09-09 06:40:33 +00:00
if ( author . has_value ( ) ) {
2023-12-04 06:50:16 +00:00
const auto is_mention = ! data . Interaction . has_value ( ) & & data . DoesMention ( author_id ) ;
if ( is_mention ) {
return " <b><span color= \" # " + IntToCSSColor ( role - > Color ) + " \" >@ " + author - > GetDisplayNameEscaped ( guild_id ) + " </span></b> " ;
} else {
return " <b><span color= \" # " + IntToCSSColor ( role - > Color ) + " \" > " + author - > GetDisplayNameEscaped ( guild_id ) + " </span></b> " ;
}
2022-09-09 06:40:33 +00:00
}
2021-04-04 08:09:56 +00:00
}
}
}
const auto author = discord . GetUser ( author_id ) ;
2022-09-09 06:40:33 +00:00
if ( author . has_value ( ) ) {
2023-06-14 02:15:48 +00:00
return author - > GetDisplayNameEscapedBold ( guild_id ) ;
2022-09-09 06:40:33 +00:00
}
return " <b>Unknown User</b> " ;
2021-04-04 08:09:56 +00:00
} ;
2021-11-16 07:37:12 +00:00
// if the message wasnt fetched from store it might have an un-fetched reference
std : : optional < std : : shared_ptr < Message > > referenced_message = data . ReferencedMessage ;
if ( data . MessageReference . has_value ( ) & & data . MessageReference - > MessageID . has_value ( ) & & ! referenced_message . has_value ( ) ) {
auto refd = discord . GetMessage ( * data . MessageReference - > MessageID ) ;
2023-07-13 22:53:47 +00:00
if ( refd . has_value ( ) ) {
2021-11-16 07:37:12 +00:00
referenced_message = std : : make_shared < Message > ( std : : move ( * refd ) ) ;
2023-07-13 22:53:47 +00:00
}
2021-11-16 07:37:12 +00:00
}
2021-04-04 08:09:56 +00:00
if ( data . Interaction . has_value ( ) ) {
2021-04-04 18:57:04 +00:00
if ( data . GuildID . has_value ( ) ) {
2022-09-09 06:40:33 +00:00
lbl - > set_markup ( get_author_markup ( data . Interaction - > User . ID , * data . GuildID ) +
2021-04-04 18:57:04 +00:00
" used <span color='#697ec4'>/ " +
Glib : : Markup : : escape_text ( data . Interaction - > Name ) +
" </span> " ) ;
2022-09-09 06:40:33 +00:00
} else if ( const auto user = discord . GetUser ( data . Interaction - > User . ID ) ; user . has_value ( ) ) {
2023-06-14 01:34:29 +00:00
lbl - > set_markup ( user - > GetDisplayNameEscapedBold ( ) ) ;
2021-04-04 18:57:04 +00:00
} else {
2022-09-09 06:40:33 +00:00
lbl - > set_markup ( " <b>Unknown User</b> " ) ;
2021-04-04 18:57:04 +00:00
}
2021-11-16 07:37:12 +00:00
} else if ( referenced_message . has_value ( ) ) {
if ( referenced_message . value ( ) = = nullptr ) {
2021-01-01 07:39:07 +00:00
lbl - > set_markup ( " <i>deleted message</i> " ) ;
} else {
2021-11-16 07:37:12 +00:00
const auto & referenced = * referenced_message . value ( ) ;
2021-01-01 07:39:07 +00:00
Glib : : ustring text ;
2021-11-16 07:37:12 +00:00
if ( referenced . Content . empty ( ) ) {
if ( ! referenced . Attachments . empty ( ) ) {
2021-01-01 07:39:07 +00:00
text = " <i>attachment</i> " ;
2021-11-16 07:37:12 +00:00
} else if ( ! referenced . Embeds . empty ( ) ) {
2021-01-01 07:39:07 +00:00
text = " <i>embed</i> " ;
}
} else {
auto buf = Gtk : : TextBuffer : : create ( ) ;
Gtk : : TextBuffer : : iterator start , end ;
buf - > get_bounds ( start , end ) ;
buf - > set_text ( referenced . Content ) ;
2023-03-16 00:19:54 +00:00
ChatUtil : : CleanupEmojis ( buf ) ;
ChatUtil : : HandleUserMentions ( buf , ChannelID , false ) ;
2021-01-01 07:39:07 +00:00
HandleChannelMentions ( buf ) ;
text = Glib : : Markup : : escape_text ( buf - > get_text ( ) ) ;
}
2023-12-04 06:50:16 +00:00
if ( referenced . GuildID . has_value ( ) ) {
2021-10-08 21:52:38 +00:00
lbl - > set_markup ( get_author_markup ( referenced . Author . ID , * referenced . GuildID ) + " : " + text ) ;
2023-12-04 06:50:16 +00:00
} else {
2021-10-08 21:52:38 +00:00
lbl - > set_markup ( get_author_markup ( referenced . Author . ID ) + " : " + text ) ;
2023-12-04 06:50:16 +00:00
}
2021-01-01 07:39:07 +00:00
}
} else {
lbl - > set_markup ( " <i>reply unavailable</i> " ) ;
}
return box ;
}
2020-12-26 08:57:08 +00:00
bool ChatMessageItemContainer : : IsEmbedImageOnly ( const EmbedData & data ) {
if ( ! data . Thumbnail . has_value ( ) ) return false ;
2023-09-09 00:44:03 +00:00
if ( data . Title . has_value ( ) ) return false ;
2020-12-26 08:57:08 +00:00
if ( data . Author . has_value ( ) ) return false ;
if ( data . Description . has_value ( ) ) return false ;
if ( data . Fields . has_value ( ) ) return false ;
if ( data . Footer . has_value ( ) ) return false ;
if ( data . Image . has_value ( ) ) return false ;
if ( data . Timestamp . has_value ( ) ) return false ;
return data . Thumbnail - > ProxyURL . has_value ( ) & & data . Thumbnail - > URL . has_value ( ) & & data . Thumbnail - > Width . has_value ( ) & & data . Thumbnail - > Height . has_value ( ) ;
}
2022-04-06 02:01:53 +00:00
void ChatMessageItemContainer : : HandleChannelMentions ( const Glib : : RefPtr < Gtk : : TextBuffer > & buf ) {
2021-01-01 07:39:07 +00:00
static auto rgx = Glib : : Regex : : create ( R " (<#( \ d+)>) " ) ;
2020-10-10 03:14:57 +00:00
2023-03-16 00:19:54 +00:00
Glib : : ustring text = ChatUtil : : GetText ( buf ) ;
2020-10-11 03:38:58 +00:00
const auto & discord = Abaddon : : Get ( ) . GetDiscordClient ( ) ;
2020-10-10 03:14:57 +00:00
2020-10-30 19:54:28 +00:00
int startpos = 0 ;
Glib : : MatchInfo match ;
while ( rgx - > match ( text , startpos , match ) ) {
int mstart , mend ;
match . fetch_pos ( 0 , mstart , mend ) ;
std : : string channel_id = match . fetch ( 1 ) ;
2020-12-10 08:50:40 +00:00
const auto chan = discord . GetChannel ( channel_id ) ;
if ( ! chan . has_value ( ) ) {
2020-10-30 19:54:28 +00:00
startpos = mend ;
2020-10-11 03:38:58 +00:00
continue ;
}
auto tag = buf - > create_tag ( ) ;
2021-07-21 07:33:52 +00:00
if ( chan - > Type = = ChannelType : : GUILD_TEXT ) {
m_channel_tagmap [ tag ] = channel_id ;
tag - > property_weight ( ) = Pango : : WEIGHT_BOLD ;
}
2020-10-11 03:38:58 +00:00
2020-10-30 19:54:28 +00:00
const auto chars_start = g_utf8_pointer_to_offset ( text . c_str ( ) , text . c_str ( ) + mstart ) ;
const auto chars_end = g_utf8_pointer_to_offset ( text . c_str ( ) , text . c_str ( ) + mend ) ;
const auto erase_from = buf - > get_iter_at_offset ( chars_start ) ;
const auto erase_to = buf - > get_iter_at_offset ( chars_end ) ;
2020-10-11 03:38:58 +00:00
auto it = buf - > erase ( erase_from , erase_to ) ;
2020-12-10 08:50:40 +00:00
const std : : string replacement = " # " + * chan - > Name ;
it = buf - > insert_with_tag ( it , " # " + * chan - > Name , tag ) ;
2020-10-11 03:38:58 +00:00
// rescan the whole thing so i dont have to deal with fixing match positions
2023-03-16 00:19:54 +00:00
text = ChatUtil : : GetText ( buf ) ;
2020-10-30 19:54:28 +00:00
startpos = 0 ;
2020-10-11 03:38:58 +00:00
}
}
2021-01-01 07:39:07 +00:00
void ChatMessageItemContainer : : HandleChannelMentions ( Gtk : : TextView * tv ) {
HandleChannelMentions ( tv - > get_buffer ( ) ) ;
}
2020-10-11 03:38:58 +00:00
// a lot of repetition here so there should probably just be one slot for textview's button-press
bool ChatMessageItemContainer : : OnClickChannel ( GdkEventButton * ev ) {
if ( m_text_component = = nullptr ) return false ;
2020-12-22 07:35:57 +00:00
if ( ev - > type ! = GDK_BUTTON_PRESS ) return false ;
2020-10-11 03:38:58 +00:00
if ( ev - > button ! = GDK_BUTTON_PRIMARY ) return false ;
auto buf = m_text_component - > get_buffer ( ) ;
Gtk : : TextBuffer : : iterator start , end ;
buf - > get_selection_bounds ( start , end ) ; // no open if selection
if ( start . get_offset ( ) ! = end . get_offset ( ) )
return false ;
int x , y ;
2022-04-06 02:01:53 +00:00
m_text_component - > window_to_buffer_coords ( Gtk : : TEXT_WINDOW_WIDGET , static_cast < int > ( ev - > x ) , static_cast < int > ( ev - > y ) , x , y ) ;
2020-10-11 03:38:58 +00:00
Gtk : : TextBuffer : : iterator iter ;
m_text_component - > get_iter_at_location ( iter , x , y ) ;
const auto tags = iter . get_tags ( ) ;
2022-04-06 02:01:53 +00:00
for ( const auto & tag : tags ) {
2020-10-11 03:38:58 +00:00
const auto it = m_channel_tagmap . find ( tag ) ;
if ( it ! = m_channel_tagmap . end ( ) ) {
m_signal_action_channel_click . emit ( it - > second ) ;
return true ;
}
}
return false ;
}
2022-08-12 00:53:02 +00:00
bool ChatMessageItemContainer : : OnTextViewButtonPress ( GdkEventButton * ev ) {
// run all button press handlers and propagate if none return true
if ( OnLinkClick ( ev ) ) return true ;
if ( OnClickChannel ( ev ) ) return true ;
if ( ev - > type = = GDK_BUTTON_PRESS & & ev - > button = = GDK_BUTTON_SECONDARY ) {
// send the event upward skipping TextView's handler because we dont want it
gtk_propagate_event ( GTK_WIDGET ( m_main . gobj ( ) ) , reinterpret_cast < GdkEvent * > ( ev ) ) ;
return true ;
}
return false ;
}
2020-11-06 05:20:31 +00:00
void ChatMessageItemContainer : : on_link_menu_copy ( ) {
Gtk : : Clipboard : : get ( ) - > set_text ( m_selected_link ) ;
}
2021-01-01 07:47:03 +00:00
void ChatMessageItemContainer : : HandleLinks ( Gtk : : TextView & tv ) {
2020-10-30 19:54:28 +00:00
const auto rgx = Glib : : Regex : : create ( R " ( \b https?: \ / \ /[^ \ s]+ \ .[^ \ s]+ \b ) " ) ;
2020-10-11 03:38:58 +00:00
2021-01-01 07:47:03 +00:00
auto buf = tv . get_buffer ( ) ;
2023-03-16 00:19:54 +00:00
Glib : : ustring text = ChatUtil : : GetText ( buf ) ;
2020-10-10 03:14:57 +00:00
2020-10-30 19:54:28 +00:00
int startpos = 0 ;
Glib : : MatchInfo match ;
while ( rgx - > match ( text , startpos , match ) ) {
int mstart , mend ;
match . fetch_pos ( 0 , mstart , mend ) ;
std : : string link = match . fetch ( 0 ) ;
2020-10-10 03:14:57 +00:00
auto tag = buf - > create_tag ( ) ;
2020-10-11 03:38:58 +00:00
m_link_tagmap [ tag ] = link ;
2023-12-05 01:19:02 +00:00
const auto color = tv . get_style_context ( ) - > get_color ( Gtk : : STATE_FLAG_LINK ) ;
tag - > property_foreground_rgba ( ) = color ;
2020-11-06 05:20:31 +00:00
tag - > set_property ( " underline " , 1 ) ; // stupid workaround for vcpkg bug (i think)
2020-10-10 03:14:57 +00:00
2020-10-30 19:54:28 +00:00
const auto chars_start = g_utf8_pointer_to_offset ( text . c_str ( ) , text . c_str ( ) + mstart ) ;
const auto chars_end = g_utf8_pointer_to_offset ( text . c_str ( ) , text . c_str ( ) + mend ) ;
const auto erase_from = buf - > get_iter_at_offset ( chars_start ) ;
const auto erase_to = buf - > get_iter_at_offset ( chars_end ) ;
2020-10-11 03:38:58 +00:00
auto it = buf - > erase ( erase_from , erase_to ) ;
it = buf - > insert_with_tag ( it , link , tag ) ;
2020-10-10 03:14:57 +00:00
2020-10-30 19:54:28 +00:00
startpos = mend ;
2020-10-10 03:14:57 +00:00
}
}
bool ChatMessageItemContainer : : OnLinkClick ( GdkEventButton * ev ) {
if ( m_text_component = = nullptr ) return false ;
2020-12-22 07:35:57 +00:00
if ( ev - > type ! = GDK_BUTTON_PRESS ) return false ;
2020-11-06 05:20:31 +00:00
if ( ev - > button ! = GDK_BUTTON_PRIMARY & & ev - > button ! = GDK_BUTTON_SECONDARY ) return false ;
2020-10-10 03:14:57 +00:00
auto buf = m_text_component - > get_buffer ( ) ;
Gtk : : TextBuffer : : iterator start , end ;
buf - > get_selection_bounds ( start , end ) ; // no open if selection
if ( start . get_offset ( ) ! = end . get_offset ( ) )
return false ;
int x , y ;
2022-04-06 02:01:53 +00:00
m_text_component - > window_to_buffer_coords ( Gtk : : TEXT_WINDOW_WIDGET , static_cast < int > ( ev - > x ) , static_cast < int > ( ev - > y ) , x , y ) ;
2020-10-10 03:14:57 +00:00
Gtk : : TextBuffer : : iterator iter ;
m_text_component - > get_iter_at_location ( iter , x , y ) ;
const auto tags = iter . get_tags ( ) ;
2022-04-06 02:01:53 +00:00
for ( const auto & tag : tags ) {
2020-10-11 03:38:58 +00:00
const auto it = m_link_tagmap . find ( tag ) ;
if ( it ! = m_link_tagmap . end ( ) ) {
2020-11-06 05:20:31 +00:00
if ( ev - > button = = GDK_BUTTON_PRIMARY ) {
LaunchBrowser ( it - > second ) ;
return true ;
} else if ( ev - > button = = GDK_BUTTON_SECONDARY ) {
m_selected_link = it - > second ;
m_link_menu . popup_at_pointer ( reinterpret_cast < GdkEvent * > ( ev ) ) ;
return true ;
}
2020-10-10 03:14:57 +00:00
}
}
return false ;
}
2020-10-11 03:38:58 +00:00
ChatMessageItemContainer : : type_signal_channel_click ChatMessageItemContainer : : signal_action_channel_click ( ) {
return m_signal_action_channel_click ;
}
2020-12-15 06:51:49 +00:00
ChatMessageItemContainer : : type_signal_action_reaction_add ChatMessageItemContainer : : signal_action_reaction_add ( ) {
return m_signal_action_reaction_add ;
}
ChatMessageItemContainer : : type_signal_action_reaction_remove ChatMessageItemContainer : : signal_action_reaction_remove ( ) {
return m_signal_action_reaction_remove ;
}
2021-07-05 05:57:55 +00:00
ChatMessageHeader : : ChatMessageHeader ( const Message & data )
: m_main_box ( Gtk : : ORIENTATION_HORIZONTAL )
, m_content_box ( Gtk : : ORIENTATION_VERTICAL )
, m_meta_box ( Gtk : : ORIENTATION_HORIZONTAL )
, m_avatar ( Abaddon : : Get ( ) . GetImageManager ( ) . GetPlaceholder ( AvatarSize ) ) {
2021-06-10 19:27:32 +00:00
UserID = data . Author . ID ;
ChannelID = data . ChannelID ;
2020-09-26 03:02:13 +00:00
2020-11-24 01:34:09 +00:00
const auto author = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetUser ( UserID ) ;
2020-11-18 21:25:10 +00:00
auto & img = Abaddon : : Get ( ) . GetImageManager ( ) ;
2020-12-22 07:24:09 +00:00
2023-08-29 01:47:04 +00:00
std : : string avatar_url ;
if ( data . IsWebhook ( ) ) {
const auto webhook_data = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetWebhookMessageData ( data . ID ) ;
if ( webhook_data . has_value ( ) ) {
avatar_url = webhook_data - > GetAvatarURL ( ) ;
}
}
if ( avatar_url . empty ( ) ) {
avatar_url = author - > GetAvatarURL ( data . GuildID ) ;
}
2021-04-08 00:42:53 +00:00
auto cb = [ this ] ( const Glib : : RefPtr < Gdk : : Pixbuf > & pb ) {
m_static_avatar = pb - > scale_simple ( AvatarSize , AvatarSize , Gdk : : INTERP_BILINEAR ) ;
2021-07-05 05:57:55 +00:00
m_avatar . property_pixbuf ( ) = m_static_avatar ;
2021-04-08 00:42:53 +00:00
} ;
2023-08-29 01:47:04 +00:00
img . LoadFromURL ( avatar_url , sigc : : track_obj ( cb , * this ) ) ;
2020-12-22 07:24:09 +00:00
2022-02-14 07:53:21 +00:00
if ( author - > HasAnimatedAvatar ( data . GuildID ) ) {
2021-01-18 03:50:55 +00:00
auto cb = [ this ] ( const Glib : : RefPtr < Gdk : : PixbufAnimation > & pb ) {
m_anim_avatar = pb ;
} ;
2022-02-14 07:53:21 +00:00
img . LoadAnimationFromURL ( author - > GetAvatarURL ( data . GuildID , " gif " ) , AvatarSize , AvatarSize , sigc : : track_obj ( cb , * this ) ) ;
2021-01-18 03:50:55 +00:00
}
2020-09-26 03:02:13 +00:00
get_style_context ( ) - > add_class ( " message-container " ) ;
2021-07-05 05:57:55 +00:00
m_author . get_style_context ( ) - > add_class ( " message-container-author " ) ;
m_timestamp . get_style_context ( ) - > add_class ( " message-container-timestamp " ) ;
m_avatar . get_style_context ( ) - > add_class ( " message-container-avatar " ) ;
2020-09-26 03:02:13 +00:00
2021-07-05 05:57:55 +00:00
m_avatar . set_valign ( Gtk : : ALIGN_START ) ;
m_avatar . set_margin_right ( 10 ) ;
2020-09-26 03:02:13 +00:00
2021-07-05 05:57:55 +00:00
m_author . set_single_line_mode ( true ) ;
m_author . set_line_wrap ( false ) ;
m_author . set_ellipsize ( Pango : : ELLIPSIZE_END ) ;
2022-03-26 06:51:56 +00:00
m_author . set_xalign ( 0.0F ) ;
2021-07-05 05:57:55 +00:00
m_author . set_can_focus ( false ) ;
2020-11-08 05:44:26 +00:00
2021-07-05 05:57:55 +00:00
m_meta_ev . signal_button_press_event ( ) . connect ( sigc : : mem_fun ( * this , & ChatMessageHeader : : on_author_button_press ) ) ;
2020-09-26 03:02:13 +00:00
2022-07-05 07:53:00 +00:00
if ( author - > IsABot ( ) | | data . WebhookID . has_value ( ) ) {
2020-10-06 02:46:44 +00:00
m_extra = Gtk : : manage ( new Gtk : : Label ) ;
m_extra - > get_style_context ( ) - > add_class ( " message-container-extra " ) ;
m_extra - > set_single_line_mode ( true ) ;
m_extra - > set_margin_start ( 12 ) ;
m_extra - > set_can_focus ( false ) ;
m_extra - > set_use_markup ( true ) ;
}
2023-08-29 01:47:04 +00:00
if ( data . IsWebhook ( ) ) {
2020-12-23 02:18:39 +00:00
m_extra - > set_markup ( " <b>Webhook</b> " ) ;
2023-08-29 01:47:04 +00:00
} else if ( author - > IsABot ( ) ) {
m_extra - > set_markup ( " <b>BOT</b> " ) ;
}
2020-10-06 02:46:44 +00:00
2021-07-05 05:57:55 +00:00
m_timestamp . set_text ( data . ID . GetLocalTimestamp ( ) ) ;
m_timestamp . set_hexpand ( true ) ;
m_timestamp . set_halign ( Gtk : : ALIGN_END ) ;
m_timestamp . set_ellipsize ( Pango : : ELLIPSIZE_END ) ;
m_timestamp . set_opacity ( 0.5 ) ;
m_timestamp . set_single_line_mode ( true ) ;
m_timestamp . set_margin_start ( 12 ) ;
m_timestamp . set_can_focus ( false ) ;
2020-09-26 03:02:13 +00:00
2021-07-05 05:57:55 +00:00
m_main_box . set_hexpand ( true ) ;
m_main_box . set_vexpand ( true ) ;
m_main_box . set_can_focus ( true ) ;
2020-09-26 03:02:13 +00:00
2021-07-05 05:57:55 +00:00
m_meta_box . set_hexpand ( true ) ;
m_meta_box . set_can_focus ( false ) ;
2020-09-26 03:02:13 +00:00
2020-12-25 08:02:40 +00:00
const auto on_enter_cb = [ this ] ( const GdkEventCrossing * event ) - > bool {
if ( m_anim_avatar )
2021-07-05 05:57:55 +00:00
m_avatar . property_pixbuf_animation ( ) = m_anim_avatar ;
2020-12-25 08:02:40 +00:00
return false ;
} ;
const auto on_leave_cb = [ this ] ( const GdkEventCrossing * event ) - > bool {
if ( m_anim_avatar )
2021-07-05 05:57:55 +00:00
m_avatar . property_pixbuf ( ) = m_static_avatar ;
2020-12-25 08:02:40 +00:00
return false ;
} ;
2021-07-05 05:57:55 +00:00
m_content_box_ev . add_events ( Gdk : : ENTER_NOTIFY_MASK | Gdk : : LEAVE_NOTIFY_MASK ) ;
m_meta_ev . add_events ( Gdk : : ENTER_NOTIFY_MASK | Gdk : : LEAVE_NOTIFY_MASK ) ;
m_avatar_ev . add_events ( Gdk : : ENTER_NOTIFY_MASK | Gdk : : LEAVE_NOTIFY_MASK ) ;
2021-11-24 08:14:41 +00:00
if ( Abaddon : : Get ( ) . GetSettings ( ) . ShowAnimations ) {
2021-07-05 05:57:55 +00:00
m_content_box_ev . signal_enter_notify_event ( ) . connect ( on_enter_cb ) ;
m_content_box_ev . signal_leave_notify_event ( ) . connect ( on_leave_cb ) ;
m_meta_ev . signal_enter_notify_event ( ) . connect ( on_enter_cb ) ;
m_meta_ev . signal_leave_notify_event ( ) . connect ( on_leave_cb ) ;
m_avatar_ev . signal_enter_notify_event ( ) . connect ( on_enter_cb ) ;
m_avatar_ev . signal_leave_notify_event ( ) . connect ( on_leave_cb ) ;
2020-12-25 08:02:40 +00:00
}
2021-07-05 05:57:55 +00:00
m_meta_box . add ( m_author ) ;
2020-10-06 02:46:44 +00:00
if ( m_extra ! = nullptr )
2021-07-05 05:57:55 +00:00
m_meta_box . add ( * m_extra ) ;
2020-11-08 05:44:26 +00:00
2021-07-05 05:57:55 +00:00
m_meta_box . add ( m_timestamp ) ;
m_meta_ev . add ( m_meta_box ) ;
m_content_box . add ( m_meta_ev ) ;
m_avatar_ev . add ( m_avatar ) ;
m_main_box . add ( m_avatar_ev ) ;
m_content_box_ev . add ( m_content_box ) ;
m_main_box . add ( m_content_box_ev ) ;
add ( m_main_box ) ;
2020-09-26 03:02:13 +00:00
set_margin_bottom ( 8 ) ;
2023-06-03 22:04:48 +00:00
set_focus_on_click ( false ) ;
2020-09-26 03:02:13 +00:00
2020-09-22 01:01:32 +00:00
show_all ( ) ;
2021-01-18 06:34:47 +00:00
auto & discord = Abaddon : : Get ( ) . GetDiscordClient ( ) ;
2023-08-29 01:47:04 +00:00
if ( data . IsWebhook ( ) ) {
const auto webhook_data = discord . GetWebhookMessageData ( data . ID ) ;
if ( webhook_data . has_value ( ) ) {
const auto name = Glib : : Markup : : escape_text ( webhook_data - > Username ) ;
m_author . set_markup ( " <span weight='bold'> " + name + " </span> " ) ;
} else {
UpdateName ( ) ;
}
} else {
auto role_update_cb = [ this ] ( . . . ) { UpdateName ( ) ; } ;
discord . signal_role_update ( ) . connect ( sigc : : track_obj ( role_update_cb , * this ) ) ;
auto guild_member_update_cb = [ this ] ( const auto & , const auto & ) { UpdateName ( ) ; } ;
discord . signal_guild_member_update ( ) . connect ( sigc : : track_obj ( guild_member_update_cb , * this ) ) ;
UpdateName ( ) ;
}
2021-07-05 05:57:55 +00:00
AttachUserMenuHandler ( m_meta_ev ) ;
AttachUserMenuHandler ( m_avatar_ev ) ;
2020-09-22 01:01:32 +00:00
}
2022-03-26 06:51:56 +00:00
void ChatMessageHeader : : UpdateName ( ) {
2020-09-26 03:02:13 +00:00
const auto & discord = Abaddon : : Get ( ) . GetDiscordClient ( ) ;
2020-11-20 00:18:59 +00:00
const auto user = discord . GetUser ( UserID ) ;
if ( ! user . has_value ( ) ) return ;
2021-10-08 21:52:38 +00:00
const auto chan = discord . GetChannel ( ChannelID ) ;
bool is_guild = chan . has_value ( ) & & chan - > GuildID . has_value ( ) ;
if ( is_guild ) {
const auto role_id = discord . GetMemberHoistedRole ( * chan - > GuildID , UserID , true ) ;
const auto role = discord . GetRole ( role_id ) ;
2023-10-21 00:30:39 +00:00
const auto name = user - > GetDisplayNameEscaped ( * chan - > GuildID ) ;
2021-10-08 21:52:38 +00:00
std : : string md ;
if ( role . has_value ( ) )
2022-03-26 06:51:56 +00:00
m_author . set_markup ( " <span weight='bold' color='# " + IntToCSSColor ( role - > Color ) + " '> " + name + " </span> " ) ;
2021-10-08 21:52:38 +00:00
else
2022-03-26 06:51:56 +00:00
m_author . set_markup ( " <span weight='bold'> " + name + " </span> " ) ;
2021-10-08 21:52:38 +00:00
} else
2023-06-14 01:28:18 +00:00
m_author . set_markup ( " <span weight='bold'> " + user - > GetDisplayNameEscaped ( ) + " </span> " ) ;
2020-09-26 03:02:13 +00:00
}
2021-03-31 02:31:13 +00:00
std : : vector < Gtk : : Widget * > ChatMessageHeader : : GetChildContent ( ) {
return m_content_widgets ;
}
2020-11-08 05:44:26 +00:00
void ChatMessageHeader : : AttachUserMenuHandler ( Gtk : : Widget & widget ) {
widget . signal_button_press_event ( ) . connect ( [ this ] ( GdkEventButton * ev ) - > bool {
if ( ev - > type = = GDK_BUTTON_PRESS & & ev - > button = = GDK_BUTTON_SECONDARY ) {
2021-05-24 05:42:04 +00:00
auto info = Abaddon : : Get ( ) . GetDiscordClient ( ) . GetChannel ( ChannelID ) ;
Snowflake guild_id ;
if ( info . has_value ( ) & & info - > GuildID . has_value ( ) )
guild_id = * info - > GuildID ;
Abaddon : : Get ( ) . ShowUserMenu ( reinterpret_cast < GdkEvent * > ( ev ) , UserID , guild_id ) ;
2020-11-08 05:44:26 +00:00
return true ;
}
return false ;
} ) ;
}
2020-10-11 05:56:30 +00:00
bool ChatMessageHeader : : on_author_button_press ( GdkEventButton * ev ) {
if ( ev - > button = = GDK_BUTTON_PRIMARY & & ( ev - > state & GDK_SHIFT_MASK ) ) {
2020-11-08 05:44:26 +00:00
m_signal_action_insert_mention . emit ( ) ;
2020-10-11 05:56:30 +00:00
return true ;
}
return false ;
}
ChatMessageHeader : : type_signal_action_insert_mention ChatMessageHeader : : signal_action_insert_mention ( ) {
return m_signal_action_insert_mention ;
}
2020-11-08 05:44:26 +00:00
ChatMessageHeader : : type_signal_action_open_user_menu ChatMessageHeader : : signal_action_open_user_menu ( ) {
return m_signal_action_open_user_menu ;
}
2020-09-26 03:02:13 +00:00
void ChatMessageHeader : : AddContent ( Gtk : : Widget * widget , bool prepend ) {
2021-03-31 02:31:13 +00:00
m_content_widgets . push_back ( widget ) ;
2021-07-05 05:57:55 +00:00
const auto cb = [ this , widget ] ( ) {
2021-03-31 02:31:13 +00:00
m_content_widgets . erase ( std : : remove ( m_content_widgets . begin ( ) , m_content_widgets . end ( ) , widget ) , m_content_widgets . end ( ) ) ;
2021-07-05 05:57:55 +00:00
} ;
widget - > signal_unmap ( ) . connect ( sigc : : track_obj ( cb , * this , * widget ) , false ) ;
m_content_box . add ( * widget ) ;
2020-09-26 03:02:13 +00:00
if ( prepend )
2021-07-05 05:57:55 +00:00
m_content_box . reorder_child ( * widget , 1 ) ;
2021-04-12 06:21:26 +00:00
if ( auto * x = dynamic_cast < ChatMessageItemContainer * > ( widget ) ) {
if ( x - > ID > NewestID )
NewestID = x - > ID ;
}
2020-09-26 03:02:13 +00:00
}