summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md53
-rw-r--r--res/css/main.css4
-rw-r--r--src/abaddon.cpp4
-rw-r--r--src/components/channels.cpp2
-rw-r--r--src/components/chatinputindicator.cpp14
-rw-r--r--src/components/chatinputindicator.hpp1
-rw-r--r--src/components/chatmessage.cpp28
-rw-r--r--src/components/chatwindow.cpp2
-rw-r--r--src/components/completer.cpp3
-rw-r--r--src/components/friendslist.cpp12
-rw-r--r--src/components/memberlist.cpp14
-rw-r--r--src/dialogs/friendpicker.cpp2
-rw-r--r--src/discord/channel.cpp4
-rw-r--r--src/discord/objects.cpp1
-rw-r--r--src/discord/objects.hpp1
-rw-r--r--src/discord/store.cpp7
-rw-r--r--src/discord/user.cpp107
-rw-r--r--src/discord/user.hpp28
-rw-r--r--src/misc/chatutil.cpp8
-rw-r--r--src/windows/guildsettings/auditlogpane.cpp18
-rw-r--r--src/windows/guildsettings/banspane.cpp6
-rw-r--r--src/windows/guildsettings/emojispane.cpp2
-rw-r--r--src/windows/guildsettings/invitespane.cpp2
-rw-r--r--src/windows/guildsettings/memberspane.cpp6
-rw-r--r--src/windows/profile/mutualfriendspane.cpp2
-rw-r--r--src/windows/profilewindow.cpp25
-rw-r--r--src/windows/profilewindow.hpp2
-rw-r--r--src/windows/voicewindow.cpp2
28 files changed, 238 insertions, 122 deletions
diff --git a/README.md b/README.md
index c66af60..70996b2 100644
--- a/README.md
+++ b/README.md
@@ -226,32 +226,33 @@ Used in guild settings popup:
Used in profile popup:
-| Selector | Description |
-|------------------------------|---------------------------------------------------------|
-| `.mutual-friend-item` | Applied to every item in the mutual friends list |
-| `.mutual-friend-item-name` | Name in mutual friend item |
-| `.mutual-friend-item-avatar` | Avatar in mutual friend item |
-| `.mutual-guild-item` | Applied to every item in the mutual guilds list |
-| `.mutual-guild-item-name` | Name in mutual guild item |
-| `.mutual-guild-item-icon` | Icon in mutual guild item |
-| `.mutual-guild-item-nick` | User nickname in mutual guild item |
-| `.profile-connection` | Applied to every item in the user connections list |
-| `.profile-connection-label` | Label in profile connection item |
-| `.profile-connection-check` | Checkmark in verified profile connection items |
-| `.profile-connections` | Container for profile connections |
-| `.profile-notes` | Container for notes in profile window |
-| `.profile-notes-label` | Label that says "NOTE" |
-| `.profile-notes-text` | Actual note text |
-| `.profile-info-pane` | Applied to container for info section of profile popup |
-| `.profile-info-created` | Label for creation date of profile |
-| `.user-profile-window` | |
-| `.profile-main-container` | Inner container for profile |
-| `.profile-avatar` | |
-| `.profile-username` | |
-| `.profile-switcher` | Buttons used to switch viewed section of profile |
-| `.profile-stack` | Container for profile info that can be switched between |
-| `.profile-badges` | Container for badges |
-| `.profile-badge` | |
+| Selector | Description |
+|--------------------------------|------------------------------------------------------------|
+| `.mutual-friend-item` | Applied to every item in the mutual friends list |
+| `.mutual-friend-item-name` | Name in mutual friend item |
+| `.mutual-friend-item-avatar` | Avatar in mutual friend item |
+| `.mutual-guild-item` | Applied to every item in the mutual guilds list |
+| `.mutual-guild-item-name` | Name in mutual guild item |
+| `.mutual-guild-item-icon` | Icon in mutual guild item |
+| `.mutual-guild-item-nick` | User nickname in mutual guild item |
+| `.profile-connection` | Applied to every item in the user connections list |
+| `.profile-connection-label` | Label in profile connection item |
+| `.profile-connection-check` | Checkmark in verified profile connection items |
+| `.profile-connections` | Container for profile connections |
+| `.profile-notes` | Container for notes in profile window |
+| `.profile-notes-label` | Label that says "NOTE" |
+| `.profile-notes-text` | Actual note text |
+| `.profile-info-pane` | Applied to container for info section of profile popup |
+| `.profile-info-created` | Label for creation date of profile |
+| `.user-profile-window` | |
+| `.profile-main-container` | Inner container for profile |
+| `.profile-avatar` | |
+| `.profile-username` | User's display name (username for backwards compatibility) |
+| `.profile-username-nondisplay` | User's actual username |
+| `.profile-switcher` | Buttons used to switch viewed section of profile |
+| `.profile-stack` | Container for profile info that can be switched between |
+| `.profile-badges` | Container for badges |
+| `.profile-badge` | |
### Settings
diff --git a/res/css/main.css b/res/css/main.css
index 40e1696..1b70301 100644
--- a/res/css/main.css
+++ b/res/css/main.css
@@ -237,6 +237,10 @@
font-size: 20px;
}
+.profile-username-nondisplay {
+ margin-left: 10px;
+}
+
.profile-badge {
margin-right: 10px;
}
diff --git a/src/abaddon.cpp b/src/abaddon.cpp
index ab2223e..92cc494 100644
--- a/src/abaddon.cpp
+++ b/src/abaddon.cpp
@@ -964,7 +964,7 @@ void Abaddon::ActionKickMember(Snowflake user_id, Snowflake guild_id) {
ConfirmDialog dlg(*m_main_window);
const auto user = m_discord.GetUser(user_id);
if (user.has_value())
- dlg.SetConfirmText("Are you sure you want to kick " + user->Username + "#" + user->Discriminator + "?");
+ dlg.SetConfirmText("Are you sure you want to kick " + user->GetUsername() + "?");
auto response = dlg.run();
if (response == Gtk::RESPONSE_OK)
m_discord.KickUser(user_id, guild_id);
@@ -974,7 +974,7 @@ void Abaddon::ActionBanMember(Snowflake user_id, Snowflake guild_id) {
ConfirmDialog dlg(*m_main_window);
const auto user = m_discord.GetUser(user_id);
if (user.has_value())
- dlg.SetConfirmText("Are you sure you want to ban " + user->Username + "#" + user->Discriminator + "?");
+ dlg.SetConfirmText("Are you sure you want to ban " + user->GetUsername() + "?");
auto response = dlg.run();
if (response == Gtk::RESPONSE_OK)
m_discord.BanUser(user_id, guild_id);
diff --git a/src/components/channels.cpp b/src/components/channels.cpp
index 6a5c8cc..26f3259 100644
--- a/src/components/channels.cpp
+++ b/src/components/channels.cpp
@@ -864,7 +864,7 @@ Gtk::TreeModel::iterator ChannelList::CreateVoiceParticipantRow(const UserData &
auto row = *m_model->append(parent);
row[m_columns.m_type] = RenderType::VoiceParticipant;
row[m_columns.m_id] = user.ID;
- row[m_columns.m_name] = user.GetEscapedName();
+ row[m_columns.m_name] = user.GetDisplayNameEscaped();
const auto voice_state = Abaddon::Get().GetDiscordClient().GetVoiceState(user.ID);
if (voice_state.has_value()) {
diff --git a/src/components/chatinputindicator.cpp b/src/components/chatinputindicator.cpp
index 13315c6..0611e71 100644
--- a/src/components/chatinputindicator.cpp
+++ b/src/components/chatinputindicator.cpp
@@ -54,6 +54,12 @@ void ChatInputIndicator::AddUser(Snowflake channel_id, const UserData &user, int
void ChatInputIndicator::SetActiveChannel(Snowflake id) {
m_active_channel = id;
+ const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
+ if (channel.has_value()) {
+ m_active_guild = channel->GuildID;
+ } else {
+ m_active_guild = std::nullopt;
+ }
ComputeTypingString();
}
@@ -105,14 +111,14 @@ void ChatInputIndicator::ComputeTypingString() {
if (typers.empty()) {
SetTypingString("");
} else if (typers.size() == 1) {
- SetTypingString(typers[0].Username + " is typing...");
+ SetTypingString(typers[0].GetDisplayName(m_active_guild) + " is typing...");
} else if (typers.size() == 2) {
- SetTypingString(typers[0].Username + " and " + typers[1].Username + " are typing...");
+ SetTypingString(typers[0].GetDisplayName(m_active_guild) + " and " + typers[1].GetDisplayName(m_active_guild) + " are typing...");
} else if (typers.size() > 2 && typers.size() <= MaxUsersInIndicator) {
Glib::ustring str;
for (size_t i = 0; i < typers.size() - 1; i++)
- str += typers[i].Username + ", ";
- SetTypingString(str + "and " + typers[typers.size() - 1].Username + " are typing...");
+ str += typers[i].GetDisplayName(m_active_guild) + ", ";
+ SetTypingString(str + "and " + typers[typers.size() - 1].GetDisplayName(m_active_guild) + " are typing...");
} else { // size() > MaxUsersInIndicator
SetTypingString("Several people are typing...");
}
diff --git a/src/components/chatinputindicator.hpp b/src/components/chatinputindicator.hpp
index 40c966e..5688393 100644
--- a/src/components/chatinputindicator.hpp
+++ b/src/components/chatinputindicator.hpp
@@ -23,5 +23,6 @@ private:
Glib::ustring m_custom_markup;
Snowflake m_active_channel;
+ std::optional<Snowflake> m_active_guild;
std::unordered_map<Snowflake, std::unordered_map<Snowflake, sigc::connection>> m_typers; // channel id -> [user id -> connection]
};
diff --git a/src/components/chatmessage.cpp b/src/components/chatmessage.cpp
index 4b2e748..4fde039 100644
--- a/src/components/chatmessage.cpp
+++ b/src/components/chatmessage.cpp
@@ -221,36 +221,36 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
if (data->Mentions.empty()) break;
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
const auto &added = data->Mentions[0];
- b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->Username + "</span> added <span color='#eeeeee'>" + added.Username + "</span></span></i>");
+ b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->GetUsername() + "</span> added <span color='#eeeeee'>" + added.GetUsername() + "</span></span></i>");
} break;
case MessageType::RECIPIENT_REMOVE: {
if (data->Mentions.empty()) break;
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
const auto &added = data->Mentions[0];
if (adder->ID == added.ID)
- b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->Username + "</span> left</span></i>");
+ b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->GetUsername() + "</span> left</span></i>");
else
- b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->Username + "</span> removed <span color='#eeeeee'>" + added.Username + "</span></span></i>");
+ b->insert_markup(s, "<i><span color='#999999'><span color='#eeeeee'>" + adder->GetUsername() + "</span> removed <span color='#eeeeee'>" + added.GetUsername() + "</span></span></i>");
} break;
case MessageType::CHANNEL_NAME_CHANGE: {
const auto author = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
- b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " changed the name to <b>" + Glib::Markup::escape_text(data->Content) + "</b></span></i>");
+ b->insert_markup(s, "<i><span color='#999999'>" + author->GetDisplayNameEscapedBold() + " changed the name to <b>" + Glib::Markup::escape_text(data->Content) + "</b></span></i>");
} break;
case MessageType::CHANNEL_ICON_CHANGE: {
const auto author = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
- b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " changed the channel icon</span></i>");
+ b->insert_markup(s, "<i><span color='#999999'>" + author->GetDisplayNameEscapedBold() + " changed the channel icon</span></i>");
} break;
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);
- b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " just boosted the server <b>" + Glib::Markup::escape_text(data->Content) + "</b> times! " +
+ b->insert_markup(s, "<i><span color='#999999'>" + author->GetDisplayNameEscapedBold() + " just boosted the server <b>" + Glib::Markup::escape_text(data->Content) + "</b> times! " +
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;
case MessageType::CHANNEL_FOLLOW_ADD: {
const auto author = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
- b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " has added <b>" + Glib::Markup::escape_text(data->Content) + "</b> to this channel. Its most important updates will show up here.</span></i>");
+ 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>");
} break;
case MessageType::CALL: {
b->insert_markup(s, "<span color='#999999'><i>[started a call]</i></span>");
@@ -270,13 +270,13 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
case MessageType::THREAD_CREATED: {
const auto author = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
if (data->MessageReference.has_value() && data->MessageReference->ChannelID.has_value()) {
- auto iter = b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " started a thread: </span></i>");
+ auto iter = b->insert_markup(s, "<i><span color='#999999'>" + author->GetDisplayNameEscapedBold() + " started a thread: </span></i>");
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 {
- b->insert_markup(s, "<i><span color='#999999'>" + author->GetEscapedBoldName() + " started a thread: </span><b>" + Glib::Markup::escape_text(data->Content) + "</b></i>");
+ b->insert_markup(s, "<i><span color='#999999'>" + author->GetDisplayNameEscapedBold() + " started a thread: </span><b>" + Glib::Markup::escape_text(data->Content) + "</b></i>");
}
} break;
default: break;
@@ -656,7 +656,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
if (role.has_value()) {
const auto author = discord.GetUser(author_id);
if (author.has_value()) {
- return "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">" + author->GetEscapedString() + "</span></b>";
+ return "<b><span color=\"#" + IntToCSSColor(role->Color) + "\">" + author->GetDisplayNameEscaped(guild_id) + "</span></b>";
}
}
}
@@ -664,7 +664,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
const auto author = discord.GetUser(author_id);
if (author.has_value()) {
- return author->GetEscapedBoldString<false>();
+ return author->GetDisplayNameEscapedBold(guild_id);
}
return "<b>Unknown User</b>";
@@ -685,7 +685,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
Glib::Markup::escape_text(data.Interaction->Name) +
"</span>");
} else if (const auto user = discord.GetUser(data.Interaction->User.ID); user.has_value()) {
- lbl->set_markup(user->GetEscapedBoldString<false>());
+ lbl->set_markup(user->GetDisplayNameEscapedBold());
} else {
lbl->set_markup("<b>Unknown User</b>");
}
@@ -1043,7 +1043,7 @@ void ChatMessageHeader::UpdateName() {
else
m_author.set_markup("<span weight='bold'>" + name + "</span>");
} else
- m_author.set_markup("<span weight='bold'>" + user->GetEscapedName() + "</span>");
+ m_author.set_markup("<span weight='bold'>" + user->GetDisplayNameEscaped() + "</span>");
}
std::vector<Gtk::Widget *> ChatMessageHeader::GetChildContent() {
@@ -1069,7 +1069,7 @@ Glib::ustring ChatMessageHeader::GetEscapedDisplayName(const UserData &user, con
if (member.has_value() && !member->Nickname.empty())
return Glib::Markup::escape_text(member->Nickname);
else
- return Glib::Markup::escape_text(user.GetEscapedName());
+ return Glib::Markup::escape_text(user.GetDisplayNameEscaped());
}
bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) {
diff --git a/src/components/chatwindow.cpp b/src/components/chatwindow.cpp
index 1565d0c..e51d491 100644
--- a/src/components/chatwindow.cpp
+++ b/src/components/chatwindow.cpp
@@ -301,7 +301,7 @@ void ChatWindow::StartReplying(Snowflake message_id) {
m_is_replying = true;
m_input->StartReplying();
if (author.has_value())
- m_input_indicator->SetCustomMarkup("Replying to " + author->GetEscapedBoldString<false>());
+ m_input_indicator->SetCustomMarkup("Replying to " + author->GetUsernameEscapedBold());
else
m_input_indicator->SetCustomMarkup("Replying...");
}
diff --git a/src/components/completer.cpp b/src/components/completer.cpp
index c6a5a08..f6f0906 100644
--- a/src/components/completer.cpp
+++ b/src/components/completer.cpp
@@ -117,12 +117,13 @@ void Completer::CompleteMentions(const Glib::ustring &term) {
if (id == me) continue;
const auto author = discord.GetUser(id);
if (!author.has_value()) continue;
+ // todo improve the predicate here
if (!StringContainsCaseless(author->Username, term)) continue;
if (i++ > 15) break;
auto entry = CreateEntry(author->GetMention());
- entry->SetText(author->Username + "#" + author->Discriminator);
+ entry->SetText(author->GetUsername());
if (channel_id.IsValid()) {
const auto chan = discord.GetChannel(channel_id);
diff --git a/src/components/friendslist.cpp b/src/components/friendslist.cpp
index d8a566f..99fbafa 100644
--- a/src/components/friendslist.cpp
+++ b/src/components/friendslist.cpp
@@ -117,16 +117,16 @@ void FriendsList::OnActionRemove(Snowflake id) {
Glib::ustring str;
switch (*discord.GetRelationship(id)) {
case RelationshipType::Blocked:
- str = "Are you sure you want to unblock " + user->Username + "#" + user->Discriminator + "?";
+ str = "Are you sure you want to unblock " + user->GetUsername() + "?";
break;
case RelationshipType::Friend:
- str = "Are you sure you want to remove " + user->Username + "#" + user->Discriminator + "?";
+ str = "Are you sure you want to remove " + user->GetUsername() + "?";
break;
case RelationshipType::PendingIncoming:
- str = "Are you sure you want to ignore " + user->Username + "#" + user->Discriminator + "?";
+ str = "Are you sure you want to ignore " + user->GetUsername() + "?";
break;
case RelationshipType::PendingOutgoing:
- str = "Are you sure you want to cancel your request to " + user->Username + "#" + user->Discriminator + "?";
+ str = "Are you sure you want to cancel your request to " + user->GetUsername() + "?";
break;
default:
break;
@@ -244,7 +244,7 @@ bool FriendsListAddComponent::OnKeyPress(GdkEventKey *e) {
FriendsListFriendRow::FriendsListFriendRow(RelationshipType type, const UserData &data)
: ID(data.ID)
, Type(type)
- , Name(data.Username + "#" + data.Discriminator)
+ , Name(data.GetUsername())
, Status(Abaddon::Get().GetDiscordClient().GetUserStatus(data.ID))
, m_accept("Accept") {
auto *ev = Gtk::manage(new Gtk::EventBox);
@@ -265,7 +265,7 @@ FriendsListFriendRow::FriendsListFriendRow(RelationshipType type, const UserData
img->SetURL(data.GetAvatarURL("png", "32"));
}
- namelbl->set_markup(data.GetEscapedBoldName());
+ namelbl->set_markup(data.GetDisplayNameEscapedBold());
UpdatePresenceLabel();
diff --git a/src/components/memberlist.cpp b/src/components/memberlist.cpp
index 19b4fb8..975b527 100644
--- a/src/components/memberlist.cpp
+++ b/src/components/memberlist.cpp
@@ -37,9 +37,17 @@ MemberListUserRow::MemberListUserRow(const std::optional<GuildData> &guild, cons
m_label->set_single_line_mode(true);
m_label->set_ellipsize(Pango::ELLIPSIZE_END);
- std::string display = data.Username;
- if (Abaddon::Get().GetSettings().ShowMemberListDiscriminators)
- display += "#" + data.Discriminator;
+ // todo remove after migration complete
+ std::string display;
+ if (data.IsPomelo()) {
+ display = data.GetDisplayName(guild.has_value() ? guild->ID : Snowflake::Invalid);
+ } else {
+ display = data.Username;
+ if (Abaddon::Get().GetSettings().ShowMemberListDiscriminators) {
+ display += "#" + data.Discriminator;
+ }
+ }
+
if (guild.has_value()) {
if (const auto col_id = data.GetHoistedRole(guild->ID, true); col_id.IsValid()) {
auto color = Abaddon::Get().GetDiscordClient().GetRole(col_id)->Color;
diff --git a/src/dialogs/friendpicker.cpp b/src/dialogs/friendpicker.cpp
index c70a9e9..fe52b77 100644
--- a/src/dialogs/friendpicker.cpp
+++ b/src/dialogs/friendpicker.cpp
@@ -62,7 +62,7 @@ FriendPickerDialogItem::FriendPickerDialogItem(Snowflake user_id)
, m_layout(Gtk::ORIENTATION_HORIZONTAL) {
auto user = *Abaddon::Get().GetDiscordClient().GetUser(user_id);
- m_name.set_markup(user.GetEscapedBoldString<false>());
+ m_name.set_markup(user.GetUsernameEscapedBold());
m_name.set_single_line_mode(true);
m_avatar.property_pixbuf() = Abaddon::Get().GetImageManager().GetPlaceholder(32);
diff --git a/src/discord/channel.cpp b/src/discord/channel.cpp
index 4b1d909..a7102ad 100644
--- a/src/discord/channel.cpp
+++ b/src/discord/channel.cpp
@@ -147,13 +147,13 @@ std::string ChannelData::GetRecipientsDisplay() const {
const auto recipients = GetDMRecipients();
if (Type == ChannelType::DM && !recipients.empty()) {
- return recipients[0].Username;
+ return recipients[0].GetDisplayName();
}
Glib::ustring r;
for (size_t i = 0; i < recipients.size(); i++) {
const auto &recipient = recipients[i];
- r += recipient.Username;
+ r += recipient.GetDisplayName();
if (i < recipients.size() - 1) {
r += ", ";
}
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index 50002f9..63bd43f 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -464,6 +464,7 @@ void from_json(const nlohmann::json &j, UserProfileData &m) {
JS_D("mutual_guilds", m.MutualGuilds);
JS_ON("premium_guild_since", m.PremiumGuildSince);
JS_ON("premium_since", m.PremiumSince);
+ JS_ON("legacy_username", m.LegacyUsername);
JS_D("user", m.User);
}
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index 7f3d05f..305ac65 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -619,6 +619,7 @@ struct UserProfileData {
std::vector<MutualGuildData> MutualGuilds;
std::optional<std::string> PremiumGuildSince; // null
std::optional<std::string> PremiumSince; // null
+ std::optional<std::string> LegacyUsername; // null
UserData User;
friend void from_json(const nlohmann::json &j, UserProfileData &m);
diff --git a/src/discord/store.cpp b/src/discord/store.cpp
index 41b2069..24b686a 100644
--- a/src/discord/store.cpp
+++ b/src/discord/store.cpp
@@ -438,6 +438,7 @@ void Store::SetUser(Snowflake id, const UserData &user) {
s->Bind(7, user.IsMFAEnabled);
s->Bind(8, user.PremiumType);
s->Bind(9, user.PublicFlags);
+ s->Bind(10, user.GlobalName);
if (!s->Insert())
fprintf(stderr, "user insert failed for %" PRIu64 ": %s\n", static_cast<uint64_t>(id), m_db.ErrStr());
@@ -1109,6 +1110,7 @@ std::optional<UserData> Store::GetUser(Snowflake id) const {
s->Get(6, r.IsMFAEnabled);
s->Get(7, r.PremiumType);
s->Get(8, r.PublicFlags);
+ s->Get(9, r.GlobalName);
s->Reset();
@@ -1233,7 +1235,8 @@ bool Store::CreateTables() {
system BOOL,
mfa BOOL,
premium INTEGER,
- pubflags INTEGER
+ pubflags INTEGER,
+ global_name TEXT
)
)";
@@ -1797,7 +1800,7 @@ bool Store::CreateStatements() {
m_stmt_set_user = std::make_unique<Statement>(m_db, R"(
REPLACE INTO users VALUES (
- ?, ?, ?, ?, ?, ?, ?, ?, ?
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
)");
if (!m_stmt_set_user->OK()) {
diff --git a/src/discord/user.cpp b/src/discord/user.cpp
index 2ee7361..f43284f 100644
--- a/src/discord/user.cpp
+++ b/src/discord/user.cpp
@@ -1,5 +1,9 @@
#include "user.hpp"
+bool UserData::IsPomelo() const noexcept {
+ return Discriminator.size() == 1 && Discriminator[0] == '0';
+}
+
bool UserData::IsABot() const noexcept {
return IsBot.has_value() && *IsBot;
}
@@ -18,49 +22,54 @@ bool UserData::HasAnimatedAvatar() const noexcept {
bool UserData::HasAnimatedAvatar(Snowflake guild_id) const {
const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
- if (member.has_value() && member->Avatar.has_value() && member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_')
+ if (member.has_value() && member->Avatar.has_value() && member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_') {
return true;
- else if (member.has_value() && !member->Avatar.has_value())
+ } else if (member.has_value() && !member->Avatar.has_value()) {
return HasAnimatedAvatar();
+ }
return false;
}
bool UserData::HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const {
- if (guild_id.has_value())
+ if (guild_id.has_value()) {
return HasAnimatedAvatar(*guild_id);
- else
- return HasAnimatedAvatar();
+ }
+
+ return HasAnimatedAvatar();
}
std::string UserData::GetAvatarURL(Snowflake guild_id, const std::string &ext, std::string size) const {
const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
if (member.has_value() && member->Avatar.has_value()) {
- if (ext == "gif" && !(member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_'))
+ if (ext == "gif" && !(member->Avatar.value()[0] == 'a' && member->Avatar.value()[1] == '_')) {
return GetAvatarURL(ext, size);
+ }
return "https://cdn.discordapp.com/guilds/" +
std::to_string(guild_id) + "/users/" + std::to_string(ID) +
"/avatars/" + *member->Avatar + "." +
ext + "?" + "size=" + size;
- } else {
- return GetAvatarURL(ext, size);
}
+ return GetAvatarURL(ext, size);
}
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, const std::string &ext, std::string size) const {
- if (guild_id.has_value())
+ if (guild_id.has_value()) {
return GetAvatarURL(*guild_id, ext, size);
- else
- return GetAvatarURL(ext, size);
+ }
+ return GetAvatarURL(ext, size);
}
std::string UserData::GetAvatarURL(const std::string &ext, std::string size) const {
- if (HasAvatar())
+ if (HasAvatar()) {
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
- else
- return GetDefaultAvatarURL();
+ }
+ return GetDefaultAvatarURL();
}
std::string UserData::GetDefaultAvatarURL() const {
+ if (IsPomelo()) {
+ return "https://cdn.discordapp.com/embed/avatars/" + std::to_string((static_cast<uint64_t>(ID) >> 22) % 6) + ".png";
+ }
return "https://cdn.discordapp.com/embed/avatars/" + std::to_string(std::stoul(Discriminator) % 5) + ".png"; // size isn't respected by the cdn
}
@@ -72,18 +81,75 @@ std::string UserData::GetMention() const {
return "<@" + std::to_string(ID) + ">";
}
-std::string UserData::GetEscapedName() const {
- return Glib::Markup::escape_text(Username);
+std::string UserData::GetDisplayName() const {
+ if (IsPomelo() && GlobalName.has_value()) {
+ return *GlobalName;
+ }
+
+ return Username;
+}
+
+std::string UserData::GetDisplayName(Snowflake guild_id) const {
+ const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
+ if (member.has_value() && !member->Nickname.empty()) {
+ return member->Nickname;
+ }
+ return GetDisplayName();
}
-std::string UserData::GetEscapedBoldName() const {
- return "<b>" + Glib::Markup::escape_text(Username) + "</b>";
+std::string UserData::GetDisplayName(const std::optional<Snowflake> &guild_id) const {
+ if (guild_id.has_value()) {
+ return GetDisplayName(*guild_id);
+ }
+ return GetDisplayName();
}
-std::string UserData::GetEscapedString() const {
+std::string UserData::GetDisplayNameEscaped() const {
+ return Glib::Markup::escape_text(GetDisplayName());
+}
+
+std::string UserData::GetDisplayNameEscaped(Snowflake guild_id) const {
+ return Glib::Markup::escape_text(GetDisplayName(guild_id));
+}
+
+std::string UserData::GetDisplayNameEscapedBold() const {
+ return "<b>" + Glib::Markup::escape_text(GetDisplayName()) + "</b>";
+}
+
+std::string UserData::GetDisplayNameEscapedBold(Snowflake guild_id) const {
+ return "<b>" + Glib::Markup::escape_text(GetDisplayName(guild_id)) + "</b>";
+}
+
+std::string UserData::GetUsername() const {
+ if (IsPomelo()) {
+ return Username;
+ }
+
+ return Username + "#" + Discriminator;
+}
+
+std::string UserData::GetUsernameEscaped() const {
+ if (IsPomelo()) {
+ return Glib::Markup::escape_text(Username);
+ }
+
return Glib::Markup::escape_text(Username) + "#" + Discriminator;
}
+std::string UserData::GetUsernameEscapedBold() const {
+ if (IsPomelo()) {
+ return "<b>" + Glib::Markup::escape_text(Username) + "</b>";
+ }
+ return "<b>" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
+}
+
+std::string UserData::GetUsernameEscapedBoldAt() const {
+ if (IsPomelo()) {
+ return "<b>@" + Glib::Markup::escape_text(Username) + "</b>";
+ }
+ return "<b>@" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
+}
+
void from_json(const nlohmann::json &j, UserData &m) {
JS_D("id", m.ID);
JS_D("username", m.Username);
@@ -104,6 +170,7 @@ void from_json(const nlohmann::json &j, UserData &m) {
JS_ON("phone", m.Phone);
JS_ON("bio", m.Bio);
JS_ON("banner", m.BannerHash);
+ JS_N("global_name", m.GlobalName);
}
void to_json(nlohmann::json &j, const UserData &m) {
@@ -127,6 +194,7 @@ void to_json(nlohmann::json &j, const UserData &m) {
JS_IF("mobile", m.IsMobile);
JS_IF("nsfw_allowed", m.IsNSFWAllowed);
JS_IF("phone", m.Phone);
+ JS_IF("global_name", m.GlobalName);
}
void UserData::update_from_json(const nlohmann::json &j) {
@@ -146,6 +214,7 @@ void UserData::update_from_json(const nlohmann::json &j) {
JS_RD("mobile", IsMobile);
JS_RD("nsfw_allowed", IsNSFWAllowed);
JS_RD("phone", Phone);
+ JS_RD("global_name", GlobalName);
}
const char *UserData::GetFlagName(uint64_t flag) {
diff --git a/src/discord/user.hpp b/src/discord/user.hpp
index 1b9d517..8b2a2c4 100644
--- a/src/discord/user.hpp
+++ b/src/discord/user.hpp
@@ -25,6 +25,11 @@ struct UserData {
VerifiedBot = 1 << 16,
EarlyVerifiedBotDeveloper = 1 << 17,
CertifiedModerator = 1 << 18,
+ BotHTTPInteractions = 1 << 19,
+ Spammer = 1 << 20,
+ DisablePremium = 1 << 21,
+ ActiveDeveloper = 1 << 22,
+ Quarantined = 1ULL << 44,
MaxFlag_PlusOne,
MaxFlag = MaxFlag_PlusOne - 1,
@@ -37,6 +42,7 @@ struct UserData {
std::string Username;
std::string Discriminator;
std::string Avatar; // null
+ std::optional<std::string> GlobalName;
std::optional<bool> IsBot;
std::optional<bool> IsSystem;
std::optional<bool> IsMFAEnabled;
@@ -60,6 +66,7 @@ struct UserData {
friend void to_json(nlohmann::json &j, const UserData &m);
void update_from_json(const nlohmann::json &j);
+ [[nodiscard]] bool IsPomelo() const noexcept;
[[nodiscard]] bool IsABot() const noexcept;
[[nodiscard]] bool IsDeleted() const;
[[nodiscard]] bool HasAvatar() const;
@@ -72,14 +79,15 @@ struct UserData {
[[nodiscard]] std::string GetDefaultAvatarURL() const;
[[nodiscard]] Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
[[nodiscard]] std::string GetMention() const;
- [[nodiscard]] std::string GetEscapedName() const;
- [[nodiscard]] std::string GetEscapedBoldName() const;
- [[nodiscard]] std::string GetEscapedString() const;
- template<bool with_at>
- [[nodiscard]] inline std::string GetEscapedBoldString() const {
- if constexpr (with_at)
- return "<b>@" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
- else
- return "<b>" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
- }
+ [[nodiscard]] std::string GetDisplayName() const;
+ [[nodiscard]] std::string GetDisplayName(Snowflake guild_id) const;
+ [[nodiscard]] std::string GetDisplayName(const std::optional<Snowflake> &guild_id) const;
+ [[nodiscard]] std::string GetDisplayNameEscaped() const;
+ [[nodiscard]] std::string GetDisplayNameEscaped(Snowflake guild_id) const;
+ [[nodiscard]] std::string GetDisplayNameEscapedBold() const;
+ [[nodiscard]] std::string GetDisplayNameEscapedBold(Snowflake guild_id) const;
+ [[nodiscard]] std::string GetUsername() const;
+ [[nodiscard]] std::string GetUsernameEscaped() const;
+ [[nodiscard]] std::string GetUsernameEscapedBold() const;
+ [[nodiscard]] std::string GetUsernameEscapedBoldAt() const;
};
diff --git a/src/misc/chatutil.cpp b/src/misc/chatutil.cpp
index a87df00..e61d32b 100644
--- a/src/misc/chatutil.cpp
+++ b/src/misc/chatutil.cpp
@@ -74,17 +74,17 @@ void HandleUserMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf, Snowflake chan
if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM || !channel->GuildID.has_value() || plain) {
if (plain) {
- replacement = "@" + user->Username + "#" + user->Discriminator;
+ replacement = "@" + user->GetUsername();
} else {
- replacement = user->GetEscapedBoldString<true>();
+ replacement = user->GetUsernameEscapedBoldAt();
}
} else {
const auto role_id = user->GetHoistedRole(*channel->GuildID, true);
const auto role = discord.GetRole(role_id);
if (!role.has_value())
- replacement = user->GetEscapedBoldString<true>();
+ replacement = user->GetUsernameEscapedBoldAt();
else
- replacement = "<span color=\"#" + IntToCSSColor(role->Color) + "\">" + user->GetEscapedBoldString<true>() + "</span>";
+ replacement = "<span color=\"#" + IntToCSSColor(role->Color) + "\">" + user->GetUsernameEscapedBoldAt() + "</span>";
}
// regex returns byte positions and theres no straightforward way in the c++ bindings to deal with that :(
diff --git a/src/windows/guildsettings/auditlogpane.cpp b/src/windows/guildsettings/auditlogpane.cpp
index 53bfd15..f2f4a17 100644
--- a/src/windows/guildsettings/auditlogpane.cpp
+++ b/src/windows/guildsettings/auditlogpane.cpp
@@ -38,7 +38,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::ustring user_markup = "<b>Unknown User</b>";
if (entry.UserID.has_value()) {
if (auto user = discord.GetUser(*entry.UserID); user.has_value())
- user_markup = discord.GetUser(*entry.UserID)->GetEscapedBoldString<false>();
+ user_markup = discord.GetUser(*entry.UserID)->GetUsernameEscapedBold();
}
// spaghetti moment
@@ -177,7 +177,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" kicked <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
} break;
case AuditLogActionType::MEMBER_PRUNE: {
@@ -193,21 +193,21 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" banned <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
} break;
case AuditLogActionType::MEMBER_BAN_REMOVE: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" removed the ban for <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
} break;
case AuditLogActionType::MEMBER_UPDATE: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" updated <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
if (entry.Changes.has_value())
for (const auto &change : *entry.Changes) {
@@ -227,7 +227,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" updated roles for <b>" +
- target_user->GetEscapedString() + "</b>";
+ target_user->GetUsernameEscaped() + "</b>";
if (entry.Changes.has_value())
for (const auto &change : *entry.Changes) {
if (change.Key == "$remove" && change.NewValue.has_value()) {
@@ -262,7 +262,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" added <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b> to the server";
} break;
case AuditLogActionType::ROLE_CREATE: {
@@ -450,14 +450,14 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" pinned a message by <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
} break;
case AuditLogActionType::MESSAGE_UNPIN: {
const auto target_user = discord.GetUser(entry.TargetID);
markup = user_markup +
" unpinned a message by <b>" +
- target_user->GetEscapedString() +
+ target_user->GetUsernameEscaped() +
"</b>";
} break;
case AuditLogActionType::STAGE_INSTANCE_CREATE: {
diff --git a/src/windows/guildsettings/banspane.cpp b/src/windows/guildsettings/banspane.cpp
index bfc7331..68a232d 100644
--- a/src/windows/guildsettings/banspane.cpp
+++ b/src/windows/guildsettings/banspane.cpp
@@ -72,7 +72,7 @@ void GuildSettingsBansPane::OnGuildBanFetch(const BanData &ban) {
auto row = *m_model->append();
row[m_columns.m_col_id] = ban.User.ID;
if (user.has_value())
- row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator;
+ row[m_columns.m_col_user] = user->GetUsername();
else
row[m_columns.m_col_user] = "<@" + std::to_string(ban.User.ID) + ">";
@@ -84,7 +84,7 @@ void GuildSettingsBansPane::OnGuildBansFetch(const std::vector<BanData> &bans) {
const auto user = Abaddon::Get().GetDiscordClient().GetUser(ban.User.ID);
auto row = *m_model->append();
row[m_columns.m_col_id] = user->ID;
- row[m_columns.m_col_user] = user->Username + "#" + user->Discriminator;
+ row[m_columns.m_col_user] = user->GetUsername();
row[m_columns.m_col_reason] = ban.Reason;
}
}
@@ -148,7 +148,7 @@ void GuildSettingsBansPane::OnBanAdd(Snowflake guild_id, Snowflake user_id) {
auto user = *discord.GetUser(user_id);
auto row = *m_model->append();
row[m_columns.m_col_id] = user_id;
- row[m_columns.m_col_user] = user.Username + "#" + user.Discriminator;
+ row[m_columns.m_col_user] = user.GetUsername();
row[m_columns.m_col_reason] = "";
}
}
diff --git a/src/windows/guildsettings/emojispane.cpp b/src/windows/guildsettings/emojispane.cpp
index c86ea64..702432f 100644
--- a/src/windows/guildsettings/emojispane.cpp
+++ b/src/windows/guildsettings/emojispane.cpp
@@ -119,7 +119,7 @@ void GuildSettingsEmojisPane::AddEmojiRow(const EmojiData &emoji) {
row[m_columns.m_col_pixbuf] = img.GetPlaceholder(32);
row[m_columns.m_col_name] = emoji.Name;
if (emoji.Creator.has_value())
- row[m_columns.m_col_creator] = emoji.Creator->Username + "#" + emoji.Creator->Discriminator;
+ row[m_columns.m_col_creator] = emoji.Creator->GetUsername();
if (emoji.IsAnimated.has_value())
row[m_columns.m_col_animated] = *emoji.IsAnimated ? "Yes" : "No";
else
diff --git a/src/windows/guildsettings/invitespane.cpp b/src/windows/guildsettings/invitespane.cpp
index 4426f6a..a76b3f2 100644
--- a/src/windows/guildsettings/invitespane.cpp
+++ b/src/windows/guildsettings/invitespane.cpp
@@ -51,7 +51,7 @@ void GuildSettingsInvitesPane::AppendInvite(const InviteData &invite) {
auto row = *m_model->append();
row[m_columns.m_col_code] = invite.Code;
if (invite.Inviter.has_value())
- row[m_columns.m_col_inviter] = invite.Inviter->Username + "#" + invite.Inviter->Discriminator;
+ row[m_columns.m_col_inviter] = invite.Inviter->GetUsername();
if (invite.MaxAge.has_value()) {
if (*invite.MaxAge == 0)
diff --git a/src/windows/guildsettings/memberspane.cpp b/src/windows/guildsettings/memberspane.cpp
index 64c28cb..8c613d0 100644
--- a/src/windows/guildsettings/memberspane.cpp
+++ b/src/windows/guildsettings/memberspane.cpp
@@ -103,7 +103,7 @@ GuildSettingsMembersListItem::GuildSettingsMembersListItem(const GuildData &guil
else
m_avatar.SetURL(member.User->GetAvatarURL("png", "32"));
- DisplayTerm = member.User->Username + "#" + member.User->Discriminator;
+ DisplayTerm = member.User->GetUsername();
const auto member_update_cb = [this](Snowflake guild_id, Snowflake user_id) {
if (user_id == UserID)
@@ -150,9 +150,9 @@ void GuildSettingsMembersListItem::UpdateColor() {
const auto user = *discord.GetUser(UserID);
if (auto color_id = discord.GetMemberHoistedRole(GuildID, UserID, true); color_id.IsValid()) {
auto role = *discord.GetRole(color_id);
- m_name.set_markup("<span color='#" + IntToCSSColor(role.Color) + "'>" + user.GetEscapedBoldString<false>() + "</span>");
+ m_name.set_markup("<span color='#" + IntToCSSColor(role.Color) + "'>" + user.GetUsernameEscapedBold() + "</span>");
} else
- m_name.set_markup(user.GetEscapedBoldString<false>());
+ m_name.set_markup(user.GetUsernameEscapedBold());
}
GuildSettingsMembersPaneInfo::GuildSettingsMembersPaneInfo(Snowflake guild_id)
diff --git a/src/windows/profile/mutualfriendspane.cpp b/src/windows/profile/mutualfriendspane.cpp
index e9f465b..75fb4a6 100644
--- a/src/windows/profile/mutualfriendspane.cpp
+++ b/src/windows/profile/mutualfriendspane.cpp
@@ -22,7 +22,7 @@ MutualFriendItem::MutualFriendItem(const UserData &user)
img.LoadFromURL(user.GetAvatarURL("png", "32"), sigc::track_obj(cb, *this));
}
- m_name.set_markup(user.GetEscapedBoldString<false>());
+ m_name.set_markup(user.GetUsernameEscapedBold());
m_name.set_valign(Gtk::ALIGN_CENTER);
add(m_avatar);
diff --git a/src/windows/profilewindow.cpp b/src/windows/profilewindow.cpp
index 3d5f140..72996aa 100644
--- a/src/windows/profilewindow.cpp
+++ b/src/windows/profilewindow.cpp
@@ -5,6 +5,7 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
, m_main(Gtk::ORIENTATION_VERTICAL)
, m_upper(Gtk::ORIENTATION_HORIZONTAL)
, m_badges(Gtk::ORIENTATION_HORIZONTAL)
+ , m_name_box(Gtk::ORIENTATION_VERTICAL)
, m_pane_info(user_id)
, m_pane_guilds(user_id)
, m_pane_friends(user_id) {
@@ -15,14 +16,15 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
set_name("user-profile");
set_default_size(450, 375);
- set_title(user.Username + "#" + user.Discriminator);
+ set_title(user.GetUsername());
set_position(Gtk::WIN_POS_CENTER);
get_style_context()->add_class("app-window");
get_style_context()->add_class("app-popup");
get_style_context()->add_class("user-profile-window");
m_main.get_style_context()->add_class("profile-main-container");
m_avatar.get_style_context()->add_class("profile-avatar");
- m_username.get_style_context()->add_class("profile-username");
+ m_displayname.get_style_context()->add_class("profile-username");
+ m_username.get_style_context()->add_class("profile-username-nondisplay");
m_switcher.get_style_context()->add_class("profile-switcher");
m_stack.get_style_context()->add_class("profile-stack");
m_badges.get_style_context()->add_class("profile-badges");
@@ -31,8 +33,8 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
m_scroll.set_vexpand(true);
m_scroll.set_propagate_natural_height(true);
- if (user.HasAvatar())
- AddPointerCursor(m_avatar_ev);
+ if (user.HasAvatar()) AddPointerCursor(m_avatar_ev);
+
m_avatar_ev.signal_button_release_event().connect([user](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_RELEASE && event->button == GDK_BUTTON_PRIMARY) {
if (user.HasAnimatedAvatar())
@@ -62,7 +64,8 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
img.LoadFromURL(user.GetAvatarURL("png", "64"), sigc::track_obj(cb, *this));
}
- m_username.set_markup(user.GetEscapedString());
+ m_displayname.set_markup(user.GetDisplayNameEscaped());
+ m_username.set_label(user.GetUsername());
m_switcher.set_stack(m_stack);
m_switcher.set_halign(Gtk::ALIGN_START);
@@ -79,10 +82,13 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
m_upper.set_halign(Gtk::ALIGN_START);
m_avatar.set_halign(Gtk::ALIGN_START);
+ m_displayname.set_halign(Gtk::ALIGN_START);
m_username.set_halign(Gtk::ALIGN_START);
m_avatar_ev.add(m_avatar);
m_upper.add(m_avatar_ev);
- m_upper.add(m_username);
+ m_upper.add(m_name_box);
+ m_name_box.add(m_displayname);
+ m_name_box.add(m_username);
m_badges_scroll.add(m_badges);
m_upper.add(m_badges_scroll);
m_main.add(m_upper);
@@ -97,8 +103,13 @@ void ProfileWindow::OnFetchProfile(const UserProfileData &data) {
m_pane_info.SetProfile(data);
m_pane_guilds.SetMutualGuilds(data.MutualGuilds);
- for (auto child : m_badges.get_children())
+ if (data.LegacyUsername.has_value()) {
+ m_username.set_tooltip_text("Originally known as " + *data.LegacyUsername);
+ }
+
+ for (auto child : m_badges.get_children()) {
delete child;
+ }
if (!data.User.PublicFlags.has_value()) return;
const auto x = *data.User.PublicFlags;
diff --git a/src/windows/profilewindow.hpp b/src/windows/profilewindow.hpp
index 2052e7b..fbc30e0 100644
--- a/src/windows/profilewindow.hpp
+++ b/src/windows/profilewindow.hpp
@@ -16,9 +16,11 @@ private:
Gtk::Box m_main;
Gtk::Box m_upper;
Gtk::Box m_badges;
+ Gtk::Box m_name_box;
Gtk::ScrolledWindow m_badges_scroll;
Gtk::EventBox m_avatar_ev;
Gtk::Image m_avatar;
+ Gtk::Label m_displayname;
Gtk::Label m_username;
Gtk::ScrolledWindow m_scroll;
Gtk::Stack m_stack;
diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp
index a915da6..a4677c6 100644
--- a/src/windows/voicewindow.cpp
+++ b/src/windows/voicewindow.cpp
@@ -39,7 +39,7 @@ public:
auto &discord = Abaddon::Get().GetDiscordClient();
const auto user = discord.GetUser(id);
if (user.has_value()) {
- m_name.set_text(user->Username);
+ m_name.set_text(user->GetUsername());
m_avatar.SetURL(user->GetAvatarURL("png", "32"));
} else {
m_name.set_text("Unknown user");