diff --git a/res/icon_header_1.ico b/res/icon_header_1.ico new file mode 100644 index 0000000..5fbfdb7 Binary files /dev/null and b/res/icon_header_1.ico differ diff --git a/res/icon_header_2.ico b/res/icon_header_2.ico new file mode 100644 index 0000000..af87d09 Binary files /dev/null and b/res/icon_header_2.ico differ diff --git a/res/icon_header_3.ico b/res/icon_header_3.ico new file mode 100644 index 0000000..95c1238 Binary files /dev/null and b/res/icon_header_3.ico differ diff --git a/res/icon_header_4.ico b/res/icon_header_4.ico new file mode 100644 index 0000000..1dc6e18 Binary files /dev/null and b/res/icon_header_4.ico differ diff --git a/res/icon_header_5.ico b/res/icon_header_5.ico new file mode 100644 index 0000000..3350dcc Binary files /dev/null and b/res/icon_header_5.ico differ diff --git a/src/discord/DiscordInstance.cpp b/src/discord/DiscordInstance.cpp index dcf95e0..bb0e3cf 100644 --- a/src/discord/DiscordInstance.cpp +++ b/src/discord/DiscordInstance.cpp @@ -659,6 +659,10 @@ void DiscordInstance::HandleRequest(void* pRequestPtr) } case MESSAGES: { + Channel* pChan = GetDiscordInstance()->GetChannel(pRequest->key); + if (!pChan) + break; + ScrollDir::eScrollDir sd = ScrollDir::BEFORE; switch (pRequest->additional_data[0]) { case /*b*/'e': sd = ScrollDir::BEFORE; break; @@ -667,7 +671,7 @@ void DiscordInstance::HandleRequest(void* pRequestPtr) } DbgPrintF("Processing request %d (%c)", sd, pRequest->additional_data[0]); uint64_t ts = GetTimeUs(); - GetMessageCache()->ProcessRequest(pRequest->key, sd, (Snowflake)GetIntFromString(pRequest->additional_data.substr(1)), j); + GetMessageCache()->ProcessRequest(pRequest->key, sd, (Snowflake)GetIntFromString(pRequest->additional_data.substr(1)), j, pChan->m_name); uint64_t te = GetTimeUs(); DbgPrintF("Total process took %lld us", te - ts); @@ -1147,8 +1151,8 @@ void DiscordInstance::JumpToMessage(Snowflake guild, Snowflake channel, Snowflak if (m_CurrentChannel != channel) { OnSelectChannel(channel); } - - GetFrontend()->JumpToMessage(message); + if (message) + GetFrontend()->JumpToMessage(message); } void DiscordInstance::LaunchURL(const std::string& url) diff --git a/src/discord/MessageCache.cpp b/src/discord/MessageCache.cpp index 7237f47..6b61c14 100644 --- a/src/discord/MessageCache.cpp +++ b/src/discord/MessageCache.cpp @@ -3,6 +3,8 @@ #include "Frontend.hpp" #include "DiscordInstance.hpp" +constexpr int MESSAGES_PER_REQUEST = 50; + using nlohmann::json; static MessageCache g_MCSingleton; @@ -19,10 +21,10 @@ void MessageCache::GetLoadedMessages(Snowflake channel, Snowflake guild, std::li out.push_back(msg.second); } -void MessageCache::ProcessRequest(Snowflake channel, ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j) +void MessageCache::ProcessRequest(Snowflake channel, ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j, const std::string& channelName) { MessageChunkList& lst = m_mapMessages[channel]; - lst.ProcessRequest(sd, anchor, j); + lst.ProcessRequest(sd, anchor, j, channelName); } void MessageCache::AddMessage(Snowflake channel, const Message& msg) @@ -74,7 +76,7 @@ MessageChunkList::MessageChunkList() m_messages[msg.m_snowflake] = msg; } -void MessageChunkList::ProcessRequest(ScrollDir::eScrollDir sd, Snowflake gap, json& j) +void MessageChunkList::ProcessRequest(ScrollDir::eScrollDir sd, Snowflake gap, json& j, const std::string& channelName) { Snowflake lowestMsg = (Snowflake) -1LL, highestMsg = 0; @@ -89,6 +91,7 @@ void MessageChunkList::ProcessRequest(ScrollDir::eScrollDir sd, Snowflake gap, j } // for each message + int receivedMessages = 0; for (json& data : j) { Message msg; @@ -101,6 +104,7 @@ void MessageChunkList::ProcessRequest(ScrollDir::eScrollDir sd, Snowflake gap, j } m_messages[msg.m_snowflake] = msg; + receivedMessages++; if (lowestMsg > msg.m_snowflake) lowestMsg = msg.m_snowflake; @@ -108,15 +112,24 @@ void MessageChunkList::ProcessRequest(ScrollDir::eScrollDir sd, Snowflake gap, j highestMsg = msg.m_snowflake; } - bool addBefore = sd != ScrollDir::AFTER; + bool addBefore = sd != ScrollDir::AFTER && receivedMessages >= MESSAGES_PER_REQUEST; bool addAfter = sd != ScrollDir::BEFORE; Message msg; - msg.m_author = GetFrontend()->GetPleaseWaitText(); + msg.m_author = ""; msg.m_message = ""; msg.m_dateFull = ""; msg.m_dateCompact = ""; + if (receivedMessages < MESSAGES_PER_REQUEST) + { + msg.m_type = MessageType::CHANNEL_HEADER; + msg.m_snowflake = 1; + msg.m_author = "#" + channelName; + m_messages[msg.m_snowflake] = msg; + } + + msg.m_author = GetFrontend()->GetPleaseWaitText(); if (addBefore && addedMessages) { msg.m_type = MessageType::GAP_UP; diff --git a/src/discord/MessageCache.hpp b/src/discord/MessageCache.hpp index e3d25b9..8127a4f 100644 --- a/src/discord/MessageCache.hpp +++ b/src/discord/MessageCache.hpp @@ -16,7 +16,7 @@ struct MessageChunkList Snowflake m_guild = 0; MessageChunkList(); - void ProcessRequest(ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j); + void ProcessRequest(ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j, const std::string& channelName); void AddMessage(const Message& msg); void EditMessage(const Message& msg); void DeleteMessage(Snowflake message); @@ -32,7 +32,7 @@ class MessageCache void GetLoadedMessages(Snowflake channel, Snowflake guild, std::list& out); // note: scroll dir used to add gap message - void ProcessRequest(Snowflake channel, ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j); + void ProcessRequest(Snowflake channel, ScrollDir::eScrollDir sd, Snowflake anchor, nlohmann::json& j, const std::string& channelName); void AddMessage(Snowflake channel, const Message& msg); void EditMessage(Snowflake channel, const Message& msg); diff --git a/src/discord/MessageType.hpp b/src/discord/MessageType.hpp index e899db8..4dbddc6 100644 --- a/src/discord/MessageType.hpp +++ b/src/discord/MessageType.hpp @@ -44,5 +44,6 @@ namespace MessageType LOADING_PINNED_MESSAGES, UNSENT_MESSAGE, SENDING_MESSAGE, + CHANNEL_HEADER, }; } diff --git a/src/discord/Util.hpp b/src/discord/Util.hpp index 2f06b59..b771818 100644 --- a/src/discord/Util.hpp +++ b/src/discord/Util.hpp @@ -41,6 +41,7 @@ std::string FormatTimeLong(time_t time, bool relativity = false); // relativity= std::string FormatTimeShort(time_t time); std::string FormatTimeShorter(time_t time); void SplitURL(const std::string& url, std::string& domainOut, std::string& resourceOut); +std::string CreateChannelLink(Snowflake guild, Snowflake channel); std::string CreateMessageLink(Snowflake guild, Snowflake channel, Snowflake message); float CompareFuzzy(const std::string& item, const char* query); // returns a "closeness" factor, =0 if no match, >0 if match. The closeness is used for sorting matches float GetAppVersion(); diff --git a/src/resource.h b/src/resource.h index f07176e..c7723b2 100644 --- a/src/resource.h +++ b/src/resource.h @@ -83,6 +83,11 @@ #define IDI_SHIFT_RIGHT 81 #define IDI_SHIFT_RIGHT_2K 82 #define IDI_ICON_2K 83 +#define IDI_HEADER_1 84 +#define IDI_HEADER_2 85 +#define IDI_HEADER_3 86 +#define IDI_HEADER_4 87 +#define IDI_HEADER_5 88 #define IDB_TARGET 200 #define IDB_CHANNEL 201 #define IDB_CATEGORY 202 @@ -292,7 +297,6 @@ #define IDC_ENABLE_TLS_CHECKS 871 #define IDC_CHECK_UPDATES 872 #define IDC_EDIT_ABOUTME 873 -#define IDC_CHECK1 874 #define IDC_DISABLE_FORMATTING 874 #define ID_FILE_PREFERENCES 1001 #define ID_FILE_STOPALLSPEECH 1002 @@ -370,7 +374,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 84 +#define _APS_NEXT_RESOURCE_VALUE 89 #define _APS_NEXT_COMMAND_VALUE 1069 #define _APS_NEXT_CONTROL_VALUE 875 #define _APS_NEXT_SYMED_VALUE 40000 diff --git a/src/resource.rc b/src/resource.rc index cec24ff..5fbf384 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -372,6 +372,16 @@ IDI_SHIFT_RIGHT_2K ICON "../res/icon_shift_right_2k.ico" IDI_ICON_2K ICON "../res/icon_discord_2k.ico" +IDI_HEADER_1 ICON "../res/icon_header_1.ico" + +IDI_HEADER_2 ICON "../res/icon_header_2.ico" + +IDI_HEADER_3 ICON "../res/icon_header_3.ico" + +IDI_HEADER_4 ICON "../res/icon_header_4.ico" + +IDI_HEADER_5 ICON "../res/icon_header_5.ico" + ///////////////////////////////////////////////////////////////////////////// // diff --git a/src/windows/MessageList.cpp b/src/windows/MessageList.cpp index dccd3fc..0c8d241 100644 --- a/src/windows/MessageList.cpp +++ b/src/windows/MessageList.cpp @@ -1514,6 +1514,24 @@ bool MessageList::IsActionMessage(MessageType::eType msgType) case MessageType::CANT_VIEW_MSG_HISTORY: case MessageType::LOADING_PINNED_MESSAGES: case MessageType::NO_PINNED_MESSAGES: + case MessageType::CHANNEL_HEADER: + return true; + } + + return false; +} + +bool MessageList::IsClientSideMessage(MessageType::eType msgType) +{ + switch (msgType) + { + case MessageType::GAP_UP: + case MessageType::GAP_DOWN: + case MessageType::GAP_AROUND: + case MessageType::CANT_VIEW_MSG_HISTORY: + case MessageType::LOADING_PINNED_MESSAGES: + case MessageType::NO_PINNED_MESSAGES: + case MessageType::CHANNEL_HEADER: return true; } @@ -1523,6 +1541,7 @@ bool MessageList::IsActionMessage(MessageType::eType msgType) // This is a horrible mess! void MessageList::DetermineMessageData( Snowflake guildID, /* IN */ + Snowflake channelID, /* IN */ MessageType::eType msgType, /* IN */ Snowflake id, /* IN */ Snowflake authorId, /* IN */ @@ -1584,6 +1603,19 @@ void MessageList::DetermineMessageData( break; } + case MessageType::CHANNEL_HEADER: + { + Channel* pChan = GetDiscordInstance()->GetChannel(channelID); + if (!pChan) { + messagePart1 = TEXT("Unknown channel"); + break; + } + + messagePart1 = TEXT("Welcome to the beginning of the "); + messagePart2 = TEXT(" channel."); + break; + } + case MessageType::CHANNEL_PINNED_MESSAGE: { messagePart1 = TEXT(""); @@ -1842,6 +1874,7 @@ int MessageList::DrawMessageReply(HDC hdc, MessageItem& item, RECT& rc) if (isActionMessage) DetermineMessageData( m_guildID, + m_channelID, refMsg.m_type, refMsg.m_snowflake, refMsg.m_author_snowflake, @@ -2045,7 +2078,12 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c if (!isFlashed) bkgdColor = GetSysColor(COLOR_3DFACE); - DrawEdge(hdc, &rect2, BDR_RAISED, edgeFlags); + if (item.m_msg.m_type != MessageType::CHANNEL_HEADER) { + DrawEdge(hdc, &rect2, BDR_RAISED, edgeFlags); + } + else if (edgeFlags & BF_MIDDLE) { + FillRect(hdc, &rect2, GetSysColorBrush(COLOR_3DFACE)); + } break; } case MS_FLAT: { @@ -2079,6 +2117,14 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c swapped = true; } + // note: Intentionally perform the swap again + if (item.m_msg.m_type == MessageType::CHANNEL_HEADER) { + COLORREF tmp = c1; + c1 = c2; + c2 = tmp; + swapped = !swapped; + } + bkgdColor = c2; if (isChainCont) { @@ -2132,6 +2178,7 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c if (isActionMessage) DetermineMessageData( m_guildID, + m_channelID, item.m_msg.m_type, item.m_msg.m_snowflake, item.m_msg.m_author_snowflake, @@ -2156,11 +2203,42 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c int dateOffset = 0; int sizePart2 = 0, sizeClick = 0, sizePart3 = 0; int actionIconSize = ScaleByDPI(16); + int oldMode = 0; + bool restoreOldMode = false; if (isActionMessage) { + if (item.m_msg.m_type == MessageType::CHANNEL_HEADER && Supports32BitIcons() && GetDeviceCaps(hdc, BITSPIXEL) >= 16) + { + restoreOldMode = true; + oldMode = SetBkMode(hdc, TRANSPARENT); + + HICON hics[5]; + int sz = ScaleByDPI(64); + + for (int i = 0; i < 5; i++) { + hics[i] = (HICON) LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_HEADER_1 + i), IMAGE_ICON, sz, sz, LR_SHARED | LR_CREATEDIBSECTION); + } + + // Render on the left side. + int x = 0; + for (int i = 0; i < 2; i++) { + DrawIconEx(hdc, x, rc.top, hics[i], sz, sz, 0, NULL, DI_NORMAL | DI_COMPAT); + x += sz; + } + + x = rc.right; + for (int i = 4; i >= 2; i--) { + x -= sz; + DrawIconEx(hdc, x, rc.top, hics[i], sz, sz, 0, NULL, DI_NORMAL | DI_COMPAT); + } + + rc.top = rc.bottom - item.m_authHeight; + } + SelectObject(hdc, g_MessageTextFont); RECT rca = rc; + RECT rcMeasure; rca.right -= ScaleByDPI(75); // TODO: Figure out why I have to do this. rcMeasure = rca; @@ -2489,6 +2567,9 @@ void MessageList::DrawMessage(HDC hdc, MessageItem& item, RECT& msgRect, RECT& c } } + if (restoreOldMode) + SetBkMode(hdc, oldMode); + // draw available embeds, if any: RECT embedRect = item.m_messageRect; auto& embedVec = item.m_embedData; @@ -2890,6 +2971,8 @@ LRESULT CALLBACK MessageList::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (!pRCMsg) break; + if (IsClientSideMessage(pRCMsg->m_msg.m_type)) break; + HMENU menu = GetSubMenu(LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MESSAGE_CONTEXT)), 0); pThis->m_rightClickedMessage = pRCMsg->m_msg.m_snowflake; @@ -3699,6 +3782,10 @@ void MessageList::AdjustHeightInfo(const MessageItem& msg, int& height, int& tex if (!IsActionMessage(msg.m_msg.m_type) && !IsCompact() && !isChainCont && height < minHeight) height = minHeight; + + if (msg.m_msg.m_type == MessageType::CHANNEL_HEADER) { + height = ScaleByDPI(64); + } } bool MessageList::ShouldBeDateGap(time_t oldTime, time_t newTime) diff --git a/src/windows/MessageList.hpp b/src/windows/MessageList.hpp index 8c889d7..783814d 100644 --- a/src/windows/MessageList.hpp +++ b/src/windows/MessageList.hpp @@ -481,6 +481,7 @@ class MessageList protected: friend class MessageItem; static bool IsActionMessage(MessageType::eType msgType); + static bool IsClientSideMessage(MessageType::eType msgType); private: void HitTestAuthor(POINT pt, BOOL& hit); @@ -511,6 +512,7 @@ class MessageList // This is a horrible mess! static void DetermineMessageData( Snowflake guildID, /* IN */ + Snowflake channelID, /* IN */ MessageType::eType msgType, /* IN */ Snowflake id, /* IN */ Snowflake authorId, /* IN */ diff --git a/src/windows/WinUtils.cpp b/src/windows/WinUtils.cpp index 59a872b..cd84a1a 100644 --- a/src/windows/WinUtils.cpp +++ b/src/windows/WinUtils.cpp @@ -255,6 +255,14 @@ std::string MakeStringFromUnicodeString(LPCWSTR wstr) return final_str; } +std::string CreateChannelLink(Snowflake guild, Snowflake channel) +{ + std::string guildStr = std::to_string(guild); + if (!guild) guildStr = "@me"; + + return "https://discord.com/channels/" + guildStr + "/" + std::to_string(channel); +} + std::string CreateMessageLink(Snowflake guild, Snowflake channel, Snowflake message) { std::string guildStr = std::to_string(guild); diff --git a/vs/DiscordMessenger.vcxproj b/vs/DiscordMessenger.vcxproj index 7b797ff..4a2b18f 100644 --- a/vs/DiscordMessenger.vcxproj +++ b/vs/DiscordMessenger.vcxproj @@ -248,6 +248,11 @@ + + + + + diff --git a/vs/DiscordMessenger.vcxproj.filters b/vs/DiscordMessenger.vcxproj.filters index e4159e0..ac1d7e0 100644 --- a/vs/DiscordMessenger.vcxproj.filters +++ b/vs/DiscordMessenger.vcxproj.filters @@ -342,6 +342,21 @@ Resource Files\Resources + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files +