merge master

This commit is contained in:
ouwou 2021-11-28 22:48:30 -05:00
commit e1703aea3f
222 changed files with 3014 additions and 2231 deletions

View File

@ -95,9 +95,9 @@ jobs:
del /f /s /q "${{ runner.workspace }}\build\.ninja_log" del /f /s /q "${{ runner.workspace }}\build\.ninja_log"
del /f /s /q "${{ runner.workspace }}\build\abaddon.ilk" del /f /s /q "${{ runner.workspace }}\build\abaddon.ilk"
del /f /s /q "${{ runner.workspace }}\build\CMakeCache.txt" del /f /s /q "${{ runner.workspace }}\build\CMakeCache.txt"
xcopy /E /I "${{ github.workspace }}\css" "${{ runner.workspace }}\build\css" xcopy /E /I "${{ github.workspace }}\res\css" "${{ runner.workspace }}\build\css"
xcopy /E /I "${{ github.workspace }}\res" "${{ runner.workspace }}\build\res" xcopy /E /I "${{ github.workspace }}\res\res" "${{ runner.workspace }}\build\res"
xcopy /E /I "${{ github.workspace }}\fonts" "${{ runner.workspace }}\build\fonts" xcopy /E /I "${{ github.workspace }}\res\fonts" "${{ runner.workspace }}\build\fonts"
mkdir "${{ runner.workspace }}\build\share" mkdir "${{ runner.workspace }}\build\share"
xcopy /E /I "${{ github.workspace }}\ci\gtk-for-windows\gtk-nsis-pack\share\glib-2.0" "${{ runner.workspace }}\build\share\glib-2.0" xcopy /E /I "${{ github.workspace }}\ci\gtk-for-windows\gtk-nsis-pack\share\glib-2.0" "${{ runner.workspace }}\build\share\glib-2.0"
copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper.exe" "${{ runner.workspace }}\build\gspawn-win64-helper.exe" copy "${{ github.workspace }}\ci\vcpkg\installed\x64-windows\tools\glib\gspawn-win64-helper.exe" "${{ runner.workspace }}\build\gspawn-win64-helper.exe"
@ -137,8 +137,8 @@ jobs:
run: | run: |
mkdir "${{ runner.workspace }}/artifactdir" mkdir "${{ runner.workspace }}/artifactdir"
cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon" cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon"
cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css" cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css"
cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res" cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res"
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
@ -190,8 +190,8 @@ jobs:
run: | run: |
mkdir "${{ runner.workspace }}/artifactdir" mkdir "${{ runner.workspace }}/artifactdir"
cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon" cp "${{runner.workspace}}/build/abaddon" "${{ runner.workspace }}/artifactdir/abaddon"
cp -r "${{ github.workspace }}/css" "${{ runner.workspace }}/artifactdir/css" cp -r "${{ github.workspace }}/res/css" "${{ runner.workspace }}/artifactdir/css"
cp -r "${{ github.workspace }}/res" "${{ runner.workspace }}/artifactdir/res" cp -r "${{ github.workspace }}/res/res" "${{ runner.workspace }}/artifactdir/res"
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2

9
.gitmodules vendored
View File

@ -1,15 +1,12 @@
[submodule "vcpkg"] [submodule "vcpkg"]
path = ci/vcpkg path = ci/vcpkg
url = https://github.com/microsoft/vcpkg/ url = https://github.com/microsoft/vcpkg/
[submodule "thirdparty/simpleini"]
path = thirdparty/simpleini
url = https://github.com/brofield/simpleini
[submodule "thirdparty/IXWebSocket"]
path = thirdparty/IXWebSocket
url = https://github.com/machinezone/ixwebsocket
[submodule "ci/vcpkg"] [submodule "ci/vcpkg"]
path = ci/vcpkg path = ci/vcpkg
url = https://github.com/microsoft/vcpkg url = https://github.com/microsoft/vcpkg
[submodule "ci/gtk-for-windows"] [submodule "ci/gtk-for-windows"]
path = ci/gtk-for-windows path = ci/gtk-for-windows
url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer
[submodule "subprojects/ixwebsocket"]
path = subprojects/ixwebsocket
url = https://github.com/machinezone/ixwebsocket

View File

@ -18,17 +18,10 @@ set(USE_OPEN_SSL TRUE)
find_package(IXWebSocket QUIET) find_package(IXWebSocket QUIET)
if (NOT IXWebSocket_FOUND) if (NOT IXWebSocket_FOUND)
message("ixwebsocket was not found and will be included as a submodule") message("ixwebsocket was not found and will be included as a submodule")
add_subdirectory(thirdparty/IXWebSocket) add_subdirectory(subprojects/ixwebsocket)
include_directories(IXWEBSOCKET_INCLUDE_DIRS) include_directories(IXWEBSOCKET_INCLUDE_DIRS)
endif() endif()
add_compile_definitions(SI_NO_CONVERSION) # only CSimpleIniA is used
find_package(simpleini QUIET)
if (NOT simpleini_FOUND)
message("simpleini was not found and will be included as a submodule")
include_directories(thirdparty/simpleini)
endif()
if(MINGW OR WIN32) if(MINGW OR WIN32)
link_libraries(ws2_32) link_libraries(ws2_32)
endif() endif()
@ -41,28 +34,16 @@ if(WIN32)
link_libraries(${Fontconfig_LIBRARIES}) link_libraries(${Fontconfig_LIBRARIES})
endif() endif()
configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h) configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/config.h)
file(GLOB ABADDON_SOURCES file(GLOB_RECURSE ABADDON_SOURCES
"*.h" "src/*.h"
"*.hpp" "src/*.hpp"
"*.cpp" "src/*.cpp"
"discord/*.hpp"
"discord/*.cpp"
"components/*.hpp"
"components/*.cpp"
"windows/*.hpp"
"windows/*.cpp"
"windows/guildsettings/*.hpp"
"windows/guildsettings/*.cpp"
"windows/profile/*.hpp"
"windows/profile/*.cpp"
"dialogs/*.hpp"
"dialogs/*.cpp"
) )
add_executable(abaddon ${ABADDON_SOURCES}) add_executable(abaddon ${ABADDON_SOURCES})
target_include_directories(abaddon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(abaddon PUBLIC ${PROJECT_SOURCE_DIR}/src)
target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR}) target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR})
target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS})
target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS})

View File

@ -4,7 +4,7 @@ Alternative Discord client made in C++ with GTK
<img src="/.readme/s3.png"> <img src="/.readme/s3.png">
[😎Discord Server](https://discord.gg/wkCU3vuzG5) <a href="https://discord.gg/wkCU3vuzG5"><img src="https://discord.com/api/guilds/858156817711890443/widget.png?style=shield"></a>
Current features: Current features:
* Not Electron * Not Electron
@ -32,7 +32,7 @@ Current features:
### Building manually (recommended if not on Windows): ### Building manually (recommended if not on Windows):
#### Windows: #### Windows:
1. `git clone https://github.com/uowuo/abaddon && cd abaddon` 1. `git clone https://github.com/uowuo/abaddon && cd abaddon`
2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows simpleini:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows` 2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows`
3. `mkdir build && cd build` 3. `mkdir build && cd build`
4. `cmake -G"Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=c:\path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVCPKG_TARGET_TRIPLET=x64-windows ..` 4. `cmake -G"Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=c:\path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVCPKG_TARGET_TRIPLET=x64-windows ..`
5. Build with Visual Studio 5. Build with Visual Studio
@ -75,7 +75,6 @@ On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/
* [IXWebSocket](https://github.com/machinezone/IXWebSocket) * [IXWebSocket](https://github.com/machinezone/IXWebSocket)
* [libcurl](https://curl.se/) * [libcurl](https://curl.se/)
* [zlib](https://zlib.net/) * [zlib](https://zlib.net/)
* [simpleini](https://github.com/brofield/simpleini)
* [SQLite3](https://www.sqlite.org/index.html) * [SQLite3](https://www.sqlite.org/index.html)
### TODO: ### TODO:
@ -178,18 +177,24 @@ Used in profile popup:
### Settings ### Settings
Settings are configured (for now) by editing abaddon.ini Settings are configured (for now) by editing abaddon.ini
The format is similar to the standard Windows ini format **except**:
* `#` is used to begin comments as opposed to `;`
* Section and key names are case-sensitive
You should edit these while the client is closed even though there's an option to reload while running You should edit these while the client is closed even though there's an option to reload while running
This listing is organized by section. This listing is organized by section.
For example, memory_db would be set by adding `memory_db = true` under the line `[discord]` For example, memory_db would be set by adding `memory_db = true` under the line `[discord]`
#### discord #### discord
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
* api_base (string) - override base url for Discord API
* memory_db (true or false, default false) - if true, Discord data will be kept in memory as opposed to on disk * memory_db (true or false, default false) - if true, Discord data will be kept in memory as opposed to on disk
* token (string) - Discord token used to login, this can be set from the menu * token (string) - Discord token used to login, this can be set from the menu
* prefetch (true or false, default false) - if true, new messages will cause the avatar and image attachments to be automatically downloaded * prefetch (true or false, default false) - if true, new messages will cause the avatar and image attachments to be automatically downloaded
#### http #### http
* user_agent (string) - sets the user-agent to use in HTTP requests to the Discord API (not including media/images) * user_agent (string) - sets the user-agent to use in HTTP requests to the Discord API (not including media/images)
* concurrent (int, default 10) - how many images can be concurrently retrieved * concurrent (int, default 20) - how many images can be concurrently retrieved
#### gui #### gui
* member_list_discriminator (true or false, default true) - show user discriminators in the member list * member_list_discriminator (true or false, default true) - show user discriminators in the member list
@ -199,8 +204,6 @@ For example, memory_db would be set by adding `memory_db = true` under the line
* animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used * animations (true or false, default true) - use animated images where available (e.g. server icons, emojis, avatars). false means static images will be used
* animated_guild_hover_only (true or false, default true) - only animate guild icons when the guild is being hovered over * animated_guild_hover_only (true or false, default true) - only animate guild icons when the guild is being hovered over
* owner_crown (true or false, default true) - show a crown next to the owner * owner_crown (true or false, default true) - show a crown next to the owner
* gateway (string) - override url for Discord gateway. must be json format and use zlib stream compression
* api_base (string) - override base url for Discord API
#### style #### style
* linkcolor (string) - color to use for links in messages * linkcolor (string) - color to use for links in messages

View File

@ -1,15 +0,0 @@
set(simpleini_LIBRARY_NAME simpleini)
find_path(simpleini_INCLUDE_DIR
NAMES SimpleIni.h
HINTS /usr/include
/usr/local/include
/opt/local/include
PATH_SUFFIXES ${simpleini_LIBRARY_NAME})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(simpleini
REQUIRED_VARS
simpleini_INCLUDE_DIR)
mark_as_advanced(simpleini_INCLUDE_DIR)

File diff suppressed because it is too large Load Diff

View File

@ -1,172 +0,0 @@
#pragma once
#include "../util.hpp"
#include "objects.hpp"
#include <unordered_map>
#include <unordered_set>
#include <mutex>
#include <filesystem>
#include <sqlite3.h>
#ifdef GetMessage // fuck you windows.h
#undef GetMessage
#endif
class Store {
public:
Store(bool mem_store = false);
~Store();
bool IsValid() const;
void SetUser(Snowflake id, const UserData &user);
void SetChannel(Snowflake id, const ChannelData &chan);
void SetGuild(Snowflake id, const GuildData &guild);
void SetRole(Snowflake id, const RoleData &role);
void SetMessage(Snowflake id, const Message &message);
void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data);
void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm);
void SetEmoji(Snowflake id, const EmojiData &emoji);
void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban);
// slap const on everything even tho its not *really* const
std::optional<ChannelData> GetChannel(Snowflake id) const;
std::optional<EmojiData> GetEmoji(Snowflake id) const;
std::optional<GuildData> GetGuild(Snowflake id) const;
std::optional<GuildMember> GetGuildMember(Snowflake guild_id, Snowflake user_id) const;
std::optional<Message> GetMessage(Snowflake id) const;
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
std::optional<RoleData> GetRole(Snowflake id) const;
std::optional<UserData> GetUser(Snowflake id) const;
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
std::vector<BanData> GetBans(Snowflake guild_id) const;
std::vector<Message> GetLastMessages(Snowflake id, size_t num) const;
std::vector<Snowflake> GetChannelMessageIDs(Snowflake id) const;
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const; // public
void ClearGuild(Snowflake id);
void ClearChannel(Snowflake id);
void ClearBan(Snowflake guild_id, Snowflake user_id);
using users_type = std::unordered_map<Snowflake, UserData>;
using channels_type = std::unordered_map<Snowflake, ChannelData>;
using guilds_type = std::unordered_map<Snowflake, GuildData>;
using roles_type = std::unordered_map<Snowflake, RoleData>;
using messages_type = std::unordered_map<Snowflake, Message>;
using members_type = std::unordered_map<Snowflake, std::unordered_map<Snowflake, GuildMember>>; // [guild][user]
using permission_overwrites_type = std::unordered_map<Snowflake, std::unordered_map<Snowflake, PermissionOverwrite>>; // [channel][user/role]
using emojis_type = std::unordered_map<Snowflake, EmojiData>;
const std::unordered_set<Snowflake> &GetChannels() const;
const std::unordered_set<Snowflake> &GetGuilds() const;
void ClearAll();
void BeginTransaction();
void EndTransaction();
private:
Message GetMessageBound(sqlite3_stmt *stmt) const;
void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction);
std::unordered_set<Snowflake> m_channels;
std::unordered_set<Snowflake> m_guilds;
bool CreateTables();
bool CreateStatements();
void Cleanup();
template<typename T>
void Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const;
template<typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type
Bind(sqlite3_stmt *stmt, int index, T val) const;
void Bind(sqlite3_stmt *stmt, int index, int num) const;
void Bind(sqlite3_stmt *stmt, int index, uint64_t num) const;
void Bind(sqlite3_stmt *stmt, int index, const std::string &str) const;
void Bind(sqlite3_stmt *stmt, int index, bool val) const;
void Bind(sqlite3_stmt *stmt, int index, std::nullptr_t) const;
bool RunInsert(sqlite3_stmt *stmt);
bool FetchOne(sqlite3_stmt *stmt) const;
template<typename T>
void Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const;
template<typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type
Get(sqlite3_stmt *stmt, int index, T &out) const;
void Get(sqlite3_stmt *stmt, int index, int &out) const;
void Get(sqlite3_stmt *stmt, int index, uint64_t &out) const;
void Get(sqlite3_stmt *stmt, int index, std::string &out) const;
void Get(sqlite3_stmt *stmt, int index, bool &out) const;
void Get(sqlite3_stmt *stmt, int index, Snowflake &out) const;
bool IsNull(sqlite3_stmt *stmt, int index) const;
void Reset(sqlite3_stmt *stmt) const;
std::filesystem::path m_db_path;
mutable sqlite3 *m_db;
mutable int m_db_err;
mutable sqlite3_stmt *m_set_user_stmt;
mutable sqlite3_stmt *m_get_user_stmt;
mutable sqlite3_stmt *m_set_perm_stmt;
mutable sqlite3_stmt *m_get_perm_stmt;
mutable sqlite3_stmt *m_set_msg_stmt;
mutable sqlite3_stmt *m_get_msg_stmt;
mutable sqlite3_stmt *m_set_role_stmt;
mutable sqlite3_stmt *m_get_role_stmt;
mutable sqlite3_stmt *m_set_emote_stmt;
mutable sqlite3_stmt *m_get_emote_stmt;
mutable sqlite3_stmt *m_set_member_stmt;
mutable sqlite3_stmt *m_get_member_stmt;
mutable sqlite3_stmt *m_set_guild_stmt;
mutable sqlite3_stmt *m_get_guild_stmt;
mutable sqlite3_stmt *m_set_chan_stmt;
mutable sqlite3_stmt *m_get_chan_stmt;
mutable sqlite3_stmt *m_set_ban_stmt;
mutable sqlite3_stmt *m_get_ban_stmt;
mutable sqlite3_stmt *m_clear_ban_stmt;
mutable sqlite3_stmt *m_get_bans_stmt;
mutable sqlite3_stmt *m_set_msg_interaction_stmt;
mutable sqlite3_stmt *m_get_last_msgs_stmt;
mutable sqlite3_stmt *m_get_msg_ids_stmt;
mutable sqlite3_stmt *m_get_pins_stmt;
mutable sqlite3_stmt *m_get_threads_stmt;
mutable sqlite3_stmt *m_clear_chan_stmt;
};
template<typename T>
inline void Store::Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const {
if (opt.has_value())
Bind(stmt, index, *opt);
else
sqlite3_bind_null(stmt, index);
}
template<typename T>
inline typename std::enable_if<std::is_enum<T>::value, void>::type
Store::Bind(sqlite3_stmt *stmt, int index, T val) const {
Bind(stmt, index, static_cast<typename std::underlying_type<T>::type>(val));
}
template<typename T>
inline void Store::Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const {
if (sqlite3_column_type(stmt, index) == SQLITE_NULL)
out = std::nullopt;
else {
T v;
Get(stmt, index, v);
out = std::optional<T>(v);
}
}
template<typename T>
inline typename std::enable_if<std::is_enum<T>::value, void>::type
Store::Get(sqlite3_stmt *stmt, int index, T &out) const {
out = static_cast<T>(sqlite3_column_int(stmt, index));
}

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,115 +0,0 @@
#include "settings.hpp"
#include <filesystem>
#include <fstream>
SettingsManager::SettingsManager(std::string filename)
: m_filename(filename) {
if (!std::filesystem::exists(filename)) {
std::fstream fs;
fs.open(filename, std::ios::out);
fs.close();
}
auto rc = m_ini.LoadFile(filename.c_str());
m_ok = rc == SI_OK;
}
void SettingsManager::Reload() {
m_ok = m_ini.LoadFile(m_filename.c_str()) == SI_OK;
}
std::string SettingsManager::GetSettingString(const std::string &section, const std::string &key, std::string fallback) const {
return m_ini.GetValue(section.c_str(), key.c_str(), fallback.c_str());
}
int SettingsManager::GetSettingInt(const std::string &section, const std::string &key, int fallback) const {
return std::stoul(GetSettingString(section, key, std::to_string(fallback)));
}
bool SettingsManager::GetSettingBool(const std::string &section, const std::string &key, bool fallback) const {
return GetSettingString(section, key, fallback ? "true" : "false") != "false";
}
bool SettingsManager::IsValid() const {
return m_ok;
}
void SettingsManager::Close() {
m_ini.SaveFile(m_filename.c_str());
}
bool SettingsManager::GetUseMemoryDB() const {
return GetSettingBool("discord", "memory_db", false);
}
std::string SettingsManager::GetUserAgent() const {
return GetSettingString("http", "user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36");
}
std::string SettingsManager::GetDiscordToken() const {
return GetSettingString("discord", "token");
}
bool SettingsManager::GetShowMemberListDiscriminators() const {
return GetSettingBool("gui", "member_list_discriminator", true);
}
bool SettingsManager::GetShowStockEmojis() const {
#ifdef _WIN32
return GetSettingBool("gui", "stock_emojis", false);
#else
return GetSettingBool("gui", "stock_emojis", true);
#endif
}
bool SettingsManager::GetShowCustomEmojis() const {
return GetSettingBool("gui", "custom_emojis", true);
}
std::string SettingsManager::GetLinkColor() const {
return GetSettingString("style", "linkcolor", "rgba(40, 200, 180, 255)");
}
std::string SettingsManager::GetChannelsExpanderColor() const {
return GetSettingString("style", "expandercolor", "rgba(255, 83, 112, 255)");
}
std::string SettingsManager::GetNSFWChannelColor() const {
return GetSettingString("style", "nsfwchannelcolor", "#ed6666");
}
int SettingsManager::GetCacheHTTPConcurrency() const {
return GetSettingInt("http", "concurrent", 20);
}
bool SettingsManager::GetPrefetch() const {
return GetSettingBool("discord", "prefetch", false);
}
std::string SettingsManager::GetMainCSS() const {
return GetSettingString("gui", "css", "main.css");
}
bool SettingsManager::GetShowAnimations() const {
return GetSettingBool("gui", "animations", true);
}
bool SettingsManager::GetShowOwnerCrown() const {
return GetSettingBool("gui", "owner_crown", true);
}
std::string SettingsManager::GetGatewayURL() const {
return GetSettingString("discord", "gateway", "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream");
}
std::string SettingsManager::GetAPIBaseURL() const {
return GetSettingString("discord", "api_base", "https://discord.com/api/v9");
}
bool SettingsManager::GetAnimatedGuildHoverOnly() const {
return GetSettingBool("gui", "animated_guild_hover_only", true);
}
bool SettingsManager::GetSaveState() const {
return GetSettingBool("gui", "save_state", true);
}

View File

@ -1,62 +0,0 @@
#pragma once
#include <string>
#include <type_traits>
#include <SimpleIni.h>
class SettingsManager {
public:
SettingsManager(std::string filename);
void Reload();
void Close();
bool GetUseMemoryDB() const;
std::string GetUserAgent() const;
std::string GetDiscordToken() const;
bool GetShowMemberListDiscriminators() const;
bool GetShowStockEmojis() const;
bool GetShowCustomEmojis() const;
int GetCacheHTTPConcurrency() const;
bool GetPrefetch() const;
std::string GetMainCSS() const;
bool GetShowAnimations() const;
bool GetShowOwnerCrown() const;
std::string GetGatewayURL() const;
std::string GetAPIBaseURL() const;
bool GetAnimatedGuildHoverOnly() const;
bool GetSaveState() const;
// i would like to use Gtk::StyleProperty for this, but it will not work on windows
// #1 it's missing from the project files for the version used by vcpkg
// #2 it's still broken and doesn't function even when added to the solution
// #3 it's a massive pain in the ass to try and bump the version to a functioning version
// because they switch build systems to nmake/meson (took months to get merged in vcpkg)
// #4 c++ build systems sucks
// three options are: use gtk4 with updated vcpkg, try and port it myself, or use msys2 instead of vcpkg
// im leaning towards msys
std::string GetLinkColor() const;
std::string GetChannelsExpanderColor() const;
std::string GetNSFWChannelColor() const;
bool IsValid() const;
template<typename T>
void SetSetting(std::string section, std::string key, T value) {
m_ini.SetValue(section.c_str(), key.c_str(), std::to_string(value).c_str());
m_ini.SaveFile(m_filename.c_str());
}
void SetSetting(std::string section, std::string key, std::string value) {
m_ini.SetValue(section.c_str(), key.c_str(), value.c_str());
m_ini.SaveFile(m_filename.c_str());
}
private:
std::string GetSettingString(const std::string &section, const std::string &key, std::string fallback = "") const;
int GetSettingInt(const std::string &section, const std::string &key, int fallback) const;
bool GetSettingBool(const std::string &section, const std::string &key, bool fallback) const;
private:
bool m_ok;
std::string m_filename;
CSimpleIniA m_ini;
};

View File

@ -23,12 +23,12 @@
Abaddon::Abaddon() Abaddon::Abaddon()
: m_settings(Platform::FindConfigFile()) : m_settings(Platform::FindConfigFile())
, m_discord(m_settings.GetUseMemoryDB()) // stupid but easy , m_discord(GetSettings().UseMemoryDB) // stupid but easy
, m_emojis(GetResPath("/emojis.bin")) { , m_emojis(GetResPath("/emojis.bin")) {
LoadFromSettings(); LoadFromSettings();
// todo: set user agent for non-client(?) // todo: set user agent for non-client(?)
std::string ua = m_settings.GetUserAgent(); std::string ua = GetSettings().UserAgent;
m_discord.SetUserAgent(ua); m_discord.SetUserAgent(ua);
m_discord.signal_gateway_ready().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReady)); m_discord.signal_gateway_ready().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReady));
@ -43,7 +43,7 @@ Abaddon::Abaddon()
m_discord.signal_thread_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnThreadUpdate)); m_discord.signal_thread_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnThreadUpdate));
m_discord.signal_message_sent().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageSent)); m_discord.signal_message_sent().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageSent));
m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect)); m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect));
if (m_settings.GetPrefetch()) if (GetSettings().Prefetch)
m_discord.signal_message_create().connect([this](const Message &message) { m_discord.signal_message_create().connect([this](const Message &message) {
if (message.Author.HasAvatar()) if (message.Author.HasAvatar())
m_img_mgr.Prefetch(message.Author.GetAvatarURL()); m_img_mgr.Prefetch(message.Author.GetAvatarURL());
@ -54,10 +54,6 @@ Abaddon::Abaddon()
}); });
} }
Abaddon::~Abaddon() {
m_settings.Close();
}
Abaddon &Abaddon::Get() { Abaddon &Abaddon::Get() {
static Abaddon instance; static Abaddon instance;
return instance; return instance;
@ -82,9 +78,30 @@ int Abaddon::StartGTK() {
m_main_window = std::make_unique<MainWindow>(); m_main_window = std::make_unique<MainWindow>();
m_main_window->set_title(APP_TITLE); m_main_window->set_title(APP_TITLE);
m_main_window->UpdateComponents();
m_main_window->set_position(Gtk::WIN_POS_CENTER); m_main_window->set_position(Gtk::WIN_POS_CENTER);
if (!m_settings.IsValid()) {
Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be opened!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
if (!m_emojis.Load()) {
Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
if (!m_discord.IsStoreValid()) {
Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
return 1;
}
// store must be checked before this can be called
m_main_window->UpdateComponents();
// crashes for some stupid reason if i put it somewhere else // crashes for some stupid reason if i put it somewhere else
SetupUserMenu(); SetupUserMenu();
@ -112,33 +129,19 @@ int Abaddon::StartGTK() {
ActionReloadCSS(); ActionReloadCSS();
m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::StopDiscord), false); m_gtk_app->signal_shutdown().connect(sigc::mem_fun(*this, &Abaddon::OnShutdown), false);
if (!m_settings.IsValid()) {
Gtk::MessageDialog dlg(*m_main_window, "The settings file could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
if (!m_emojis.Load()) {
Gtk::MessageDialog dlg(*m_main_window, "The emoji file couldn't be loaded!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
if (!m_discord.IsStoreValid()) {
Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
return 1;
}
m_main_window->show(); m_main_window->show();
return m_gtk_app->run(*m_main_window); return m_gtk_app->run(*m_main_window);
} }
void Abaddon::OnShutdown() {
StopDiscord();
m_settings.Close();
}
void Abaddon::LoadFromSettings() { void Abaddon::LoadFromSettings() {
std::string token = m_settings.GetDiscordToken(); std::string token = GetSettings().DiscordToken;
if (token.size()) { if (token.size()) {
m_discord_token = token; m_discord_token = token;
m_discord.UpdateToken(m_discord_token); m_discord.UpdateToken(m_discord_token);
@ -246,8 +249,8 @@ void Abaddon::DiscordOnThreadUpdate(const ThreadUpdateData &data) {
} }
} }
const SettingsManager &Abaddon::GetSettings() const { SettingsManager::Settings &Abaddon::GetSettings() {
return m_settings; return m_settings.GetSettings();
} }
Glib::RefPtr<Gtk::CssProvider> Abaddon::GetStyleProvider() { Glib::RefPtr<Gtk::CssProvider> Abaddon::GetStyleProvider() {
@ -365,7 +368,7 @@ void Abaddon::SetupUserMenu() {
} }
void Abaddon::SaveState() { void Abaddon::SaveState() {
if (!m_settings.GetSaveState()) return; if (!GetSettings().SaveState) return;
AbaddonApplicationState state; AbaddonApplicationState state;
state.ActiveChannel = m_main_window->GetChatActiveChannel(); state.ActiveChannel = m_main_window->GetChatActiveChannel();
@ -385,7 +388,7 @@ void Abaddon::SaveState() {
} }
void Abaddon::LoadState() { void Abaddon::LoadState() {
if (!m_settings.GetSaveState()) return; if (!GetSettings().SaveState) return;
const auto data = ReadWholeFile(GetStateCachePath("/state.json")); const auto data = ReadWholeFile(GetStateCachePath("/state.json"));
if (data.empty()) return; if (data.empty()) return;
@ -489,7 +492,7 @@ void Abaddon::ActionSetToken() {
m_discord_token = dlg.GetToken(); m_discord_token = dlg.GetToken();
m_discord.UpdateToken(m_discord_token); m_discord.UpdateToken(m_discord_token);
m_main_window->UpdateComponents(); m_main_window->UpdateComponents();
m_settings.SetSetting("discord", "token", m_discord_token); GetSettings().DiscordToken = m_discord_token;
} }
} }
@ -552,16 +555,9 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) {
return; return;
Snowflake before_id = m_main_window->GetChatOldestListedMessage(); Snowflake before_id = m_main_window->GetChatOldestListedMessage();
auto knownset = m_discord.GetMessageIDsForChannel(id); auto msgs = m_discord.GetMessagesBefore(id, before_id);
std::vector<Snowflake> knownvec(knownset.begin(), knownset.end());
std::sort(knownvec.begin(), knownvec.end());
auto latest = std::find_if(knownvec.begin(), knownvec.end(), [&before_id](Snowflake x) -> bool { return x == before_id; });
int distance = std::distance(knownvec.begin(), latest);
if (distance >= 50) { if (msgs.size() >= 50) {
std::vector<Message> msgs;
for (auto it = knownvec.begin() + distance - 50; it != knownvec.begin() + distance; it++)
msgs.push_back(*m_discord.GetMessage(*it));
m_main_window->UpdateChatPrependHistory(msgs); m_main_window->UpdateChatPrependHistory(msgs);
return; return;
} }
@ -703,7 +699,7 @@ bool Abaddon::ShowConfirm(const Glib::ustring &prompt, Gtk::Window *window) {
void Abaddon::ActionReloadCSS() { void Abaddon::ActionReloadCSS() {
try { try {
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider); Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_provider);
m_css_provider->load_from_path(GetCSSPath("/" + m_settings.GetMainCSS())); m_css_provider->load_from_path(GetCSSPath("/" + GetSettings().MainCSS));
Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); Gtk::StyleContext::add_provider_for_screen(Gdk::Screen::get_default(), m_css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider); Gtk::StyleContext::remove_provider_for_screen(Gdk::Screen::get_default(), m_css_low_provider);

View File

@ -14,7 +14,6 @@
class Abaddon { class Abaddon {
private: private:
Abaddon(); Abaddon();
~Abaddon();
Abaddon(const Abaddon &) = delete; Abaddon(const Abaddon &) = delete;
Abaddon &operator=(const Abaddon &) = delete; Abaddon &operator=(const Abaddon &) = delete;
Abaddon(Abaddon &&) = delete; Abaddon(Abaddon &&) = delete;
@ -24,6 +23,8 @@ public:
static Abaddon &Get(); static Abaddon &Get();
int StartGTK(); int StartGTK();
void OnShutdown();
void StartDiscord(); void StartDiscord();
void StopDiscord(); void StopDiscord();
@ -74,7 +75,7 @@ public:
void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code); void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code);
void DiscordOnThreadUpdate(const ThreadUpdateData &data); void DiscordOnThreadUpdate(const ThreadUpdateData &data);
const SettingsManager &GetSettings() const; SettingsManager::Settings &GetSettings();
Glib::RefPtr<Gtk::CssProvider> GetStyleProvider(); Glib::RefPtr<Gtk::CssProvider> GetStyleProvider();

View File

@ -2,9 +2,9 @@
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include "../abaddon.hpp" #include "abaddon.hpp"
#include "../imgmanager.hpp" #include "imgmanager.hpp"
#include "../util.hpp" #include "util.hpp"
#include "statusindicator.hpp" #include "statusindicator.hpp"
ChannelList::ChannelList() ChannelList::ChannelList()
@ -263,11 +263,9 @@ void ChannelList::UpdateGuild(Snowflake id) {
const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id); const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(id);
if (!iter || !guild.has_value()) return; if (!iter || !guild.has_value()) return;
static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
(*iter)[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild->Name) + "</b>"; (*iter)[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild->Name) + "</b>";
(*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); (*iter)[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
if (show_animations && guild->HasAnimatedIcon()) { if (Abaddon::Get().GetSettings().ShowAnimations && guild->HasAnimatedIcon()) {
const auto cb = [this, id](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { const auto cb = [this, id](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) {
auto iter = GetIteratorForGuildFromID(id); auto iter = GetIteratorForGuildFromID(id);
if (iter) (*iter)[m_columns.m_icon_anim] = pb; if (iter) (*iter)[m_columns.m_icon_anim] = pb;
@ -436,9 +434,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
guild_row[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild.Name) + "</b>"; guild_row[m_columns.m_name] = "<b>" + Glib::Markup::escape_text(guild.Name) + "</b>";
guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize); guild_row[m_columns.m_icon] = img.GetPlaceholder(GuildIconSize);
static const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations(); if (Abaddon::Get().GetSettings().ShowAnimations && guild.HasAnimatedIcon()) {
if (show_animations && guild.HasAnimatedIcon()) {
const auto cb = [this, id = guild.ID](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) { const auto cb = [this, id = guild.ID](const Glib::RefPtr<Gdk::PixbufAnimation> &pb) {
auto iter = GetIteratorForGuildFromID(id); auto iter = GetIteratorForGuildFromID(id);
if (iter) (*iter)[m_columns.m_icon_anim] = pb; if (iter) (*iter)[m_columns.m_icon_anim] = pb;
@ -998,7 +994,7 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);
const static bool hover_only = Abaddon::Get().GetSettings().GetAnimatedGuildHoverOnly(); const bool hover_only = Abaddon::Get().GetSettings().AnimatedGuildHoverOnly;
const bool is_hovered = flags & Gtk::CELL_RENDERER_PRELIT; const bool is_hovered = flags & Gtk::CELL_RENDERER_PRELIT;
auto anim = m_property_pixbuf_animation.get_value(); auto anim = m_property_pixbuf_animation.get_value();
@ -1069,7 +1065,7 @@ void CellRendererChannels::render_vfunc_category(const Cairo::RefPtr<Cairo::Cont
cr->move_to(x1, y1); cr->move_to(x1, y1);
cr->line_to(x2, y2); cr->line_to(x2, y2);
cr->line_to(x3, y3); cr->line_to(x3, y3);
static const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetChannelsExpanderColor()); const auto expander_color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelsExpanderColor);
cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue()); cr->set_source_rgb(expander_color.get_red(), expander_color.get_green(), expander_color.get_blue());
cr->stroke(); cr->stroke();
@ -1115,7 +1111,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h); Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
const static auto nsfw_color = Gdk::RGBA(Abaddon::Get().GetSettings().GetNSFWChannelColor()); const auto nsfw_color = Gdk::RGBA(Abaddon::Get().GetSettings().NSFWChannelColor);
if (m_property_nsfw.get_value()) if (m_property_nsfw.get_value())
m_renderer_text.property_foreground_rgba() = nsfw_color; m_renderer_text.property_foreground_rgba() = nsfw_color;
m_renderer_text.render(cr, widget, background_area, text_cell_area, flags); m_renderer_text.render(cr, widget, background_area, text_cell_area, flags);

View File

@ -1,7 +1,7 @@
#include <filesystem> #include <filesystem>
#include "chatinputindicator.hpp" #include "chatinputindicator.hpp"
#include "../abaddon.hpp" #include "abaddon.hpp"
#include "../util.hpp" #include "util.hpp"
constexpr static const int MaxUsersInIndicator = 4; constexpr static const int MaxUsersInIndicator = 4;

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <gtkmm.h> #include <gtkmm.h>
#include <unordered_map> #include <unordered_map>
#include "../discord/message.hpp" #include "discord/message.hpp"
#include "../discord/user.hpp" #include "discord/user.hpp"
class ChatInputIndicator : public Gtk::Box { class ChatInputIndicator : public Gtk::Box {
public: public:

View File

@ -1,6 +1,6 @@
#include "chatmessage.hpp" #include "chatmessage.hpp"
#include "../abaddon.hpp" #include "abaddon.hpp"
#include "../util.hpp" #include "util.hpp"
#include "lazyimage.hpp" #include "lazyimage.hpp"
#include <unordered_map> #include <unordered_map>
@ -231,11 +231,13 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
} }
} break; } break;
case MessageType::RECIPIENT_ADD: { case MessageType::RECIPIENT_ADD: {
if (data->Mentions.size() == 0) break;
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID); const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
const auto &added = data->Mentions[0]; 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->Username + "</span> added <span color='#eeeeee'>" + added.Username + "</span></span></i>");
} break; } break;
case MessageType::RECIPIENT_REMOVE: { case MessageType::RECIPIENT_REMOVE: {
if (data->Mentions.size() == 0) break;
const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID); const auto &adder = Abaddon::Get().GetDiscordClient().GetUser(data->Author.ID);
const auto &added = data->Mentions[0]; const auto &added = data->Mentions[0];
if (adder->ID == added.ID) if (adder->ID == added.ID)
@ -355,7 +357,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const EmbedData &emb
} }
return false; return false;
}); });
static auto color = Abaddon::Get().GetSettings().GetLinkColor(); static auto color = Abaddon::Get().GetSettings().LinkColor;
title_label->override_color(Gdk::RGBA(color)); title_label->override_color(Gdk::RGBA(color));
title_label->set_markup("<b>" + Glib::Markup::escape_text(*embed.Title) + "</b>"); title_label->set_markup("<b>" + Glib::Markup::escape_text(*embed.Title) + "</b>");
} }
@ -653,6 +655,14 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
return author->GetEscapedBoldString<false>(); return author->GetEscapedBoldString<false>();
}; };
// 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);
if (refd.has_value())
referenced_message = std::make_shared<Message>(std::move(*refd));
}
if (data.Interaction.has_value()) { if (data.Interaction.has_value()) {
const auto user = *discord.GetUser(data.Interaction->User.ID); const auto user = *discord.GetUser(data.Interaction->User.ID);
@ -664,16 +674,16 @@ Gtk::Widget *ChatMessageItemContainer::CreateReplyComponent(const Message &data)
} else { } else {
lbl->set_markup(user.GetEscapedBoldString<false>()); lbl->set_markup(user.GetEscapedBoldString<false>());
} }
} else if (data.ReferencedMessage.has_value()) { } else if (referenced_message.has_value()) {
if (data.ReferencedMessage.value().get() == nullptr) { if (referenced_message.value() == nullptr) {
lbl->set_markup("<i>deleted message</i>"); lbl->set_markup("<i>deleted message</i>");
} else { } else {
const auto &referenced = *data.ReferencedMessage.value().get(); const auto &referenced = *referenced_message.value();
Glib::ustring text; Glib::ustring text;
if (referenced.Content == "") { if (referenced.Content.empty()) {
if (referenced.Attachments.size() > 0) { if (!referenced.Attachments.empty()) {
text = "<i>attachment</i>"; text = "<i>attachment</i>";
} else if (referenced.Embeds.size() > 0) { } else if (!referenced.Embeds.empty()) {
text = "<i>embed</i>"; text = "<i>embed</i>";
} }
} else { } else {
@ -788,7 +798,6 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
int mstart, mend; int mstart, mend;
if (!match.fetch_pos(0, mstart, mend)) break; if (!match.fetch_pos(0, mstart, mend)) break;
const bool is_animated = match.fetch(0)[1] == 'a'; const bool is_animated = match.fetch(0)[1] == 'a';
const bool show_animations = Abaddon::Get().GetSettings().GetShowAnimations();
const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); 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 chars_end = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mend);
@ -796,7 +805,7 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
auto end_it = buf->get_iter_at_offset(chars_end); auto end_it = buf->get_iter_at_offset(chars_end);
startpos = mend; startpos = mend;
if (is_animated && show_animations) { if (is_animated && Abaddon::Get().GetSettings().ShowAnimations) {
const auto mark_start = buf->create_mark(start_it, false); const auto mark_start = buf->create_mark(start_it, false);
end_it.backward_char(); end_it.backward_char();
const auto mark_end = buf->create_mark(end_it, false); const auto mark_end = buf->create_mark(end_it, false);
@ -825,7 +834,9 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
buf->delete_mark(mark_start); buf->delete_mark(mark_start);
buf->delete_mark(mark_end); buf->delete_mark(mark_end);
auto it = buf->erase(start_it, end_it); auto it = buf->erase(start_it, end_it);
buf->insert_pixbuf(it, pixbuf->scale_simple(EmojiSize, EmojiSize, Gdk::INTERP_BILINEAR)); int width, height;
GetImageDimensions(pixbuf->get_width(), pixbuf->get_height(), width, height, EmojiSize, EmojiSize);
buf->insert_pixbuf(it, pixbuf->scale_simple(width, height, Gdk::INTERP_BILINEAR));
}; };
img.LoadFromURL(EmojiData::URLFromID(match.fetch(2)), sigc::track_obj(cb, tv)); img.LoadFromURL(EmojiData::URLFromID(match.fetch(2)), sigc::track_obj(cb, tv));
} }
@ -835,11 +846,8 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
} }
void ChatMessageItemContainer::HandleEmojis(Gtk::TextView &tv) { void ChatMessageItemContainer::HandleEmojis(Gtk::TextView &tv) {
static const bool stock_emojis = Abaddon::Get().GetSettings().GetShowStockEmojis(); if (Abaddon::Get().GetSettings().ShowStockEmojis) HandleStockEmojis(tv);
static const bool custom_emojis = Abaddon::Get().GetSettings().GetShowCustomEmojis(); if (Abaddon::Get().GetSettings().ShowCustomEmojis) HandleCustomEmojis(tv);
if (stock_emojis) HandleStockEmojis(tv);
if (custom_emojis) HandleCustomEmojis(tv);
} }
void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) { void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) {
@ -959,9 +967,6 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) {
auto buf = tv.get_buffer(); auto buf = tv.get_buffer();
Glib::ustring text = GetText(buf); Glib::ustring text = GetText(buf);
// i'd like to let this be done thru css like .message-link { color: #bitch; } but idk how
static auto link_color = Abaddon::Get().GetSettings().GetLinkColor();
int startpos = 0; int startpos = 0;
Glib::MatchInfo match; Glib::MatchInfo match;
while (rgx->match(text, startpos, match)) { while (rgx->match(text, startpos, match)) {
@ -970,7 +975,7 @@ void ChatMessageItemContainer::HandleLinks(Gtk::TextView &tv) {
std::string link = match.fetch(0); std::string link = match.fetch(0);
auto tag = buf->create_tag(); auto tag = buf->create_tag();
m_link_tagmap[tag] = link; m_link_tagmap[tag] = link;
tag->property_foreground_rgba() = Gdk::RGBA(link_color); tag->property_foreground_rgba() = Gdk::RGBA(Abaddon::Get().GetSettings().LinkColor);
tag->set_property("underline", 1); // stupid workaround for vcpkg bug (i think) tag->set_property("underline", 1); // stupid workaround for vcpkg bug (i think)
const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart); const auto chars_start = g_utf8_pointer_to_offset(text.c_str(), text.c_str() + mstart);
@ -1128,7 +1133,7 @@ ChatMessageHeader::ChatMessageHeader(const Message &data)
m_content_box_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); 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_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); m_avatar_ev.add_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
if (Abaddon::Get().GetSettings().GetShowAnimations()) { if (Abaddon::Get().GetSettings().ShowAnimations) {
m_content_box_ev.signal_enter_notify_event().connect(on_enter_cb); 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_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_enter_notify_event().connect(on_enter_cb);

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <gtkmm.h> #include <gtkmm.h>
#include "../discord/discord.hpp" #include "discord/discord.hpp"
class ChatMessageItemContainer : public Gtk::Box { class ChatMessageItemContainer : public Gtk::Box {
public: public:

View File

@ -1,6 +1,6 @@
#include "chatwindow.hpp" #include "chatwindow.hpp"
#include "chatmessage.hpp" #include "chatmessage.hpp"
#include "../abaddon.hpp" #include "abaddon.hpp"
#include "chatinputindicator.hpp" #include "chatinputindicator.hpp"
#include "ratelimitindicator.hpp" #include "ratelimitindicator.hpp"
#include "chatinput.hpp" #include "chatinput.hpp"

View File

@ -2,7 +2,7 @@
#include <gtkmm.h> #include <gtkmm.h>
#include <string> #include <string>
#include <set> #include <set>
#include "../discord/discord.hpp" #include "discord/discord.hpp"
#include "completer.hpp" #include "completer.hpp"
class ChatMessageHeader; class ChatMessageHeader;

View File

@ -1,7 +1,7 @@
#include <unordered_set> #include <unordered_set>
#include "completer.hpp" #include "completer.hpp"
#include "../abaddon.hpp" #include "abaddon.hpp"
#include "../util.hpp" #include "util.hpp"
constexpr const int CompleterHeight = 150; constexpr const int CompleterHeight = 150;
constexpr const int MaxCompleterEntries = 30; constexpr const int MaxCompleterEntries = 30;

View File

@ -2,7 +2,7 @@
#include <gtkmm.h> #include <gtkmm.h>
#include <functional> #include <functional>
#include "lazyimage.hpp" #include "lazyimage.hpp"
#include "../discord/snowflake.hpp" #include "discord/snowflake.hpp"
constexpr static int CompleterImageSize = 24; constexpr static int CompleterImageSize = 24;

Some files were not shown because too many files have changed in this diff Show More