50 Commits

Author SHA1 Message Date
ouwou
c22a49f64e no more vcpkg 2022-04-08 13:49:51 -04:00
ouwou
436024b4a0 remove mark all as read (it doesnt work on API end) 2022-04-07 20:26:15 -04:00
ouwou
61cde0f7e1 fix more menu stuff
also reformat
2022-04-07 20:18:10 -04:00
ouwou
a9399873fd fix open dm opening the wrong thing sometimes 2022-04-07 03:02:25 -04:00
ouwou
c2be1d3668 update readme
also format according to whatever jetbrains thinks is good i guess
2022-04-07 02:17:59 -04:00
ouwou
1d981d2c5a fix menus looking weird when opened for first time 2022-04-07 00:21:24 -04:00
ouwou
c2b7ca780e no more msvc builds 2022-04-06 21:15:50 -04:00
ouwou
57e95c8969 replace file chooser with native
also remove clipboard since it was just a workaround and i dont want to maintain it
2022-04-06 21:07:45 -04:00
ouwou
56a74fb5dd improve scrolling behavior again, refactor
scrolling is almost exactly how i want it, but when an existing message's height allocation is changed it still causes the scroll position to change, but its not that bad and is better than what i had before anyways so it is good enough for now. ideally if you are scrolled in the middle it will stay put completely
2022-04-06 19:58:41 -04:00
ouwou
49685c3989 fix up a bunch of clang-tidy stuff
mostly changing references, which i hope doesnt break stuff with models (TreeRow, iterators) since they gave me some strange problems in the past
2022-04-05 22:01:53 -04:00
ouwou
9767e1e7fd Merge branch 'master' into msys 2022-03-31 03:21:26 -04:00
ouwou
a83e9c01a6 improve scrolling up behavior when loading new messages 2022-03-31 03:21:07 -04:00
ouwou
7b1ceeedf4 enable fontconfig stuff on msys2 too 2022-03-31 03:06:36 -04:00
ouwou
a0b3c9f8a4 Merge branch 'master' into msys 2022-03-26 02:58:59 -04:00
ouwou
a2a45757e9 handle nicknames for message headers 2022-03-26 02:51:56 -04:00
ouwou
271d21c7bd fix UB 2022-03-05 01:01:19 -05:00
ouwou
46b88566f1 obfuscate token in input dialog 2022-03-04 23:04:07 -05:00
ouwou
af60bceada optimize sql for getting unknown member ids 2022-03-04 23:03:09 -05:00
ouwou
3583a5d251 dont request guild members if there are no user ids 2022-03-03 23:57:48 -05:00
ouwou
4503aeabc4 request when loading message history 2022-03-03 23:50:14 -05:00
ouwou
7f1d3df4a5 start sending request guild members for unknown members 2022-03-03 23:45:30 -05:00
ouwou
17f1289c84 fill out gateway op enum using internal names 2022-03-03 03:01:09 -05:00
ouwou
fc3d0fddd2 align stickers 2022-02-27 00:52:52 -05:00
ouwou
481685b3bb format all 2022-01-09 23:20:08 -05:00
ouwou
f31d431517 Merge branch 'unread' into msys 2022-01-08 20:11:52 -05:00
ouwou
f19dcc0114 Merge branch 'master' into msys 2021-12-13 00:34:19 -05:00
ouwou
38a49d172c warn if pixbufloaders arent found 2021-12-13 00:31:50 -05:00
ouwou
72935b0558 dont crash immediately if gif pixbufloader doesnt exist 2021-12-12 23:59:57 -05:00
ouwou
c9647f9b33 update paths 2021-11-28 23:52:45 -05:00
ouwou
e1703aea3f merge master 2021-11-28 22:48:30 -05:00
ouwou
fd53a76bf6 copy compiled schemas 2021-11-18 23:41:47 -05:00
ouwou
b5c1394662 copy fonts 2021-11-18 21:32:27 -05:00
ouwou
66d7cb581c update cert path 2021-11-18 21:15:24 -05:00
ouwou
8f823420b6 more typos 2021-11-18 20:58:10 -05:00
ouwou
36d5e011e8 more fixings (hopefully) 2021-11-18 20:44:18 -05:00
ouwou
5a4bcbf377 update deps 2021-11-18 19:20:07 -05:00
ouwou
0fe007569e update deps 2021-11-18 18:59:24 -05:00
ouwou
c49e454ec0 double typo 2021-11-18 02:54:19 -05:00
ouwou
8afc8c62ef typo 2021-11-18 02:37:08 -05:00
ouwou
4644adff94 fix stuff 2021-11-18 02:22:53 -05:00
ouwou
5e08083b5a copy msys2 dependencies to artifact 2021-11-18 02:02:50 -05:00
ouwou
95a8da803a fix artifact path 2021-11-18 01:27:29 -05:00
ouwou
43a41b34bc update ixwebsocket submodule from master 2021-11-18 01:08:19 -05:00
ouwou
9c285a09e5 Merge branch 'master' of https://github.com/uowuo/abaddon into msys 2021-11-18 01:03:35 -05:00
ouwou
1f68da6b77 update IXWebSocket submodule 2021-11-09 02:00:11 -05:00
ouwou
98e0e84d57 low iq moment 2021-11-09 01:23:43 -05:00
ouwou
6ddba4363a add uses 2021-11-09 01:14:38 -05:00
ouwou
b5b5c40ecd first try at actions 2021-11-09 01:13:06 -05:00
ouwou
d84fe2b800 use fontconfig setup on msys too 2021-11-09 01:01:33 -05:00
ouwou
da561ba4d5 initial msys compatibility 2021-11-09 00:55:18 -05:00
92 changed files with 1231 additions and 1189 deletions

View File

@@ -3,57 +3,60 @@ name: Abaddon CI
on: [push, pull_request]
jobs:
windows:
name: windows-${{ matrix.buildtype }}
runs-on: windows-2019
msys2:
name: msys2-mingw64
runs-on: windows-latest
strategy:
matrix:
buildtype: [Debug, RelWithDebInfo, MinSizeRel]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: Fetch CMake
uses: lukka/get-cmake@v3.21.2
- name: Fetch dependencies
uses: lukka/run-vcpkg@v7
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
vcpkgArguments: gtkmm nlohmann-json zlib sqlite3 glibmm openssl ixwebsocket curl
vcpkgDirectory: ${{ github.workspace }}/ci/vcpkg/
vcpkgTriplet: x64-windows
msystem: mingw64
update: true
install: >-
git
make
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-cmake
mingw-w64-x86_64-ninja
mingw-w64-x86_64-sqlite3
mingw-w64-x86_64-nlohmann-json
mingw-w64-x86_64-curl
mingw-w64-x86_64-zlib
mingw-w64-x86_64-gtkmm3
- name: Build
uses: lukka/run-cmake@v3
with:
useVcpkgToolchainFile: true
vcpkgTriplet: x64-windows
buildDirectory: ${{ runner.workspace }}/build
cmakeBuildType: ${{ matrix.buildtype }}
- name: Setup artifact files
shell: cmd
run: |
del /f /s /q "${{ runner.workspace }}\build\CMakeFiles"
rmdir /s /q "${{ runner.workspace }}\build\CMakeFiles"
del /f /s /q "${{ runner.workspace }}\build\.ninja_deps"
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\CMakeCache.txt"
xcopy /E /I "${{ github.workspace }}\res\css" "${{ runner.workspace }}\build\css"
xcopy /E /I "${{ github.workspace }}\res\res" "${{ runner.workspace }}\build\res"
xcopy /E /I "${{ github.workspace }}\res\fonts" "${{ runner.workspace }}\build\fonts"
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"
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-console.exe" "${{ runner.workspace }}\build\gspawn-win64-helper-console.exe"
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }}
cmake --build build
- name: Setup Artifact
run: |
mkdir -p build/artifactdir/bin build/artifactdir/ssl/certs build/artifactdir/lib build/artifactdir/share/glib-2.0/schemas
cd build
cp *.exe artifactdir/bin
cd ..
cp /mingw64/ssl/certs/ca-bundle.crt build/artifactdir/ssl/certs
cp -r /mingw64/lib/gdk-pixbuf-2.0 build/artifactdir/lib
cp -r res/css res/res res/fonts build/artifactdir/bin
cp /mingw64/share/glib-2.0/schemas/gschemas.compiled build/artifactdir/share/glib-2.0/schemas
cat "ci/msys-deps.txt" | sed 's/\r$//' | xargs -I % cp /mingw64% build/artifactdir/bin
- name: Upload build
uses: actions/upload-artifact@v2
with:
name: build-windows-${{ matrix.buildtype }}
path: ${{ runner.workspace }}/build
name: build-windows-msys2-${{ matrix.buildtype }}
path: build/artifactdir
mac:
name: macos-${{ matrix.buildtype }}
runs-on: macos-latest

6
.gitmodules vendored
View File

@@ -1,9 +1,3 @@
[submodule "vcpkg"]
path = ci/vcpkg
url = https://github.com/microsoft/vcpkg/
[submodule "ci/vcpkg"]
path = ci/vcpkg
url = https://github.com/microsoft/vcpkg
[submodule "ci/gtk-for-windows"]
path = ci/gtk-for-windows
url = https://github.com/tschoonj/GTK-for-Windows-Runtime-Environment-Installer

129
README.md
View File

@@ -7,6 +7,7 @@ Alternative Discord client made in C++ with GTK
<a href="https://discord.gg/wkCU3vuzG5"><img src="https://discord.com/api/guilds/858156817711890443/widget.png?style=shield"></a>
Current features:
* Not Electron
* Handles most types of chat messages including embeds, images, and replies
* Completely styleable/customizable with CSS (if you have a system GTK theme it won't really use it though)
@@ -23,24 +24,41 @@ Current features:
* Emojis<sup>2</sup>
* Thread support<sup>3</sup>
* Animated avatars, server icons, emojis (can be turned off)
1 - Abaddon tries its best to make Discord think it's a legitimate web client. Some of the things done to do this include: using a browser user agent, sending the same IDENTIFY message that the official web client does, using API v9 endpoints in all cases, and not using endpoints the web client does not normally use. There are still a few smaller inconsistencies, however. For example the web client sends lots of telemetry via the `/science` endpoint (uBlock origin stops this) as well as in the headers of all requests. **In any case,** you should use an official client for joining servers, sending new DMs, or managing your friends list if you are afraid of being caught in Discord's spam filters (unlikely).
2 - Unicode emojis are substituted manually as opposed to rendered by GTK on non-Windows platforms. This can be changed with the `stock_emojis` setting as shown at the bottom of this README. A CBDT-based font using Twemoji is provided to allow GTK to render emojis natively on Windows.
1 - Abaddon tries its best to make Discord think it's a legitimate web client. Some of the things done to do this
include: using a browser user agent, sending the same IDENTIFY message that the official web client does, using API v9
endpoints in all cases, and not using endpoints the web client does not normally use. There are still a few smaller
inconsistencies, however. For example the web client sends lots of telemetry via the `/science` endpoint (uBlock origin
stops this) as well as in the headers of all requests. **In any case,** you should use an official client for joining
servers, sending new DMs, or managing your friends list if you are afraid of being caught in Discord's spam filters
(unlikely).
2 - Unicode emojis are substituted manually as opposed to rendered by GTK on non-Windows platforms. This can be changed
with the `stock_emojis` setting as shown at the bottom of this README. A CBDT-based font using Twemoji is provided to
allow GTK to render emojis natively on Windows.
3 - There are some inconsistencies with thread state that might be encountered in some more uncommon cases, but they are
the result of fundamental issues with Discord's thread implementation.
3 - There are some inconsistencies with thread state that might be encountered in some more uncommon cases, but they are the result of fundamental issues with Discord's thread implementation.
### Building manually (recommended if not on Windows):
#### Windows:
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 sqlite3:x64-windows openssl:x64-windows curl:x64-windows`
#### Windows (with MSYS2):
1. Install following packages:
* mingw-w64-x86_64-cmake
* mingw-w64-x86_64-ninja
* mingw-w64-x86_64-sqlite3
* mingw-w64-x86_64-nlohmann-json
* mingw-w64-x86_64-curl
* mingw-w64-x86_64-zlib
* mingw-w64-x86_64-gtkmm3
2. `git clone --recurse-submodules="subprojects" https://github.com/uowuo/abaddon && cd abaddon`
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 ..`
5. Build with Visual Studio
Or, do steps 1 and 2, and open CMakeLists.txt in Visual Studio if `vcpkg integrate install` was run
4. `cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo ..`
5. `ninja`
#### Mac:
1. `git clone https://github.com/uowuo/abaddon && cd abaddon`
2. `brew install gtkmm3 nlohmann-json`
3. `git submodule update --init subprojects`
@@ -49,7 +67,9 @@ Or, do steps 1 and 2, and open CMakeLists.txt in Visual Studio if `vcpkg integra
6. `make`
#### Linux:
1. Install dependencies: `libgtkmm-3.0-dev`, `libcurl4-gnutls-dev`, and [nlohmann-json](https://github.com/nlohmann/json)
1. Install dependencies: `libgtkmm-3.0-dev`, `libcurl4-gnutls-dev`,
and [nlohmann-json](https://github.com/nlohmann/json)
2. `git clone https://github.com/uowuo/abaddon && cd abaddon`
3. `mkdir build && cd build`
4. `cmake ..`
@@ -62,16 +82,20 @@ Latest release version: https://github.com/uowuo/abaddon/releases/latest
**CI:**
- Windows: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-windows-RelWithDebInfo.zip)
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned, unpackaged, requires gtkmm3 (e.g. from homebrew)
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now), requires gtkmm3. built on Ubuntu 18.04 + gcc9
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned,
unpackaged, requires gtkmm3 (e.g. from homebrew)
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now),
requires gtkmm3. built on Ubuntu 18.04 + gcc9
⚠️ If you use Windows, make sure to start from the directory containing `css` and `res`
⚠️ If you use Windows, make sure to start from the `bin` directory
On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/usr/share/abaddon`
`abaddon.ini` will also be automatically used if located at `~/.config/abaddon/abaddon.ini` and there is no `abaddon.ini` in the working directory
`abaddon.ini` will also be automatically used if located at `~/.config/abaddon/abaddon.ini` and there is
no `abaddon.ini` in the working directory
#### Dependencies:
#### Dependencies:
* [gtkmm](https://www.gtkmm.org/en/)
* [JSON for Modern C++](https://github.com/nlohmann/json)
* [IXWebSocket](https://github.com/machinezone/IXWebSocket)
@@ -80,20 +104,21 @@ On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/
* [SQLite3](https://www.sqlite.org/index.html)
### TODO:
* Voice support
* User activities
* Nicknames
* More server management stuff
* Manage friends
* A bunch of other stuff
### Styling
#### CSS selectors
.app-window - Applied to all windows. This means the main window and all popups
.app-popup - Additional class for `.app-window`s when the window is not the main window
.channel-list - Container of the channel list
.app-popup - Additional class for `.app-window`s when the window is not the main window
.channel-list - Container of the channel list
.messages - Container of user messages
.message-container - The container which holds a user's messages
.message-container-author - The author label for a message container
@@ -109,47 +134,45 @@ On Linux, `css` and `res` can also be loaded from `~/.local/share/abaddon` or `/
.replying - Extra class for chat input container when a reply is currently being created
.reaction-box - Contains a reaction image and the count
.reacted - Additional class for reaction-box when the user has reacted with a particular reaction
.reaction-count - Contains the count for reaction
.reaction-count - Contains the count for reaction
.completer - Container for the message completer
.completer-entry - Container for a single entry in the completer
.completer-entry-label - Contains the label for an entry in the completer
.completer-entry-image - Contains the image for an entry in the completer
.completer-entry-image - Contains the image for an entry in the completer
.embed - Container for a message embed
.embed-author - The author of an embed
.embed-title - The title of an embed
.embed-description - The description of an embed
.embed-field-title - The title of an embed field
.embed-field-value - The value of an embed field
.embed-footer - The footer of an embed
.embed-footer - The footer of an embed
.members - Container of the member list
.members-row - All rows within the members container
.members-row-label - All labels in the members container
.members-row-member - Rows containing a member
.members-row-role - Rows containing a role
.members-row-avatar - Contains the avatar for a row in the member list
.members-row-avatar - Contains the avatar for a row in the member list
.status-indicator - The status indicator
.online - Applied to status indicators when the associated user is online
.idle - Applied to status indicators when the associated user is away
.dnd - Applied to status indicators when the associated user is on do not disturb
.offline - Applied to status indicators when the associated user is offline
.typing-indicator - The typing indicator (also used for replies)
.offline - Applied to status indicators when the associated user is offline
.typing-indicator - The typing indicator (also used for replies)
Used in reorderable list implementation:
.drag-icon
.drag-hover-top
.drag-hover-bottom
.drag-icon .drag-hover-top .drag-hover-bottom
Used in guild settings popup:
.guild-settings-window
.guild-members-pane-list - Container for list of members in the members pane
.guild-members-pane-info - Container for member info
.guild-roles-pane-list - Container for list of roles in the roles pane
.guild-roles-pane-list - Container for list of roles in the roles pane
Used in profile popup:
.mutual-friend-item - Applied to every item in the mutual friends list
.mutual-friend-item-name - Name in mutual friend item
@@ -174,11 +197,13 @@ Used in profile popup:
.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
.profile-badge
### Settings
Settings are configured (for now) by editing abaddon.ini
The format is similar to the standard Windows ini format **except**:
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
@@ -187,27 +212,35 @@ This listing is organized by section.
For example, memory_db would be set by adding `memory_db = true` under the line `[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
* 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
* user_agent (string) - sets the user-agent to use in HTTP requests to the Discord API (not including media/images)
* concurrent (int, default 20) - how many images can be concurrently retrieved
#### gui
* member_list_discriminator (true or false, default true) - show user discriminators in the member list
* stock_emojis (true or false, default true) - allow abaddon to substitute unicode emojis with images from emojis.bin, must be false to allow GTK to render emojis itself
* stock_emojis (true or false, default true) - allow abaddon to substitute unicode emojis with images from emojis.bin,
must be false to allow GTK to render emojis itself
* custom_emojis (true or false, default true) - download and use custom Discord emojis
* css (string) - path to the main CSS file
* 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
* 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
* owner_crown (true or false, default true) - show a crown next to the owner
* unreads (true or false, default true) - show unread indicators and mention badges
#### style
* linkcolor (string) - color to use for links in messages
* expandercolor (string) - color to use for the expander in the channel list
* nsfwchannelcolor (string) - color to use for NSFW channels in the channel list

57
ci/msys-deps.txt Normal file
View File

@@ -0,0 +1,57 @@
/bin/gdbus.exe
/bin/gspawn-win64-helper-console.exe
/bin/libatk-1.0-0.dll
/bin/libatkmm-1.6-1.dll
/bin/libbrotlicommon.dll
/bin/libbrotlidec.dll
/bin/libbz2-1.dll
/bin/libcairo-2.dll
/bin/libcairo-gobject-2.dll
/bin/libcairomm-1.0-1.dll
/bin/libcrypto-1_1-x64.dll
/bin/libcurl-4.dll
/bin/libdatrie-1.dll
/bin/libdeflate.dll
/bin/libepoxy-0.dll
/bin/libexpat-1.dll
/bin/libffi-7.dll
/bin/libfontconfig-1.dll
/bin/libfreetype-6.dll
/bin/libfribidi-0.dll
/bin/libgcc_s_seh-1.dll
/bin/libgdk-3-0.dll
/bin/libgdk_pixbuf-2.0-0.dll
/bin/libgdkmm-3.0-1.dll
/bin/libgio-2.0-0.dll
/bin/libgiomm-2.4-1.dll
/bin/libglib-2.0-0.dll
/bin/libglibmm-2.4-1.dll
/bin/libgmodule-2.0-0.dll
/bin/libgobject-2.0-0.dll
/bin/libgraphite2.dll
/bin/libgtk-3-0.dll
/bin/libgtkmm-3.0-1.dll
/bin/libharfbuzz-0.dll
/bin/libiconv-2.dll
/bin/libidn2-0.dll
/bin/libintl-8.dll
/bin/libnghttp2-14.dll
/bin/libpango-1.0-0.dll
/bin/libpangocairo-1.0-0.dll
/bin/libpangoft2-1.0-0.dll
/bin/libpangomm-1.4-1.dll
/bin/libpangowin32-1.0-0.dll
/bin/libpcre-1.dll
/bin/libpixman-1-0.dll
/bin/libpng16-16.dll
/bin/libpsl-5.dll
/bin/libsigc-2.0-0.dll
/bin/libsqlite3-0.dll
/bin/libssh2-1.dll
/bin/libssl-1_1-x64.dll
/bin/libstdc++-6.dll
/bin/libthai-0.dll
/bin/libunistring-2.dll
/bin/libwinpthread-1.dll
/bin/libzstd.dll
/bin/zlib1.dll

Submodule ci/vcpkg deleted from a9b27ed5df

View File

@@ -63,19 +63,42 @@ int Abaddon::StartGTK() {
m_gtk_app = Gtk::Application::create("com.github.uowuo.abaddon");
m_css_provider = Gtk::CssProvider::create();
m_css_provider->signal_parsing_error().connect([this](const Glib::RefPtr<const Gtk::CssSection> &section, const Glib::Error &error) {
Gtk::MessageDialog dlg(*m_main_window, "css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
m_css_provider->signal_parsing_error().connect([](const Glib::RefPtr<const Gtk::CssSection> &section, const Glib::Error &error) {
Gtk::MessageDialog dlg("css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
});
m_css_low_provider = Gtk::CssProvider::create();
m_css_low_provider->signal_parsing_error().connect([this](const Glib::RefPtr<const Gtk::CssSection> &section, const Glib::Error &error) {
Gtk::MessageDialog dlg(*m_main_window, "low-priority css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
m_css_low_provider->signal_parsing_error().connect([](const Glib::RefPtr<const Gtk::CssSection> &section, const Glib::Error &error) {
Gtk::MessageDialog dlg("low-priority css failed parsing (" + error.what() + ")", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
});
#ifdef _WIN32
bool png_found = false;
bool gif_found = false;
for (const auto &fmt : Gdk::Pixbuf::get_formats()) {
if (fmt.get_name() == "png")
png_found = true;
else if (fmt.get_name() == "gif")
gif_found = true;
}
if (!png_found) {
Gtk::MessageDialog dlg("The PNG pixbufloader wasn't detected. Abaddon may not work as a result.", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
if (!gif_found) {
Gtk::MessageDialog dlg("The GIF pixbufloader wasn't detected. Animations may not display as a result.", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
dlg.run();
}
#endif
m_main_window = std::make_unique<MainWindow>();
m_main_window->set_title(APP_TITLE);
m_main_window->set_position(Gtk::WIN_POS_CENTER);
@@ -142,7 +165,7 @@ void Abaddon::OnShutdown() {
void Abaddon::LoadFromSettings() {
std::string token = GetSettings().DiscordToken;
if (token.size()) {
if (!token.empty()) {
m_discord_token = token;
m_discord.UpdateToken(m_discord_token);
}
@@ -269,7 +292,7 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_
delete child;
if (guild.has_value() && user.has_value()) {
const auto roles = user->GetSortedRoles();
m_user_menu_roles->set_visible(roles.size() > 0);
m_user_menu_roles->set_visible(!roles.empty());
for (const auto &role : roles) {
auto *item = Gtk::manage(new Gtk::MenuItem(role.Name));
if (role.Color != 0) {
@@ -325,6 +348,20 @@ void Abaddon::ShowGuildVerificationGateDialog(Snowflake guild_id) {
}
}
void Abaddon::CheckMessagesForMembers(const ChannelData &chan, const std::vector<Message> &msgs) {
if (!chan.GuildID.has_value()) return;
std::vector<Snowflake> unknown;
std::transform(msgs.begin(), msgs.end(),
std::back_inserter(unknown),
[](const Message &msg) -> Snowflake {
return msg.Author.ID;
});
const auto fetch = m_discord.FilterUnknownMembersFrom(*chan.GuildID, unknown.begin(), unknown.end());
m_discord.RequestMembers(*chan.GuildID, fetch.begin(), fetch.end());
}
void Abaddon::SetupUserMenu() {
m_user_menu = Gtk::manage(new Gtk::Menu);
m_user_menu_insert_mention = Gtk::manage(new Gtk::MenuItem("Insert Mention"));
@@ -536,7 +573,8 @@ void Abaddon::ActionChannelOpened(Snowflake id) {
if (m_channels_requested.find(id) == m_channels_requested.end()) {
// dont fire requests we know will fail
if (can_access) {
m_discord.FetchMessagesInChannel(id, [this, id](const std::vector<Message> &msgs) {
m_discord.FetchMessagesInChannel(id, [channel, this, id](const std::vector<Message> &msgs) {
CheckMessagesForMembers(*channel, msgs);
m_main_window->UpdateChatWindowContents();
m_channels_requested.insert(id);
});
@@ -579,7 +617,11 @@ void Abaddon::ActionChatLoadHistory(Snowflake id) {
m_discord.FetchMessagesInChannelBefore(id, before_id, [this, id](const std::vector<Message> &msgs) {
m_channels_history_loading.erase(id);
if (msgs.size() == 0) {
const auto channel = m_discord.GetChannel(id);
if (channel.has_value())
CheckMessagesForMembers(*channel, msgs);
if (msgs.empty()) {
m_channels_history_loaded.insert(id);
} else {
m_main_window->UpdateChatPrependHistory(msgs);
@@ -649,7 +691,7 @@ void Abaddon::ActionSetStatus() {
const auto status = dlg.GetStatusType();
const auto activity_type = dlg.GetActivityType();
const auto activity_name = dlg.GetActivityName();
if (activity_name == "") {
if (activity_name.empty()) {
m_discord.UpdateStatus(status, false);
} else {
ActivityData activity;
@@ -735,6 +777,17 @@ EmojiResource &Abaddon::GetEmojis() {
int main(int argc, char **argv) {
if (std::getenv("ABADDON_NO_FC") == nullptr)
Platform::SetupFonts();
char *systemLocale = std::setlocale(LC_ALL, "");
try {
std::locale::global(std::locale(systemLocale));
} catch (...) {
try {
std::locale::global(std::locale::classic());
std::setlocale(LC_ALL, systemLocale);
} catch (...) {}
}
#if defined(_WIN32) && defined(_MSC_VER)
TCHAR buf[2] { 0 };
GetEnvironmentVariableA("GTK_CSD", buf, sizeof(buf));

View File

@@ -14,14 +14,15 @@
class Abaddon {
private:
Abaddon();
public:
static Abaddon &Get();
Abaddon(const Abaddon &) = delete;
Abaddon &operator=(const Abaddon &) = delete;
Abaddon(Abaddon &&) = delete;
Abaddon &operator=(Abaddon &&) = delete;
public:
static Abaddon &Get();
int StartGTK();
void OnShutdown();
@@ -93,6 +94,8 @@ public:
protected:
void ShowGuildVerificationGateDialog(Snowflake guild_id);
void CheckMessagesForMembers(const ChannelData &chan, const std::vector<Message> &msgs);
void SetupUserMenu();
void SaveState();
void LoadState();

View File

@@ -10,8 +10,6 @@ CellRendererPixbufAnimation::CellRendererPixbufAnimation()
property_ypad() = 2;
}
CellRendererPixbufAnimation::~CellRendererPixbufAnimation() {}
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> CellRendererPixbufAnimation::property_pixbuf() {
return m_property_pixbuf.get_proxy();
}

View File

@@ -6,7 +6,7 @@
class CellRendererPixbufAnimation : public Gtk::CellRenderer {
public:
CellRendererPixbufAnimation();
virtual ~CellRendererPixbufAnimation();
~CellRendererPixbufAnimation() override = default;
Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf>> property_pixbuf();
Glib::PropertyProxy<Glib::RefPtr<Gdk::PixbufAnimation>> property_pixbuf_animation();

View File

@@ -207,12 +207,6 @@ ChannelList::ChannelList()
m_menu_thread.append(m_menu_thread_copy_id);
m_menu_thread.show_all();
m_menu_guild.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnGuildSubmenuPopup));
m_menu_category.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnCategorySubmenuPopup));
m_menu_channel.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnChannelSubmenuPopup));
m_menu_dm.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnDMSubmenuPopup));
m_menu_thread.signal_popped_up().connect(sigc::mem_fun(*this, &ChannelList::OnThreadSubmenuPopup));
auto &discord = Abaddon::Get().GetDiscordClient();
discord.signal_message_create().connect(sigc::mem_fun(*this, &ChannelList::OnMessageCreate));
discord.signal_guild_create().connect(sigc::mem_fun(*this, &ChannelList::UpdateNewGuild));
@@ -291,7 +285,7 @@ void ChannelList::UpdateChannel(Snowflake id) {
auto channel = Abaddon::Get().GetDiscordClient().GetChannel(id);
if (!iter || !channel.has_value()) return;
if (channel->Type == ChannelType::GUILD_CATEGORY) return UpdateChannelCategory(*channel);
if (!IsTextChannel(channel->Type)) return;
if (!channel->IsText()) return;
// refresh stuff that might have changed
const bool is_orphan_TMP = !channel->ParentID.has_value();
@@ -399,7 +393,7 @@ void ChannelList::OnThreadListSync(const ThreadListSyncData &data) {
queue.pop();
if ((*item)[m_columns.m_type] == RenderType::Thread)
threads.push_back(static_cast<Snowflake>((*item)[m_columns.m_id]));
for (auto child : item->children())
for (const auto &child : item->children())
queue.push(child);
}
@@ -580,7 +574,7 @@ Gtk::TreeModel::iterator ChannelList::AddGuild(const GuildData &guild) {
if (thread.has_value())
threads[*thread->ParentID].push_back(*thread);
}
const auto add_threads = [&](const ChannelData &channel, Gtk::TreeRow row) {
const auto add_threads = [&](const ChannelData &channel, const Gtk::TreeRow &row) {
row[m_columns.m_expanded] = true;
const auto it = threads.find(channel.ID);
@@ -648,7 +642,7 @@ Gtk::TreeModel::iterator ChannelList::CreateThreadRow(const Gtk::TreeNodeChildre
thread_row[m_columns.m_type] = RenderType::Thread;
thread_row[m_columns.m_id] = channel.ID;
thread_row[m_columns.m_name] = "- " + Glib::Markup::escape_text(*channel.Name);
thread_row[m_columns.m_sort] = channel.ID;
thread_row[m_columns.m_sort] = static_cast<int64_t>(channel.ID);
thread_row[m_columns.m_nsfw] = false;
return thread_iter;
@@ -692,7 +686,7 @@ bool ChannelList::IsTextChannel(ChannelType type) {
}
// this should be unncessary but something is behaving strange so its just in case
void ChannelList::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) {
void ChannelList::OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const {
(*iter)[m_columns.m_expanded] = false;
}
@@ -737,14 +731,14 @@ void ChannelList::AddPrivateChannels() {
std::optional<UserData> top_recipient;
const auto recipients = dm->GetDMRecipients();
if (recipients.size() > 0)
if (!recipients.empty())
top_recipient = recipients[0];
auto iter = m_model->append(header_row->children());
auto row = *iter;
row[m_columns.m_type] = RenderType::DM;
row[m_columns.m_id] = dm_id;
row[m_columns.m_sort] = -(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id);
row[m_columns.m_sort] = static_cast<int64_t>(-(dm->LastMessageID.has_value() ? *dm->LastMessageID : dm_id));
row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
if (dm->Type == ChannelType::DM && top_recipient.has_value())
@@ -781,7 +775,7 @@ void ChannelList::UpdateCreateDMChannel(const ChannelData &dm) {
auto row = *iter;
row[m_columns.m_type] = RenderType::DM;
row[m_columns.m_id] = dm.ID;
row[m_columns.m_sort] = -(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID);
row[m_columns.m_sort] = static_cast<int64_t>(-(dm.LastMessageID.has_value() ? *dm.LastMessageID : dm.ID));
row[m_columns.m_icon] = img.GetPlaceholder(DMIconSize);
if (dm.Type == ChannelType::DM && top_recipient.has_value())
@@ -817,7 +811,7 @@ void ChannelList::OnMessageCreate(const Message &msg) {
if (!channel.has_value()) return;
if (channel->Type == ChannelType::DM || channel->Type == ChannelType::GROUP_DM) {
if (iter)
(*iter)[m_columns.m_sort] = -msg.ID;
(*iter)[m_columns.m_sort] = static_cast<int64_t>(-msg.ID);
}
if (channel->GuildID.has_value())
if ((iter = GetIteratorForGuildFromID(*channel->GuildID)))
@@ -826,19 +820,23 @@ void ChannelList::OnMessageCreate(const Message &msg) {
bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
if (ev->button == GDK_BUTTON_SECONDARY && ev->type == GDK_BUTTON_PRESS) {
if (m_view.get_path_at_pos(ev->x, ev->y, m_path_for_menu)) {
if (m_view.get_path_at_pos(static_cast<int>(ev->x), static_cast<int>(ev->y), m_path_for_menu)) {
auto row = (*m_model->get_iter(m_path_for_menu));
switch (static_cast<RenderType>(row[m_columns.m_type])) {
case RenderType::Guild:
OnGuildSubmenuPopup();
m_menu_guild.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
break;
case RenderType::Category:
OnCategorySubmenuPopup();
m_menu_category.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
break;
case RenderType::TextChannel:
OnChannelSubmenuPopup();
m_menu_channel.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
break;
case RenderType::DM: {
OnDMSubmenuPopup();
const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(static_cast<Snowflake>(row[m_columns.m_id]));
if (channel.has_value()) {
m_menu_dm_close.set_label(channel->Type == ChannelType::DM ? "Close" : "Leave");
@@ -848,6 +846,7 @@ bool ChannelList::OnButtonPressEvent(GdkEventButton *ev) {
m_menu_dm.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
} break;
case RenderType::Thread: {
OnThreadSubmenuPopup();
m_menu_thread.popup_at_pointer(reinterpret_cast<GdkEvent *>(ev));
break;
} break;
@@ -887,7 +886,7 @@ void ChannelList::MoveRow(const Gtk::TreeModel::iterator &iter, const Gtk::TreeM
m_model->erase(iter);
}
void ChannelList::OnGuildSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void ChannelList::OnGuildSubmenuPopup() {
const auto iter = m_model->get_iter(m_path_for_menu);
if (!iter) return;
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
@@ -897,7 +896,7 @@ void ChannelList::OnGuildSubmenuPopup(const Gdk::Rectangle *flipped_rect, const
m_menu_guild_toggle_mute.set_label("Mute");
}
void ChannelList::OnCategorySubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void ChannelList::OnCategorySubmenuPopup() {
const auto iter = m_model->get_iter(m_path_for_menu);
if (!iter) return;
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
@@ -907,7 +906,7 @@ void ChannelList::OnCategorySubmenuPopup(const Gdk::Rectangle *flipped_rect, con
m_menu_category_toggle_mute.set_label("Mute");
}
void ChannelList::OnChannelSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void ChannelList::OnChannelSubmenuPopup() {
const auto iter = m_model->get_iter(m_path_for_menu);
if (!iter) return;
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
@@ -917,7 +916,7 @@ void ChannelList::OnChannelSubmenuPopup(const Gdk::Rectangle *flipped_rect, cons
m_menu_channel_toggle_mute.set_label("Mute");
}
void ChannelList::OnDMSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void ChannelList::OnDMSubmenuPopup() {
auto iter = m_model->get_iter(m_path_for_menu);
if (!iter) return;
const auto id = static_cast<Snowflake>((*iter)[m_columns.m_id]);
@@ -927,7 +926,7 @@ void ChannelList::OnDMSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk
m_menu_dm_toggle_mute.set_label("Mute");
}
void ChannelList::OnThreadSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void ChannelList::OnThreadSubmenuPopup() {
m_menu_thread_archive.set_visible(false);
m_menu_thread_unarchive.set_visible(false);

View File

@@ -84,7 +84,7 @@ protected:
bool IsTextChannel(ChannelType type);
void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
void OnRowCollapsed(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path) const;
void OnRowExpanded(const Gtk::TreeModel::iterator &iter, const Gtk::TreeModel::Path &path);
bool SelectionFunc(const Glib::RefPtr<Gtk::TreeModel> &model, const Gtk::TreeModel::Path &path, bool is_currently_selected);
bool OnButtonPressEvent(GdkEventButton *ev);
@@ -134,11 +134,11 @@ protected:
Gtk::MenuItem m_menu_thread_mark_as_read;
Gtk::MenuItem m_menu_thread_toggle_mute;
void OnGuildSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnCategorySubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnChannelSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnDMSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnThreadSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnGuildSubmenuPopup();
void OnCategorySubmenuPopup();
void OnChannelSubmenuPopup();
void OnDMSubmenuPopup();
void OnThreadSubmenuPopup();
bool m_updating_listing = false;

View File

@@ -27,9 +27,6 @@ CellRendererChannels::CellRendererChannels()
});
}
CellRendererChannels::~CellRendererChannels() {
}
Glib::PropertyProxy<RenderType> CellRendererChannels::property_type() {
return m_property_type.get_proxy();
}
@@ -212,7 +209,10 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
const double text_w = text_natural.width;
const double text_h = text_natural.height;
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
Gdk::Rectangle text_cell_area(static_cast<int>(text_x),
static_cast<int>(text_y),
static_cast<int>(text_w),
static_cast<int>(text_h));
static const auto color = Gdk::RGBA(Abaddon::Get().GetSettings().ChannelColor);
m_renderer_text.property_foreground_rgba() = color;
@@ -231,7 +231,11 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
const auto cb = [this, &widget, anim, icon_x, icon_y, icon_w, icon_h] {
if (m_pixbuf_anim_iters.at(anim)->advance())
widget.queue_draw_area(icon_x, icon_y, icon_w, icon_h);
widget.queue_draw_area(
static_cast<int>(icon_x),
static_cast<int>(icon_y),
static_cast<int>(icon_w),
static_cast<int>(icon_h));
};
if ((hover_only && is_hovered) || !hover_only)
@@ -264,12 +268,12 @@ void CellRendererChannels::render_vfunc_guild(const Cairo::RefPtr<Cairo::Context
const auto y = background_area.get_y();
const auto w = background_area.get_width();
const auto h = background_area.get_height();
cr->rectangle(x, y + h / 2 - 24 / 2, 3, 24);
cr->rectangle(x, y + h / 2.0 - 24.0 / 2.0, 3.0, 24.0);
cr->fill();
}
if (total_mentions < 1) return;
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
auto *paned = dynamic_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
if (paned != nullptr) {
const auto edge = std::min(paned->get_position(), background_area.get_width());
@@ -415,7 +419,7 @@ void CellRendererChannels::render_vfunc_channel(const Cairo::RefPtr<Cairo::Conte
}
if (unread_state < 1) return;
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
auto *paned = dynamic_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
if (paned != nullptr) {
const auto edge = std::min(paned->get_position(), cell_area.get_width());
@@ -487,7 +491,7 @@ void CellRendererChannels::render_vfunc_thread(const Cairo::RefPtr<Cairo::Contex
}
if (unread_state < 1) return;
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
auto *paned = dynamic_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
if (paned != nullptr) {
const auto edge = std::min(paned->get_position(), cell_area.get_width());
@@ -522,7 +526,7 @@ void CellRendererChannels::render_vfunc_dmheader(const Cairo::RefPtr<Cairo::Cont
if (!Abaddon::Get().GetSettings().Unreads) return;
auto *paned = static_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
auto *paned = dynamic_cast<Gtk::Paned *>(widget.get_ancestor(Gtk::Paned::get_type()));
if (paned != nullptr) {
const auto edge = std::min(paned->get_position(), background_area.get_width());
if (const auto unread = Abaddon::Get().GetDiscordClient().GetUnreadDMsCount(); unread > 0)
@@ -587,7 +591,10 @@ void CellRendererChannels::render_vfunc_dm(const Cairo::RefPtr<Cairo::Context> &
const double text_w = text_natural.width;
const double text_h = text_natural.height;
Gdk::Rectangle text_cell_area(text_x, text_y, text_w, text_h);
Gdk::Rectangle text_cell_area(static_cast<int>(text_x),
static_cast<int>(text_y),
static_cast<int>(text_w),
static_cast<int>(text_h));
auto &discord = Abaddon::Get().GetDiscordClient();
const auto id = m_property_id.get_value();
@@ -642,7 +649,7 @@ void CellRendererChannels::cairo_path_rounded_rect(const Cairo::RefPtr<Cairo::Co
void CellRendererChannels::unread_render_mentions(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area) {
Pango::FontDescription font;
font.set_family("sans 14");
//font.set_weight(Pango::WEIGHT_BOLD);
// font.set_weight(Pango::WEIGHT_BOLD);
auto layout = widget.create_pango_layout(std::to_string(mentions));
layout->set_font_description(font);

View File

@@ -18,7 +18,7 @@ enum class RenderType : uint8_t {
class CellRendererChannels : public Gtk::CellRenderer {
public:
CellRendererChannels();
virtual ~CellRendererChannels();
~CellRendererChannels() override = default;
Glib::PropertyProxy<RenderType> property_type();
Glib::PropertyProxy<uint64_t> property_id();
@@ -106,7 +106,7 @@ protected:
Gtk::CellRendererState flags);
static void cairo_path_rounded_rect(const Cairo::RefPtr<Cairo::Context> &cr, double x, double y, double w, double h, double r);
void unread_render_mentions(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area);
static void unread_render_mentions(const Cairo::RefPtr<Cairo::Context> &cr, Gtk::Widget &widget, int mentions, int edge, const Gdk::Rectangle &cell_area);
private:
Gtk::CellRendererText m_renderer_text;

View File

@@ -25,16 +25,16 @@ ChatInputIndicator::ChatInputIndicator()
if (!std::filesystem::exists(path)) return;
auto gif_data = ReadWholeFile(path);
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&](int inw, int inh) {
int w, h;
GetImageDimensions(inw, inh, w, h, 20, 10);
loader->set_size(w, h);
});
loader->write(gif_data.data(), gif_data.size());
try {
loader->signal_size_prepared().connect([&](int inw, int inh) {
int w, h;
GetImageDimensions(inw, inh, w, h, 20, 10);
loader->set_size(w, h);
});
loader->write(gif_data.data(), gif_data.size());
loader->close();
m_img.property_pixbuf_animation() = loader->get_animation();
} catch (const std::exception &) {}
} catch (...) {}
}
void ChatInputIndicator::AddUser(Snowflake channel_id, const UserData &user, int timeout) {
@@ -84,14 +84,14 @@ void ChatInputIndicator::OnMessageCreate(const Message &message) {
void ChatInputIndicator::SetTypingString(const Glib::ustring &str) {
m_label.set_text(str);
if (str == "")
if (str.empty())
m_img.hide();
else if (m_img.property_pixbuf_animation().get_value())
m_img.show();
}
void ChatInputIndicator::ComputeTypingString() {
if (m_custom_markup != "") {
if (!m_custom_markup.empty()) {
m_label.set_markup(m_custom_markup);
m_img.hide();
return;
@@ -104,7 +104,7 @@ void ChatInputIndicator::ComputeTypingString() {
if (user.has_value())
typers.push_back(*user);
}
if (typers.size() == 0) {
if (typers.empty()) {
SetTypingString("");
} else if (typers.size() == 1) {
SetTypingString(typers[0].Username + " is typing...");

View File

@@ -1,6 +1,6 @@
#include "chatmessage.hpp"
#include "chatlist.hpp"
#include "abaddon.hpp"
#include "chatmessage.hpp"
#include "constants.hpp"
ChatList::ChatList() {
@@ -8,17 +8,11 @@ ChatList::ChatList() {
set_can_focus(false);
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
signal_edge_reached().connect(sigc::mem_fun(*this, &ChatList::OnScrollEdgeOvershot));
auto v = get_vadjustment();
v->signal_value_changed().connect([this, v] {
m_should_scroll_to_bottom = v->get_upper() - v->get_page_size() <= v->get_value();
});
get_vadjustment()->signal_value_changed().connect(sigc::mem_fun(*this, &ChatList::OnVAdjustmentValueChanged));
get_vadjustment()->property_upper().signal_changed().connect(sigc::mem_fun(*this, &ChatList::OnVAdjustmentUpperChanged));
m_list.signal_size_allocate().connect([this](Gtk::Allocation &) {
if (m_should_scroll_to_bottom)
ScrollToBottom();
});
m_list.signal_size_allocate().connect(sigc::mem_fun(*this, &ChatList::OnListSizeAllocate));
m_list.set_focus_hadjustment(get_hadjustment());
m_list.set_focus_vadjustment(get_vadjustment());
@@ -30,56 +24,7 @@ ChatList::ChatList() {
m_list.show();
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
m_menu_copy_id->signal_activate().connect([this] {
Gtk::Clipboard::get()->set_text(std::to_string(m_menu_selected_message));
});
m_menu_copy_id->show();
m_menu.append(*m_menu_copy_id);
m_menu_delete_message = Gtk::manage(new Gtk::MenuItem("Delete Message"));
m_menu_delete_message->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().DeleteMessage(m_active_channel, m_menu_selected_message);
});
m_menu_delete_message->show();
m_menu.append(*m_menu_delete_message);
m_menu_edit_message = Gtk::manage(new Gtk::MenuItem("Edit Message"));
m_menu_edit_message->signal_activate().connect([this] {
m_signal_action_message_edit.emit(m_active_channel, m_menu_selected_message);
});
m_menu_edit_message->show();
m_menu.append(*m_menu_edit_message);
m_menu_copy_content = Gtk::manage(new Gtk::MenuItem("Copy Content"));
m_menu_copy_content->signal_activate().connect([this] {
const auto msg = Abaddon::Get().GetDiscordClient().GetMessage(m_menu_selected_message);
if (msg.has_value())
Gtk::Clipboard::get()->set_text(msg->Content);
});
m_menu_copy_content->show();
m_menu.append(*m_menu_copy_content);
m_menu_reply_to = Gtk::manage(new Gtk::MenuItem("Reply To"));
m_menu_reply_to->signal_activate().connect([this] {
m_signal_action_reply_to.emit(m_menu_selected_message);
});
m_menu_reply_to->show();
m_menu.append(*m_menu_reply_to);
m_menu_unpin = Gtk::manage(new Gtk::MenuItem("Unpin"));
m_menu_unpin->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().Unpin(m_active_channel, m_menu_selected_message, [](...) {});
});
m_menu.append(*m_menu_unpin);
m_menu_pin = Gtk::manage(new Gtk::MenuItem("Pin"));
m_menu_pin->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().Pin(m_active_channel, m_menu_selected_message, [](...) {});
});
m_menu.append(*m_menu_pin);
m_menu.show();
SetupMenu();
}
void ChatList::Clear() {
@@ -98,6 +43,7 @@ void ChatList::SetActiveChannel(Snowflake id) {
void ChatList::ProcessNewMessage(const Message &data, bool prepend) {
auto &discord = Abaddon::Get().GetDiscordClient();
if (!discord.IsStarted()) return;
if (!prepend) m_ignore_next_upper = true;
// delete preview message when gateway sends it back
if (!data.IsPending && data.Nonce.has_value() && data.Author.ID == discord.GetUserData().ID) {
@@ -243,7 +189,7 @@ void ChatList::RefetchMessage(Snowflake id) {
}
Snowflake ChatList::GetOldestListedMessage() {
if (m_id_to_widget.size() > 0)
if (!m_id_to_widget.empty())
return m_id_to_widget.begin()->first;
else
return Snowflake::Invalid;
@@ -306,14 +252,85 @@ void ChatList::ActuallyRemoveMessage(Snowflake id) {
RemoveMessageAndHeader(it->second);
}
void ChatList::OnScrollEdgeOvershot(Gtk::PositionType pos) {
if (pos == Gtk::POS_TOP)
m_signal_action_chat_load_history.emit(m_active_channel);
void ChatList::SetupMenu() {
m_menu_copy_id = Gtk::manage(new Gtk::MenuItem("Copy ID"));
m_menu_copy_id->signal_activate().connect([this] {
Gtk::Clipboard::get()->set_text(std::to_string(m_menu_selected_message));
});
m_menu_copy_id->show();
m_menu.append(*m_menu_copy_id);
m_menu_delete_message = Gtk::manage(new Gtk::MenuItem("Delete Message"));
m_menu_delete_message->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().DeleteMessage(m_active_channel, m_menu_selected_message);
});
m_menu_delete_message->show();
m_menu.append(*m_menu_delete_message);
m_menu_edit_message = Gtk::manage(new Gtk::MenuItem("Edit Message"));
m_menu_edit_message->signal_activate().connect([this] {
m_signal_action_message_edit.emit(m_active_channel, m_menu_selected_message);
});
m_menu_edit_message->show();
m_menu.append(*m_menu_edit_message);
m_menu_copy_content = Gtk::manage(new Gtk::MenuItem("Copy Content"));
m_menu_copy_content->signal_activate().connect([this] {
const auto msg = Abaddon::Get().GetDiscordClient().GetMessage(m_menu_selected_message);
if (msg.has_value())
Gtk::Clipboard::get()->set_text(msg->Content);
});
m_menu_copy_content->show();
m_menu.append(*m_menu_copy_content);
m_menu_reply_to = Gtk::manage(new Gtk::MenuItem("Reply To"));
m_menu_reply_to->signal_activate().connect([this] {
m_signal_action_reply_to.emit(m_menu_selected_message);
});
m_menu_reply_to->show();
m_menu.append(*m_menu_reply_to);
m_menu_unpin = Gtk::manage(new Gtk::MenuItem("Unpin"));
m_menu_unpin->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().Unpin(m_active_channel, m_menu_selected_message, [](...) {});
});
m_menu.append(*m_menu_unpin);
m_menu_pin = Gtk::manage(new Gtk::MenuItem("Pin"));
m_menu_pin->signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().Pin(m_active_channel, m_menu_selected_message, [](...) {});
});
m_menu.append(*m_menu_pin);
m_menu.show();
}
void ChatList::ScrollToBottom() {
auto x = get_vadjustment();
x->set_value(x->get_upper());
auto v = get_vadjustment();
v->set_value(v->get_upper());
}
void ChatList::OnVAdjustmentValueChanged() {
auto v = get_vadjustment();
if (m_history_timer.elapsed() > 1 && v->get_value() < 500) {
m_history_timer.start();
m_signal_action_chat_load_history.emit(m_active_channel);
}
m_should_scroll_to_bottom = v->get_upper() - v->get_page_size() <= v->get_value();
}
void ChatList::OnVAdjustmentUpperChanged() {
auto v = get_vadjustment();
if (!m_ignore_next_upper && !m_should_scroll_to_bottom && m_old_upper > -1.0) {
const auto inc = v->get_upper() - m_old_upper;
v->set_value(v->get_value() + inc);
}
m_ignore_next_upper = false;
m_old_upper = v->get_upper();
}
void ChatList::OnListSizeAllocate(Gtk::Allocation &allocation) {
if (m_should_scroll_to_bottom) ScrollToBottom();
}
void ChatList::RemoveMessageAndHeader(Gtk::Widget *widget) {

View File

@@ -2,6 +2,7 @@
#include <gtkmm.h>
#include <map>
#include <vector>
#include "discord/message.hpp"
#include "discord/snowflake.hpp"
class ChatList : public Gtk::ScrolledWindow {
@@ -25,8 +26,11 @@ public:
void ActuallyRemoveMessage(Snowflake id); // perhaps not the best method name
private:
void OnScrollEdgeOvershot(Gtk::PositionType pos);
void SetupMenu();
void ScrollToBottom();
void OnVAdjustmentValueChanged();
void OnVAdjustmentUpperChanged();
void OnListSizeAllocate(Gtk::Allocation &allocation);
void RemoveMessageAndHeader(Gtk::Widget *widget);
bool m_use_pinned_menu = false;
@@ -47,11 +51,15 @@ private:
int m_num_rows = 0;
std::map<Snowflake, Gtk::Widget *> m_id_to_widget;
bool m_ignore_next_upper = false;
double m_old_upper = -1.0;
bool m_should_scroll_to_bottom = true;
Gtk::ListBox m_list;
bool m_separate_all = false;
Glib::Timer m_history_timer;
public:
// these are all forwarded by the parent
using type_signal_action_message_edit = sigc::signal<void, Snowflake, Snowflake>;
@@ -101,15 +109,6 @@ inline void ChatList::SetMessages(Iter begin, Iter end) {
template<typename Iter>
inline void ChatList::PrependMessages(Iter begin, Iter end) {
const auto old_upper = get_vadjustment()->get_upper();
const auto old_value = get_vadjustment()->get_value();
for (Iter it = begin; it != end; it++)
ProcessNewMessage(*it, true);
// force everything to process before getting new values
while (Gtk::Main::events_pending())
Gtk::Main::iteration();
const auto new_upper = get_vadjustment()->get_upper();
if (old_value == 0.0 && (new_upper - old_upper) > 0.0)
get_vadjustment()->set_value(new_upper - old_upper);
// this isn't ideal
}

View File

@@ -30,7 +30,7 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(const Message &d
if (data.Nonce.has_value())
container->Nonce = *data.Nonce;
if (data.Content.size() > 0 || data.Type != MessageType::DEFAULT) {
if (!data.Content.empty() || data.Type != MessageType::DEFAULT) {
container->m_text_component = container->CreateTextComponent(data);
container->AttachEventHandlers(*container->m_text_component);
container->m_main.add(*container->m_text_component);
@@ -78,7 +78,7 @@ ChatMessageItemContainer *ChatMessageItemContainer::FromMessage(const Message &d
container->m_main.add(*widget);
}
if (data.Reactions.has_value() && data.Reactions->size() > 0) {
if (data.Reactions.has_value() && !data.Reactions->empty()) {
container->m_reactions_component = container->CreateReactionsComponent(data);
container->m_main.add(*container->m_reactions_component);
}
@@ -114,7 +114,7 @@ void ChatMessageItemContainer::UpdateReactions() {
}
const auto data = Abaddon::Get().GetDiscordClient().GetMessage(ID);
if (data->Reactions.has_value() && data->Reactions->size() > 0) {
if (data->Reactions.has_value() && !data->Reactions->empty()) {
m_reactions_component = CreateReactionsComponent(*data);
m_reactions_component->show_all();
m_main.add(*m_reactions_component);
@@ -150,7 +150,7 @@ void ChatMessageItemContainer::UpdateAttributes() {
m_attrib_label->set_markup("<span color='#999999'>[edited]</span>");
}
void ChatMessageItemContainer::AddClickHandler(Gtk::Widget *widget, std::string url) {
void ChatMessageItemContainer::AddClickHandler(Gtk::Widget *widget, const std::string &url) {
// clang-format off
widget->signal_button_press_event().connect([url](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
@@ -224,13 +224,13 @@ void ChatMessageItemContainer::UpdateTextComponent(Gtk::TextView *tv) {
}
} break;
case MessageType::RECIPIENT_ADD: {
if (data->Mentions.size() == 0) break;
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>");
} break;
case MessageType::RECIPIENT_REMOVE: {
if (data->Mentions.size() == 0) break;
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)
@@ -361,7 +361,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const EmbedData &emb
if (embed.URL.has_value()) {
AddPointerCursor(*title_ev);
auto url = *embed.URL;
title_ev->signal_button_press_event().connect([this, url = std::move(url)](GdkEventButton *event) -> bool {
title_ev->signal_button_press_event().connect([url = std::move(url)](GdkEventButton *event) -> bool {
if (event->button == GDK_BUTTON_PRIMARY) {
LaunchBrowser(url);
return true;
@@ -389,7 +389,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateEmbedComponent(const EmbedData &emb
}
// todo: handle inline fields
if (embed.Fields.has_value() && embed.Fields->size() > 0) {
if (embed.Fields.has_value() && !embed.Fields->empty()) {
auto *flow = Gtk::manage(new Gtk::FlowBox);
flow->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
flow->set_min_children_per_line(3);
@@ -516,23 +516,6 @@ Gtk::Widget *ChatMessageItemContainer::CreateAttachmentComponent(const Attachmen
return ev;
}
Gtk::Widget *ChatMessageItemContainer::CreateStickerComponentDeprecated(const StickerData &data) {
auto *box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
auto *imgw = Gtk::manage(new Gtk::Image);
box->add(*imgw);
auto &img = Abaddon::Get().GetImageManager();
if (data.FormatType == StickerFormatType::PNG || data.FormatType == StickerFormatType::APNG) {
auto cb = [this, imgw](const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) {
imgw->property_pixbuf() = pixbuf;
};
img.LoadFromURL(data.GetURL(), sigc::track_obj(cb, *imgw));
}
AttachEventHandlers(*box);
return box;
}
Gtk::Widget *ChatMessageItemContainer::CreateStickersComponent(const std::vector<StickerItem> &data) {
auto *box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
@@ -541,6 +524,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateStickersComponent(const std::vector
if (sticker.FormatType != StickerFormatType::PNG && sticker.FormatType != StickerFormatType::APNG) continue;
auto *ev = Gtk::manage(new Gtk::EventBox);
auto *img = Gtk::manage(new LazyImage(sticker.GetURL(), StickerComponentSize, StickerComponentSize, false));
img->set_halign(Gtk::ALIGN_START);
img->set_size_request(StickerComponentSize, StickerComponentSize); // should this go in LazyImage ?
img->show();
ev->show();
@@ -607,7 +591,7 @@ Gtk::Widget *ChatMessageItemContainer::CreateReactionsComponent(const Message &d
// image
if (is_stock) { // unicode/stock
const auto shortcode = emojis.GetShortCodeForPattern(reaction.Emoji.Name);
if (shortcode != "")
if (!shortcode.empty())
ev->set_tooltip_text(shortcode);
const auto &pb = emojis.GetPixBuf(reaction.Emoji.Name);
@@ -783,7 +767,7 @@ void ChatMessageItemContainer::HandleRoleMentions(const Glib::RefPtr<Gtk::TextBu
}
}
void ChatMessageItemContainer::HandleUserMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf) {
void ChatMessageItemContainer::HandleUserMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf) const {
constexpr static const auto mentions_regex = R"(<@!?(\d+)>)";
static auto rgx = Glib::Regex::create(mentions_regex);
@@ -860,7 +844,7 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
const auto mark_start = buf->create_mark(start_it, false);
end_it.backward_char();
const auto mark_end = buf->create_mark(end_it, false);
const auto cb = [this, &tv, buf, mark_start, mark_end](const Glib::RefPtr<Gdk::PixbufAnimation> &pixbuf) {
const auto cb = [&tv, buf, mark_start, mark_end](const Glib::RefPtr<Gdk::PixbufAnimation> &pixbuf) {
auto start_it = mark_start->get_iter();
auto end_it = mark_end->get_iter();
end_it.forward_char();
@@ -878,7 +862,7 @@ void ChatMessageItemContainer::HandleCustomEmojis(Gtk::TextView &tv) {
const auto mark_start = buf->create_mark(start_it, false);
end_it.backward_char();
const auto mark_end = buf->create_mark(end_it, false);
const auto cb = [this, buf, mark_start, mark_end](const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) {
const auto cb = [buf, mark_start, mark_end](const Glib::RefPtr<Gdk::Pixbuf> &pixbuf) {
auto start_it = mark_start->get_iter();
auto end_it = mark_end->get_iter();
end_it.forward_char();
@@ -901,7 +885,7 @@ void ChatMessageItemContainer::HandleEmojis(Gtk::TextView &tv) {
if (Abaddon::Get().GetSettings().ShowCustomEmojis) HandleCustomEmojis(tv);
}
void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf) {
void ChatMessageItemContainer::CleanupEmojis(const Glib::RefPtr<Gtk::TextBuffer> &buf) {
static auto rgx = Glib::Regex::create(R"(<a?:([\w\d_]+):(\d+)>)");
auto text = GetText(buf);
@@ -921,9 +905,9 @@ void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf)
startpos = mend;
const auto it = buf->erase(start_it, end_it);
const int alen = text.size();
const int alen = static_cast<int>(text.size());
text = GetText(buf);
const int blen = text.size();
const int blen = static_cast<int>(text.size());
startpos -= (alen - blen);
buf->insert(it, new_term);
@@ -932,7 +916,7 @@ void ChatMessageItemContainer::CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf)
}
}
void ChatMessageItemContainer::HandleChannelMentions(Glib::RefPtr<Gtk::TextBuffer> buf) {
void ChatMessageItemContainer::HandleChannelMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf) {
static auto rgx = Glib::Regex::create(R"(<#(\d+)>)");
Glib::ustring text = GetText(buf);
@@ -989,12 +973,12 @@ bool ChatMessageItemContainer::OnClickChannel(GdkEventButton *ev) {
return false;
int x, y;
m_text_component->window_to_buffer_coords(Gtk::TEXT_WINDOW_WIDGET, ev->x, ev->y, x, y);
m_text_component->window_to_buffer_coords(Gtk::TEXT_WINDOW_WIDGET, static_cast<int>(ev->x), static_cast<int>(ev->y), x, y);
Gtk::TextBuffer::iterator iter;
m_text_component->get_iter_at_location(iter, x, y);
const auto tags = iter.get_tags();
for (auto tag : tags) {
for (const auto &tag : tags) {
const auto it = m_channel_tagmap.find(tag);
if (it != m_channel_tagmap.end()) {
m_signal_action_channel_click.emit(it->second);
@@ -1052,12 +1036,12 @@ bool ChatMessageItemContainer::OnLinkClick(GdkEventButton *ev) {
return false;
int x, y;
m_text_component->window_to_buffer_coords(Gtk::TEXT_WINDOW_WIDGET, ev->x, ev->y, x, y);
m_text_component->window_to_buffer_coords(Gtk::TEXT_WINDOW_WIDGET, static_cast<int>(ev->x), static_cast<int>(ev->y), x, y);
Gtk::TextBuffer::iterator iter;
m_text_component->get_iter_at_location(iter, x, y);
const auto tags = iter.get_tags();
for (auto tag : tags) {
for (const auto &tag : tags) {
const auto it = m_link_tagmap.find(tag);
if (it != m_link_tagmap.end()) {
if (ev->button == GDK_BUTTON_PRIMARY) {
@@ -1130,11 +1114,10 @@ ChatMessageHeader::ChatMessageHeader(const Message &data)
m_avatar.set_valign(Gtk::ALIGN_START);
m_avatar.set_margin_right(10);
m_author.set_markup(data.Author.GetEscapedBoldName());
m_author.set_single_line_mode(true);
m_author.set_line_wrap(false);
m_author.set_ellipsize(Pango::ELLIPSIZE_END);
m_author.set_xalign(0.f);
m_author.set_xalign(0.0F);
m_author.set_can_focus(false);
m_meta_ev.signal_button_press_event().connect(sigc::mem_fun(*this, &ChatMessageHeader::on_author_button_press));
@@ -1211,30 +1194,32 @@ ChatMessageHeader::ChatMessageHeader(const Message &data)
show_all();
auto &discord = Abaddon::Get().GetDiscordClient();
auto role_update_cb = [this](...) { UpdateNameColor(); };
auto role_update_cb = [this](...) { UpdateName(); };
discord.signal_role_update().connect(sigc::track_obj(role_update_cb, *this));
auto guild_member_update_cb = [this](const auto &, const auto &) { UpdateNameColor(); };
auto guild_member_update_cb = [this](const auto &, const auto &) { UpdateName(); };
discord.signal_guild_member_update().connect(sigc::track_obj(guild_member_update_cb, *this));
UpdateNameColor();
UpdateName();
AttachUserMenuHandler(m_meta_ev);
AttachUserMenuHandler(m_avatar_ev);
}
void ChatMessageHeader::UpdateNameColor() {
void ChatMessageHeader::UpdateName() {
const auto &discord = Abaddon::Get().GetDiscordClient();
const auto user = discord.GetUser(UserID);
if (!user.has_value()) return;
const auto chan = discord.GetChannel(ChannelID);
bool is_guild = chan.has_value() && chan->GuildID.has_value();
if (is_guild) {
const auto member = discord.GetMember(UserID, *chan->GuildID);
const auto role_id = discord.GetMemberHoistedRole(*chan->GuildID, UserID, true);
const auto role = discord.GetRole(role_id);
const auto name = GetEscapedDisplayName(*user, member);
std::string md;
if (role.has_value())
m_author.set_markup("<span weight='bold' color='#" + IntToCSSColor(role->Color) + "'>" + user->GetEscapedName() + "</span>");
m_author.set_markup("<span weight='bold' color='#" + IntToCSSColor(role->Color) + "'>" + name + "</span>");
else
m_author.set_markup("<span weight='bold'>" + user->GetEscapedName() + "</span>");
m_author.set_markup("<span weight='bold'>" + name + "</span>");
} else
m_author.set_markup("<span weight='bold'>" + user->GetEscapedName() + "</span>");
}
@@ -1258,6 +1243,13 @@ void ChatMessageHeader::AttachUserMenuHandler(Gtk::Widget &widget) {
});
}
Glib::ustring ChatMessageHeader::GetEscapedDisplayName(const UserData &user, const std::optional<GuildMember> &member) {
if (member.has_value() && !member->Nickname.empty())
return Glib::Markup::escape_text(member->Nickname);
else
return Glib::Markup::escape_text(user.GetEscapedName());
}
bool ChatMessageHeader::on_author_button_press(GdkEventButton *ev) {
if (ev->button == GDK_BUTTON_PRIMARY && (ev->state & GDK_SHIFT_MASK)) {
m_signal_action_insert_mention.emit();

View File

@@ -19,14 +19,13 @@ public:
void SetFailed();
protected:
void AddClickHandler(Gtk::Widget *widget, std::string);
static void AddClickHandler(Gtk::Widget *widget, const std::string &);
Gtk::TextView *CreateTextComponent(const Message &data); // Message.Content
void UpdateTextComponent(Gtk::TextView *tv);
Gtk::Widget *CreateEmbedsComponent(const std::vector<EmbedData> &embeds);
Gtk::Widget *CreateEmbedComponent(const EmbedData &data); // Message.Embeds[0]
static Gtk::Widget *CreateEmbedComponent(const EmbedData &data); // Message.Embeds[0]
Gtk::Widget *CreateImageComponent(const std::string &proxy_url, const std::string &url, int inw, int inh);
Gtk::Widget *CreateAttachmentComponent(const AttachmentData &data); // non-image attachments
Gtk::Widget *CreateStickerComponentDeprecated(const StickerData &data);
Gtk::Widget *CreateStickersComponent(const std::vector<StickerItem> &data);
Gtk::Widget *CreateReactionsComponent(const Message &data);
Gtk::Widget *CreateReplyComponent(const Message &data);
@@ -35,14 +34,14 @@ protected:
static bool IsEmbedImageOnly(const EmbedData &data);
void HandleRoleMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleUserMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleStockEmojis(Gtk::TextView &tv);
void HandleCustomEmojis(Gtk::TextView &tv);
void HandleEmojis(Gtk::TextView &tv);
void CleanupEmojis(Glib::RefPtr<Gtk::TextBuffer> buf);
static void HandleRoleMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleUserMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf) const;
static void HandleStockEmojis(Gtk::TextView &tv);
static void HandleCustomEmojis(Gtk::TextView &tv);
static void HandleEmojis(Gtk::TextView &tv);
static void CleanupEmojis(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleChannelMentions(Glib::RefPtr<Gtk::TextBuffer> buf);
void HandleChannelMentions(const Glib::RefPtr<Gtk::TextBuffer> &buf);
void HandleChannelMentions(Gtk::TextView *tv);
bool OnClickChannel(GdkEventButton *ev);
@@ -91,11 +90,12 @@ public:
ChatMessageHeader(const Message &data);
void AddContent(Gtk::Widget *widget, bool prepend);
void UpdateNameColor();
void UpdateName();
std::vector<Gtk::Widget *> GetChildContent();
protected:
void AttachUserMenuHandler(Gtk::Widget &widget);
static Glib::ustring GetEscapedDisplayName(const UserData &user, const std::optional<GuildMember> &member);
bool on_author_button_press(GdkEventButton *ev);

View File

@@ -1,5 +1,4 @@
#include "chatwindow.hpp"
#include "chatmessage.hpp"
#include "abaddon.hpp"
#include "chatinputindicator.hpp"
#include "ratelimitindicator.hpp"
@@ -88,7 +87,7 @@ ChatWindow::ChatWindow() {
m_meta->add(*m_input_indicator);
m_meta->add(*m_rate_limit_indicator);
//m_scroll->add(*m_list);
// m_scroll->add(*m_list);
m_main->add(m_topic);
m_main->add(*m_chat);
m_main->add(m_completer);
@@ -134,7 +133,7 @@ void ChatWindow::AddNewHistory(const std::vector<Message> &msgs) {
m_chat->PrependMessages(msgs.crbegin(), msgs.crend());
}
void ChatWindow::InsertChatInput(std::string text) {
void ChatWindow::InsertChatInput(const std::string &text) {
m_input->InsertText(text);
}
@@ -159,7 +158,7 @@ bool ChatWindow::OnInputSubmit(const Glib::ustring &text) {
if (!m_rate_limit_indicator->CanSpeak())
return false;
if (text.size() == 0)
if (text.empty())
return false;
if (m_active_channel.IsValid())

View File

@@ -25,7 +25,7 @@ public:
void DeleteMessage(Snowflake id); // add [deleted] indicator
void UpdateMessage(Snowflake id); // add [edited] indicator
void AddNewHistory(const std::vector<Message> &msgs); // prepend messages
void InsertChatInput(std::string text);
void InsertChatInput(const std::string& text);
Snowflake GetOldestListedMessage(); // oldest message that is currently in the ListBox
void UpdateReactions(Snowflake id);
void SetTopic(const std::string &text);
@@ -47,8 +47,8 @@ protected:
void OnMessageSendFail(const std::string &nonce, float retry_after);
Gtk::Box *m_main;
//Gtk::ListBox *m_list;
//Gtk::ScrolledWindow *m_scroll;
// Gtk::ListBox *m_list;
// Gtk::ScrolledWindow *m_scroll;
Gtk::EventBox m_topic; // todo probably make everything else go on the stack
Gtk::Label m_topic_text;

View File

@@ -1,4 +1,5 @@
#include <unordered_set>
#include <utility>
#include "completer.hpp"
#include "abaddon.hpp"
#include "util.hpp"
@@ -46,7 +47,7 @@ bool Completer::ProcessKeyPress(GdkEventKey *e) {
switch (e->keyval) {
case GDK_KEY_Down: {
if (m_entries.size() == 0) return true;
if (m_entries.empty()) return true;
const auto index = static_cast<size_t>(m_list.get_selected_row()->get_index());
if (index >= m_entries.size() - 1) return true;
m_list.select_row(*m_entries[index + 1]);
@@ -54,7 +55,7 @@ bool Completer::ProcessKeyPress(GdkEventKey *e) {
}
return true;
case GDK_KEY_Up: {
if (m_entries.size() == 0) return true;
if (m_entries.empty()) return true;
const auto index = static_cast<size_t>(m_list.get_selected_row()->get_index());
if (index == 0) return true;
m_list.select_row(*m_entries[index - 1]);
@@ -62,7 +63,7 @@ bool Completer::ProcessKeyPress(GdkEventKey *e) {
}
return true;
case GDK_KEY_Return: {
if (m_entries.size() == 0) return true;
if (m_entries.empty()) return true;
DoCompletion(m_list.get_selected_row());
}
return true;
@@ -74,11 +75,11 @@ bool Completer::ProcessKeyPress(GdkEventKey *e) {
}
void Completer::SetGetRecentAuthors(get_recent_authors_cb cb) {
m_recent_authors_cb = cb;
m_recent_authors_cb = std::move(cb);
}
void Completer::SetGetChannelID(get_channel_id_cb cb) {
m_channel_id_cb = cb;
m_channel_id_cb = std::move(cb);
}
bool Completer::IsShown() const {
@@ -86,7 +87,7 @@ bool Completer::IsShown() const {
}
CompleterEntry *Completer::CreateEntry(const Glib::ustring &completion) {
auto entry = Gtk::manage(new CompleterEntry(completion, m_entries.size()));
auto entry = Gtk::manage(new CompleterEntry(completion, static_cast<int>(m_entries.size())));
m_entries.push_back(entry);
entry->show_all();
m_list.add(*entry);
@@ -152,7 +153,7 @@ void Completer::CompleteEmojis(const Glib::ustring &term) {
const auto make_entry = [&](const Glib::ustring &name, const Glib::ustring &completion, const Glib::ustring &url = "", bool animated = false) -> CompleterEntry * {
const auto entry = CreateEntry(completion);
entry->SetText(name);
if (url == "") return entry;
if (url.empty()) return entry;
if (animated)
entry->SetAnimation(url);
else
@@ -173,8 +174,8 @@ void Completer::CompleteEmojis(const Glib::ustring &term) {
const auto emoji = *discord.GetEmoji(tmp.ID);
if (emoji.IsAnimated.has_value() && *emoji.IsAnimated) continue;
if (emoji.IsAvailable.has_value() && !*emoji.IsAvailable) continue;
if (emoji.Roles.has_value() && emoji.Roles->size() > 0) continue;
if (term.size() > 0)
if (emoji.Roles.has_value() && !emoji.Roles->empty()) continue;
if (!term.empty())
if (!StringContainsCaseless(emoji.Name, term)) continue;
if (i++ > MaxCompleterEntries) break;
@@ -190,8 +191,8 @@ void Completer::CompleteEmojis(const Glib::ustring &term) {
const auto emoji = *discord.GetEmoji(tmp.ID);
const bool is_animated = emoji.IsAnimated.has_value() && *emoji.IsAnimated;
if (emoji.IsAvailable.has_value() && !*emoji.IsAvailable) continue;
if (emoji.Roles.has_value() && emoji.Roles->size() > 0) continue;
if (term.size() > 0)
if (emoji.Roles.has_value() && !emoji.Roles->empty()) continue;
if (!term.empty())
if (!StringContainsCaseless(emoji.Name, term)) continue;
if (i++ > MaxCompleterEntries) goto done;
@@ -275,7 +276,7 @@ void Completer::OnTextBufferChanged() {
default:
break;
}
if (m_entries.size() > 0) {
if (!m_entries.empty()) {
m_list.select_row(*m_entries[0]);
set_reveal_child(true);
} else {
@@ -329,8 +330,8 @@ Glib::ustring Completer::GetTerm() {
return m_start.get_text(m_end);
}
CompleterEntry::CompleterEntry(const Glib::ustring &completion, int index)
: m_completion(completion)
CompleterEntry::CompleterEntry(Glib::ustring completion, int index)
: m_completion(std::move(completion))
, m_index(index)
, m_box(Gtk::ORIENTATION_HORIZONTAL) {
set_halign(Gtk::ALIGN_START);

View File

@@ -8,7 +8,7 @@ constexpr static int CompleterImageSize = 24;
class CompleterEntry : public Gtk::ListBoxRow {
public:
CompleterEntry(const Glib::ustring &completion, int index);
CompleterEntry(Glib::ustring completion, int index);
void SetTextColor(int color); // SetText will reset
void SetText(const Glib::ustring &text);
void SetImage(const Glib::RefPtr<Gdk::Pixbuf> &pb);

View File

@@ -102,7 +102,7 @@ bool DragListBox::scroll() {
}
void DragListBox::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time) {
int index = 0;
int index;
if (m_hover_row != nullptr) {
if (m_top) {
index = m_hover_row->get_index() - 1;
@@ -130,7 +130,7 @@ void DragListBox::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &co
void DragListBox::add_draggable(Gtk::ListBoxRow *widget) {
widget->drag_source_set(m_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
widget->signal_drag_begin().connect(sigc::bind<0>(sigc::mem_fun(*this, &DragListBox::row_drag_begin), widget));
widget->signal_drag_data_get().connect([this, widget](const Glib::RefPtr<Gdk::DragContext> &context, Gtk::SelectionData &selection_data, guint info, guint time) {
widget->signal_drag_data_get().connect([widget](const Glib::RefPtr<Gdk::DragContext> &context, Gtk::SelectionData &selection_data, guint info, guint time) {
selection_data.set("GTK_LIST_BOX_ROW", 32, reinterpret_cast<const guint8 *>(&widget), sizeof(&widget));
});
add(*widget);

View File

@@ -133,7 +133,7 @@ void FriendsList::OnActionRemove(Snowflake id) {
break;
}
if (Abaddon::Get().ShowConfirm(str, window)) {
const auto cb = [this, window](DiscordError code) {
const auto cb = [window](DiscordError code) {
if (code == DiscordError::NONE) return;
Gtk::MessageDialog dlg(*window, "Failed to remove user", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -215,7 +215,7 @@ void FriendsListAddComponent::Submit() {
if (hashpos == Glib::ustring::npos) return;
const auto username = text.substr(0, hashpos);
const auto discriminator = text.substr(hashpos + 1);
if (username.size() == 0 || discriminator.size() != 4) return;
if (username.empty() || discriminator.size() != 4) return;
if (discriminator.find_first_not_of("0123456789") != Glib::ustring::npos) return;
m_requesting = true;
@@ -229,7 +229,9 @@ void FriendsListAddComponent::Submit() {
m_label.set_text("Failed: "s + GetDiscordErrorDisplayString(code));
}
};
Abaddon::Get().GetDiscordClient().SendFriendRequest(username, std::stoul(discriminator), sigc::track_obj(cb, *this));
Abaddon::Get().GetDiscordClient().SendFriendRequest(username,
static_cast<int>(std::stoul(discriminator)),
sigc::track_obj(cb, *this));
}
bool FriendsListAddComponent::OnKeyPress(GdkEventKey *e) {

View File

@@ -1,4 +1,6 @@
#include "lazyimage.hpp"
#include <utility>
#include "abaddon.hpp"
LazyImage::LazyImage(int w, int h, bool use_placeholder)
@@ -9,8 +11,8 @@ LazyImage::LazyImage(int w, int h, bool use_placeholder)
signal_draw().connect(sigc::mem_fun(*this, &LazyImage::OnDraw));
}
LazyImage::LazyImage(const std::string &url, int w, int h, bool use_placeholder)
: m_url(url)
LazyImage::LazyImage(std::string url, int w, int h, bool use_placeholder)
: m_url(std::move(url))
, m_width(w)
, m_height(h) {
if (use_placeholder)
@@ -27,7 +29,7 @@ void LazyImage::SetURL(const std::string &url) {
}
bool LazyImage::OnDraw(const Cairo::RefPtr<Cairo::Context> &context) {
if (!m_needs_request || m_url == "") return false;
if (!m_needs_request || m_url.empty()) return false;
m_needs_request = false;
if (m_animated) {

View File

@@ -5,7 +5,7 @@
class LazyImage : public Gtk::Image {
public:
LazyImage(int w, int h, bool use_placeholder = true);
LazyImage(const std::string &url, int w, int h, bool use_placeholder = true);
LazyImage(std::string url, int w, int h, bool use_placeholder = true);
void SetAnimated(bool is_animated);
void SetURL(const std::string &url);

View File

@@ -151,7 +151,7 @@ void MemberList::UpdateMemberList() {
if (!pos_role.has_value()) {
roleless_users.push_back(id);
continue;
};
}
pos_to_role[pos_role->Position] = *pos_role;
pos_to_users[pos_role->Position].push_back(std::move(*user));
@@ -161,7 +161,7 @@ void MemberList::UpdateMemberList() {
int num_rows = 0;
const auto guild = *discord.GetGuild(m_guild_id);
auto add_user = [this, &user_to_color, &num_rows, guild](const UserData &data) -> bool {
auto add_user = [this, &num_rows, guild](const UserData &data) -> bool {
if (num_rows++ > MaxMemberListRows) return false;
auto *row = Gtk::manage(new MemberListUserRow(guild, data));
m_id_to_row[data.ID] = row;
@@ -170,7 +170,7 @@ void MemberList::UpdateMemberList() {
return true;
};
auto add_role = [this](std::string name) {
auto add_role = [this](const std::string &name) {
auto *role_row = Gtk::manage(new Gtk::ListBoxRow);
auto *role_lbl = Gtk::manage(new Gtk::Label);
@@ -215,7 +215,7 @@ void MemberList::UpdateMemberList() {
}
void MemberList::AttachUserMenuHandler(Gtk::ListBoxRow *row, Snowflake id) {
row->signal_button_press_event().connect([this, row, id](GdkEventButton *e) -> bool {
row->signal_button_press_event().connect([this, id](GdkEventButton *e) -> bool {
if (e->type == GDK_BUTTON_PRESS && e->button == GDK_BUTTON_SECONDARY) {
Abaddon::Get().ShowUserMenu(reinterpret_cast<const GdkEvent *>(e), id, m_guild_id);
return true;

View File

@@ -66,7 +66,7 @@ int RateLimitIndicator::GetTimeLeft() const {
if (sec_diff <= 0)
return 0;
else
return sec_diff;
return static_cast<int>(sec_diff);
}
int RateLimitIndicator::GetRateLimit() const {

View File

@@ -2,10 +2,6 @@
#include "abaddon.hpp"
static const constexpr int Diameter = 8;
static const auto OnlineColor = Gdk::RGBA("#43B581");
static const auto IdleColor = Gdk::RGBA("#FAA61A");
static const auto DNDColor = Gdk::RGBA("#982929");
static const auto OfflineColor = Gdk::RGBA("#808080");
StatusIndicator::StatusIndicator(Snowflake user_id)
: Glib::ObjectBase("statusindicator")
@@ -26,9 +22,6 @@ StatusIndicator::StatusIndicator(Snowflake user_id)
CheckStatus();
}
StatusIndicator::~StatusIndicator() {
}
void StatusIndicator::CheckStatus() {
const auto status = Abaddon::Get().GetDiscordClient().GetUserStatus(m_id);
const auto last_status = m_status;
@@ -121,7 +114,7 @@ bool StatusIndicator::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
const auto color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL);
cr->set_source_rgb(color.get_red(), color.get_green(), color.get_blue());
cr->arc(width / 2, height / 2, width / 3, 0.0, 2 * (4 * std::atan(1)));
cr->arc(width / 2.0, height / 2.0, width / 3.0, 0.0, 2 * (4 * std::atan(1)));
cr->close_path();
cr->fill_preserve();
cr->stroke();

View File

@@ -6,7 +6,7 @@
class StatusIndicator : public Gtk::Widget {
public:
StatusIndicator(Snowflake user_id);
virtual ~StatusIndicator();
~StatusIndicator() override = default;
protected:
Gtk::SizeRequestMode get_request_mode_vfunc() const override;

View File

@@ -10,7 +10,7 @@ public:
protected:
void on_entry_changed();
bool IsCode(std::string str);
static bool IsCode(std::string str);
Gtk::Box m_layout;
Gtk::Button m_ok;

View File

@@ -1,6 +1,6 @@
#include "token.hpp"
std::string trim(const std::string& str) {
std::string trim(const std::string &str) {
const auto first = str.find_first_not_of(' ');
if (first == std::string::npos) return str;
const auto last = str.find_last_not_of(' ');
@@ -30,6 +30,8 @@ TokenDialog::TokenDialog(Gtk::Window &parent)
m_bbox.pack_start(m_cancel, Gtk::PACK_SHRINK);
m_bbox.set_layout(Gtk::BUTTONBOX_END);
m_entry.set_input_purpose(Gtk::INPUT_PURPOSE_PASSWORD);
m_entry.set_visibility(false);
m_entry.set_hexpand(true);
m_layout.add(m_entry);
m_layout.add(m_bbox);

View File

@@ -26,7 +26,7 @@ constexpr inline const char *GetPresenceString(PresenceStatus s) {
return "";
}
constexpr inline const char* GetPresenceDisplayString(PresenceStatus s) {
constexpr inline const char *GetPresenceDisplayString(PresenceStatus s) {
switch (s) {
case PresenceStatus::Online:
return "Online";

View File

@@ -4,7 +4,7 @@
struct BanData {
std::string Reason; // null
UserData User; // access id
UserData User; // access id
friend void from_json(const nlohmann::json &j, BanData &m);
};

View File

@@ -116,5 +116,8 @@ std::vector<UserData> ChannelData::GetDMRecipients() const {
return ret;
}
return std::vector<UserData>();
return {};
}
bool ChannelData::IsText() const noexcept {
return Type == ChannelType::GUILD_TEXT || Type == ChannelType::GUILD_NEWS;
}

View File

@@ -94,14 +94,15 @@ struct ChannelData {
friend void from_json(const nlohmann::json &j, ChannelData &m);
void update_from_json(const nlohmann::json &j);
bool NSFW() const;
bool IsDM() const noexcept;
bool IsThread() const noexcept;
bool IsJoinedThread() const;
bool IsCategory() const noexcept;
bool HasIcon() const noexcept;
std::string GetIconURL() const;
std::vector<Snowflake> GetChildIDs() const;
std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
std::vector<UserData> GetDMRecipients() const;
[[nodiscard]] bool NSFW() const;
[[nodiscard]] bool IsDM() const noexcept;
[[nodiscard]] bool IsThread() const noexcept;
[[nodiscard]] bool IsJoinedThread() const;
[[nodiscard]] bool IsCategory() const noexcept;
[[nodiscard]] bool IsText() const noexcept;
[[nodiscard]] bool HasIcon() const noexcept;
[[nodiscard]] std::string GetIconURL() const;
[[nodiscard]] std::vector<Snowflake> GetChildIDs() const;
[[nodiscard]] std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
[[nodiscard]] std::vector<UserData> GetDMRecipients() const;
};

View File

@@ -1,8 +1,8 @@
#include "abaddon.hpp"
#include "discord.hpp"
#include "util.hpp"
#include <cassert>
#include <cinttypes>
#include <utility>
using namespace std::string_literals;
@@ -68,10 +68,6 @@ bool DiscordClient::IsStoreValid() const {
return m_store.IsValid();
}
const UserSettings &DiscordClient::GetUserSettings() const {
return m_user_settings;
}
std::unordered_set<Snowflake> DiscordClient::GetGuilds() const {
return m_store.GetGuilds();
}
@@ -87,7 +83,7 @@ std::vector<Snowflake> DiscordClient::GetUserSortedGuilds() const {
auto guilds = GetGuilds();
for (const auto &entry : m_user_settings.GuildFolders) { // can contain guilds not a part of
for (const auto &id : entry.GuildIDs) {
if (std::find(guilds.begin(), guilds.end(), id) != guilds.end())
if (guilds.find(id) != guilds.end())
folder_order.push_back(id);
}
}
@@ -115,19 +111,19 @@ std::vector<Message> DiscordClient::GetMessagesBefore(Snowflake channel_id, Snow
return m_store.GetMessagesBefore(channel_id, message_id, limit);
}
void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback) {
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](http::response_type r) {
void DiscordClient::FetchInvite(const std::string &code, const sigc::slot<void(std::optional<InviteData>)> &callback) {
m_http.MakeGET("/invites/" + code + "?with_counts=true", [callback](http::response_type r) {
if (!CheckCode(r)) {
if (r.status_code == 404)
callback(std::nullopt);
return;
};
}
callback(nlohmann::json::parse(r.text).get<InviteData>());
});
}
void DiscordClient::FetchMessagesInChannel(Snowflake id, sigc::slot<void(const std::vector<Message> &)> cb) {
void DiscordClient::FetchMessagesInChannel(Snowflake id, const sigc::slot<void(const std::vector<Message> &)> &cb) {
std::string path = "/channels/" + std::to_string(id) + "/messages?limit=50";
m_http.MakeGET(path, [this, id, cb](const http::response_type &r) {
if (!CheckCode(r)) {
@@ -164,9 +160,9 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, sigc::slot<void(const s
});
}
void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, sigc::slot<void(const std::vector<Message> &)> cb) {
void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, const sigc::slot<void(const std::vector<Message> &)> &cb) {
std::string path = "/channels/" + std::to_string(channel_id) + "/messages?limit=50&before=" + std::to_string(before_id);
m_http.MakeGET(path, [this, channel_id, cb](http::response_type r) {
m_http.MakeGET(path, [this, cb](http::response_type r) {
if (!CheckCode(r)) return;
std::vector<Message> msgs;
@@ -210,10 +206,6 @@ std::optional<GuildMember> DiscordClient::GetMember(Snowflake user_id, Snowflake
return m_store.GetGuildMember(guild_id, user_id);
}
std::optional<BanData> DiscordClient::GetBan(Snowflake guild_id, Snowflake user_id) const {
return m_store.GetBan(guild_id, user_id);
}
std::optional<PermissionOverwrite> DiscordClient::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const {
return m_store.GetPermissionOverwrite(channel_id, id);
}
@@ -243,14 +235,14 @@ std::optional<RoleData> DiscordClient::GetMemberHighestRole(Snowflake guild_id,
const auto data = GetMember(user_id, guild_id);
if (!data.has_value()) return std::nullopt;
if (data->Roles.size() == 0) return std::nullopt;
if (data->Roles.empty()) return std::nullopt;
if (data->Roles.size() == 1) return GetRole(data->Roles[0]);
std::vector<RoleData> roles;
for (const auto id : data->Roles)
roles.push_back(*GetRole(id));
return *std::max_element(roles.begin(), roles.end(), [this](const auto &a, const auto &b) -> bool {
return *std::max_element(roles.begin(), roles.end(), [](const auto &a, const auto &b) -> bool {
return a.Position < b.Position;
});
}
@@ -281,7 +273,7 @@ std::vector<ChannelData> DiscordClient::GetActiveThreads(Snowflake channel_id) c
return m_store.GetActiveThreads(channel_id);
}
void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback) {
void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, const sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> &callback) {
m_http.MakeGET("/channels/" + std::to_string(channel_id) + "/threads/archived/public", [this, callback](const http::response_type &r) {
if (CheckCode(r)) {
const auto data = nlohmann::json::parse(r.text).get<ArchivedThreadsResponseData>();
@@ -294,7 +286,7 @@ void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, sigc::slot<vo
});
}
void DiscordClient::GetArchivedPrivateThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback) {
void DiscordClient::GetArchivedPrivateThreads(Snowflake channel_id, const sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> &callback) {
m_http.MakeGET("/channels/" + std::to_string(channel_id) + "/users/@me/threads/archived/private", [this, callback](const http::response_type &r) {
if (CheckCode(r)) {
const auto data = nlohmann::json::parse(r.text).get<ArchivedThreadsResponseData>();
@@ -411,7 +403,7 @@ bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowfla
return actor_highest->Position > target_highest->Position;
}
void DiscordClient::ChatMessageCallback(std::string nonce, const http::response_type &response) {
void DiscordClient::ChatMessageCallback(const std::string &nonce, const http::response_type &response) {
if (!CheckCode(response)) {
if (response.status_code == http::TooManyRequests) {
try { // not sure if this body is guaranteed
@@ -481,7 +473,7 @@ void DiscordClient::DeleteMessage(Snowflake channel_id, Snowflake id) {
void DiscordClient::EditMessage(Snowflake channel_id, Snowflake id, std::string content) {
std::string path = "/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id);
MessageEditObject obj;
obj.Content = content;
obj.Content = std::move(content);
nlohmann::json j = obj;
m_http.MakePATCH(path, j.dump(), [](auto) {});
}
@@ -516,7 +508,7 @@ void DiscordClient::SendThreadLazyLoad(Snowflake id) {
m_websocket.Send(msg);
}
void DiscordClient::JoinGuild(std::string code) {
void DiscordClient::JoinGuild(const std::string &code) {
m_http.MakePOST("/invites/" + code, "{}", [](auto) {});
}
@@ -554,14 +546,10 @@ void DiscordClient::UpdateStatus(PresenceStatus status, bool is_afk, const Activ
m_signal_presence_update.emit(GetUserData(), status);
}
void DiscordClient::CreateDM(Snowflake user_id) {
CreateDM(user_id, [](...) {});
}
void DiscordClient::CreateDM(Snowflake user_id, sigc::slot<void(DiscordError code, Snowflake channel_id)> callback) {
void DiscordClient::CreateDM(Snowflake user_id, const sigc::slot<void(DiscordError code, Snowflake channel_id)> &callback) {
CreateDMObject obj;
obj.Recipients.push_back(user_id);
m_http.MakePOST("/users/@me/channels", nlohmann::json(obj).dump(), [this, callback](const http::response &response) {
m_http.MakePOST("/users/@me/channels", nlohmann::json(obj).dump(), [callback](const http::response &response) {
if (!CheckCode(response)) {
callback(DiscordError::NONE, Snowflake::Invalid);
return;
@@ -572,7 +560,7 @@ void DiscordClient::CreateDM(Snowflake user_id, sigc::slot<void(DiscordError cod
}
void DiscordClient::CloseDM(Snowflake channel_id) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id), [this](const http::response &response) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id), [](const http::response &response) {
CheckCode(response);
});
}
@@ -582,7 +570,7 @@ std::optional<Snowflake> DiscordClient::FindDM(Snowflake user_id) {
for (const auto &id : channels) {
const auto channel = m_store.GetChannel(id);
const auto recipients = channel->GetDMRecipients();
if (recipients.size() == 1 && recipients[0].ID == user_id)
if (channel->Type == ChannelType::DM && recipients.size() == 1 && recipients[0].ID == user_id)
return id;
}
@@ -617,14 +605,10 @@ void DiscordClient::RemoveReaction(Snowflake id, Glib::ustring param) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", [](auto) {});
}
void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name) {
SetGuildName(id, name, [](auto) {});
}
void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildObject obj;
obj.Name = name;
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &r) {
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [callback](const http::response_type &r) {
if (CheckCode(r))
callback(DiscordError::NONE);
else
@@ -632,14 +616,10 @@ void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::
});
}
void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data) {
SetGuildIcon(id, data, [](auto) {});
}
void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildObject obj;
obj.IconData = data;
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &r) {
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [callback](const http::response_type &r) {
if (CheckCode(r))
callback(DiscordError::NONE);
else
@@ -647,12 +627,8 @@ void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::sl
});
}
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id) {
UnbanUser(guild_id, user_id, [](const auto) {});
}
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const http::response_type &response) {
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -660,12 +636,8 @@ void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<
});
}
void DiscordClient::DeleteInvite(const std::string &code) {
DeleteInvite(code, [](const auto) {});
}
void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(DiscordError code)> callback) {
m_http.MakeDELETE("/invites/" + code, [this, callback](const http::response_type &response) {
void DiscordClient::DeleteInvite(const std::string &code, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakeDELETE("/invites/" + code, [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -674,21 +646,21 @@ void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(Discor
}
void DiscordClient::AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id) {
m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), "", [this](const http::response_type &response) {
m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), "", [](const http::response_type &response) {
CheckCode(response);
});
}
void DiscordClient::RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), [this](const http::response_type &response) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), [](const http::response_type &response) {
CheckCode(response);
});
}
void DiscordClient::ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildRoleObject obj;
obj.Permissions = permissions;
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -696,10 +668,10 @@ void DiscordClient::ModifyRolePermissions(Snowflake guild_id, Snowflake role_id,
});
}
void DiscordClient::ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildRoleObject obj;
obj.Name = name;
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -707,10 +679,10 @@ void DiscordClient::ModifyRoleName(Snowflake guild_id, Snowflake role_id, const
});
}
void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildRoleObject obj;
obj.Color = color;
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -718,7 +690,7 @@ void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint3
});
}
void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::RGBA color, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, const Gdk::RGBA &color, const sigc::slot<void(DiscordError code)> &callback) {
uint32_t int_color = 0;
int_color |= static_cast<uint32_t>(color.get_blue() * 255.0) << 0;
int_color |= static_cast<uint32_t>(color.get_green() * 255.0) << 8;
@@ -726,7 +698,7 @@ void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::
ModifyRoleColor(guild_id, role_id, int_color, callback);
}
void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, const sigc::slot<void(DiscordError code)> &callback) {
const auto guild = GetGuild(guild_id);
if (!guild.has_value() || !guild->Roles.has_value()) return;
const auto &roles = *guild->Roles;
@@ -763,7 +735,7 @@ void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, in
for (size_t i = range_from; i < range_to; i++)
obj.Positions.push_back({ roles[i].ID, roles[i].Position + dir });
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles", nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles", nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -771,11 +743,11 @@ void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, in
});
}
void DiscordClient::ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildEmojiObject obj;
obj.Name = name;
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -783,8 +755,8 @@ void DiscordClient::ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, cons
});
}
void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), [this, callback](const http::response_type &response) {
void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -792,14 +764,8 @@ void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::sl
});
}
std::optional<GuildApplicationData> DiscordClient::GetGuildApplication(Snowflake guild_id) const {
const auto it = m_guild_join_requests.find(guild_id);
if (it == m_guild_join_requests.end()) return std::nullopt;
return it->second;
}
void DiscordClient::RemoveRelationship(Snowflake id, sigc::slot<void(DiscordError Code)> callback) {
m_http.MakeDELETE("/users/@me/relationships/" + std::to_string(id), [this, callback](const http::response_type &response) {
void DiscordClient::RemoveRelationship(Snowflake id, const sigc::slot<void(DiscordError Code)> &callback) {
m_http.MakeDELETE("/users/@me/relationships/" + std::to_string(id), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -807,11 +773,11 @@ void DiscordClient::RemoveRelationship(Snowflake id, sigc::slot<void(DiscordErro
});
}
void DiscordClient::SendFriendRequest(const Glib::ustring &username, int discriminator, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::SendFriendRequest(const Glib::ustring &username, int discriminator, const sigc::slot<void(DiscordError code)> &callback) {
FriendRequestObject obj;
obj.Username = username;
obj.Discriminator = discriminator;
m_http.MakePOST("/users/@me/relationships", nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePOST("/users/@me/relationships", nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -819,8 +785,8 @@ void DiscordClient::SendFriendRequest(const Glib::ustring &username, int discrim
});
}
void DiscordClient::PutRelationship(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePUT("/users/@me/relationships/" + std::to_string(id), "{}", [this, callback](const http::response_type &response) {
void DiscordClient::PutRelationship(Snowflake id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePUT("/users/@me/relationships/" + std::to_string(id), "{}", [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -828,8 +794,8 @@ void DiscordClient::PutRelationship(Snowflake id, sigc::slot<void(DiscordError c
});
}
void DiscordClient::Pin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), "", [this, callback](const http::response_type &response) {
void DiscordClient::Pin(Snowflake channel_id, Snowflake message_id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), "", [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -837,8 +803,8 @@ void DiscordClient::Pin(Snowflake channel_id, Snowflake message_id, sigc::slot<v
});
}
void DiscordClient::Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), [this, callback](const http::response_type &response) {
void DiscordClient::Unpin(Snowflake channel_id, Snowflake message_id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -848,8 +814,8 @@ void DiscordClient::Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot
// i dont know if the location parameter is necessary at all but discord's thread implementation is extremely strange
// so its here just in case
void DiscordClient::LeaveThread(Snowflake channel_id, const std::string &location, sigc::slot<void(DiscordError code)> callback) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/thread-members/@me?location=" + location, [this, callback](const http::response_type &response) {
void DiscordClient::LeaveThread(Snowflake channel_id, const std::string &location, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/thread-members/@me?location=" + location, [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -857,11 +823,11 @@ void DiscordClient::LeaveThread(Snowflake channel_id, const std::string &locatio
});
}
void DiscordClient::ArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::ArchiveThread(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback) {
ModifyChannelObject obj;
obj.Archived = true;
obj.Locked = true;
m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -869,11 +835,11 @@ void DiscordClient::ArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordE
});
}
void DiscordClient::UnArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::UnArchiveThread(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback) {
ModifyChannelObject obj;
obj.Archived = false;
obj.Locked = false;
m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -881,11 +847,11 @@ void DiscordClient::UnArchiveThread(Snowflake channel_id, sigc::slot<void(Discor
});
}
void DiscordClient::MarkChannelAsRead(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::MarkChannelAsRead(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback) {
if (m_unread.find(channel_id) == m_unread.end()) return;
const auto iter = m_last_message_id.find(channel_id);
if (iter == m_last_message_id.end()) return;
m_http.MakePOST("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(iter->second) + "/ack", "{\"token\":null}", [this, callback](const http::response_type &response) {
m_http.MakePOST("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(iter->second) + "/ack", "{\"token\":null}", [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -893,7 +859,7 @@ void DiscordClient::MarkChannelAsRead(Snowflake channel_id, sigc::slot<void(Disc
});
}
void DiscordClient::MarkGuildAsRead(Snowflake guild_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::MarkGuildAsRead(Snowflake guild_id, const sigc::slot<void(DiscordError code)> &callback) {
AckBulkData data;
const auto channels = GetChannelsInGuild(guild_id);
for (const auto &[unread, mention_count] : m_unread) {
@@ -908,7 +874,7 @@ void DiscordClient::MarkGuildAsRead(Snowflake guild_id, sigc::slot<void(DiscordE
if (data.ReadStates.empty()) return;
m_http.MakePOST("/read-states/ack-bulk", nlohmann::json(data).dump(), [this, callback](const http::response_type &response) {
m_http.MakePOST("/read-states/ack-bulk", nlohmann::json(data).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -916,14 +882,14 @@ void DiscordClient::MarkGuildAsRead(Snowflake guild_id, sigc::slot<void(DiscordE
});
}
void DiscordClient::MuteChannel(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::MuteChannel(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback) {
const auto channel = GetChannel(channel_id);
if (!channel.has_value()) return;
const auto guild_id_path = channel->GuildID.has_value() ? std::to_string(*channel->GuildID) : "@me"s;
nlohmann::json j;
j["channel_overrides"][std::to_string(channel_id)]["mute_config"] = MuteConfigData { std::nullopt, -1 };
j["channel_overrides"][std::to_string(channel_id)]["muted"] = true;
m_http.MakePATCH("/users/@me/guilds/" + guild_id_path + "/settings", j.dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/users/@me/guilds/" + guild_id_path + "/settings", j.dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -931,13 +897,13 @@ void DiscordClient::MuteChannel(Snowflake channel_id, sigc::slot<void(DiscordErr
});
}
void DiscordClient::UnmuteChannel(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::UnmuteChannel(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback) {
const auto channel = GetChannel(channel_id);
if (!channel.has_value()) return;
const auto guild_id_path = channel->GuildID.has_value() ? std::to_string(*channel->GuildID) : "@me"s;
nlohmann::json j;
j["channel_overrides"][std::to_string(channel_id)]["muted"] = false;
m_http.MakePATCH("/users/@me/guilds/" + guild_id_path + "/settings", j.dump(), [this, callback](const http::response_type &response) {
m_http.MakePATCH("/users/@me/guilds/" + guild_id_path + "/settings", j.dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -945,19 +911,8 @@ void DiscordClient::UnmuteChannel(Snowflake channel_id, sigc::slot<void(DiscordE
});
}
void DiscordClient::MarkAllAsRead(sigc::slot<void(DiscordError code)> callback) {
AckBulkData data;
for (const auto &[unread, mention_count] : m_unread) {
const auto iter = m_last_message_id.find(unread);
if (iter == m_last_message_id.end()) continue;
auto &e = data.ReadStates.emplace_back();
e.ID = unread;
e.LastMessageID = iter->second;
}
if (data.ReadStates.empty()) return;
m_http.MakePOST("/read-states/ack-bulk", nlohmann::json(data).dump(), [this, callback](const http::response_type &response) {
void DiscordClient::MuteGuild(Snowflake id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePATCH("/users/@me/guilds/" + std::to_string(id) + "/settings", R"({"muted":true})", [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -965,8 +920,8 @@ void DiscordClient::MarkAllAsRead(sigc::slot<void(DiscordError code)> callback)
});
}
void DiscordClient::MuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePATCH("/users/@me/guilds/" + std::to_string(id) + "/settings", R"({"muted":true})", [this, callback](const http::response_type &response) {
void DiscordClient::UnmuteGuild(Snowflake id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePATCH("/users/@me/guilds/" + std::to_string(id) + "/settings", R"({"muted":false})", [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -974,8 +929,8 @@ void DiscordClient::MuteGuild(Snowflake id, sigc::slot<void(DiscordError code)>
});
}
void DiscordClient::UnmuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePATCH("/users/@me/guilds/" + std::to_string(id) + "/settings", R"({"muted":false})", [this, callback](const http::response_type &response) {
void DiscordClient::MuteThread(Snowflake id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":true})", [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -983,8 +938,8 @@ void DiscordClient::UnmuteGuild(Snowflake id, sigc::slot<void(DiscordError code)
});
}
void DiscordClient::MuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":true})", [this, callback](const http::response_type &response) {
void DiscordClient::UnmuteThread(Snowflake id, const sigc::slot<void(DiscordError code)> &callback) {
m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":false})", [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -992,16 +947,7 @@ void DiscordClient::MuteThread(Snowflake id, sigc::slot<void(DiscordError code)>
});
}
void DiscordClient::UnmuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback) {
m_http.MakePATCH("/channels/" + std::to_string(id) + "/thread-members/@me/settings", R"({"muted":false})", [this, callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
callback(GetCodeFromResponse(response));
});
}
void DiscordClient::FetchPinned(Snowflake id, sigc::slot<void(std::vector<Message>, DiscordError code)> callback) {
void DiscordClient::FetchPinned(Snowflake id, const sigc::slot<void(std::vector<Message>, DiscordError code)> &callback) {
// return from db if we know the pins have already been requested
if (m_channels_pinned_requested.find(id) != m_channels_pinned_requested.end()) {
callback(m_store.GetPinnedMessages(id), DiscordError::NONE);
@@ -1019,7 +965,7 @@ void DiscordClient::FetchPinned(Snowflake id, sigc::slot<void(std::vector<Messag
std::sort(data.begin(), data.end(), [](const Message &a, const Message &b) { return a.ID < b.ID; });
for (auto &msg : data)
StoreMessageData(msg);
callback(std::move(data), DiscordError::NONE);
callback(data, DiscordError::NONE);
});
}
@@ -1040,7 +986,7 @@ std::vector<BanData> DiscordClient::GetBansInGuild(Snowflake guild_id) {
return m_store.GetBans(guild_id);
}
void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback) {
void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(BanData)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const http::response_type &response) {
if (!CheckCode(response)) return;
auto ban = nlohmann::json::parse(response.text).get<BanData>();
@@ -1050,7 +996,7 @@ void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::s
});
}
void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback) {
void DiscordClient::FetchGuildBans(Snowflake guild_id, const sigc::slot<void(std::vector<BanData>)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const http::response_type &response) {
if (!CheckCode(response)) return;
auto bans = nlohmann::json::parse(response.text).get<std::vector<BanData>>();
@@ -1064,8 +1010,8 @@ void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vect
});
}
void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const http::response_type &response) {
void DiscordClient::FetchGuildInvites(Snowflake guild_id, const sigc::slot<void(std::vector<InviteData>)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback](const http::response_type &response) {
// store?
if (!CheckCode(response)) return;
auto invites = nlohmann::json::parse(response.text).get<std::vector<InviteData>>();
@@ -1080,7 +1026,7 @@ void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::v
});
}
void DiscordClient::FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogData)> callback) {
void DiscordClient::FetchAuditLog(Snowflake guild_id, const sigc::slot<void(AuditLogData)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/audit-logs", [this, callback](const http::response &response) {
if (!CheckCode(response)) return;
auto data = nlohmann::json::parse(response.text).get<AuditLogData>();
@@ -1094,7 +1040,7 @@ void DiscordClient::FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogDa
});
}
void DiscordClient::FetchGuildEmojis(Snowflake guild_id, sigc::slot<void(std::vector<EmojiData>)> callback) {
void DiscordClient::FetchGuildEmojis(Snowflake guild_id, const sigc::slot<void(std::vector<EmojiData>)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/emojis", [this, callback](const http::response_type &response) {
if (!CheckCode(response)) return;
auto emojis = nlohmann::json::parse(response.text).get<std::vector<EmojiData>>();
@@ -1102,19 +1048,19 @@ void DiscordClient::FetchGuildEmojis(Snowflake guild_id, sigc::slot<void(std::ve
for (const auto &emoji : emojis)
m_store.SetEmoji(emoji.ID, emoji);
m_store.EndTransaction();
callback(std::move(emojis));
callback(emojis);
});
}
void DiscordClient::FetchUserProfile(Snowflake user_id, sigc::slot<void(UserProfileData)> callback) {
m_http.MakeGET("/users/" + std::to_string(user_id) + "/profile", [this, callback](const http::response_type &response) {
void DiscordClient::FetchUserProfile(Snowflake user_id, const sigc::slot<void(UserProfileData)> &callback) {
m_http.MakeGET("/users/" + std::to_string(user_id) + "/profile", [callback](const http::response_type &response) {
if (!CheckCode(response)) return;
callback(nlohmann::json::parse(response.text).get<UserProfileData>());
});
}
void DiscordClient::FetchUserNote(Snowflake user_id, sigc::slot<void(std::string note)> callback) {
m_http.MakeGET("/users/@me/notes/" + std::to_string(user_id), [this, callback](const http::response_type &response) {
void DiscordClient::FetchUserNote(Snowflake user_id, const sigc::slot<void(std::string note)> &callback) {
m_http.MakeGET("/users/@me/notes/" + std::to_string(user_id), [callback](const http::response_type &response) {
if (response.status_code == 404) return;
if (!CheckCode(response)) return;
const auto note = nlohmann::json::parse(response.text).get<UserNoteObject>().Note;
@@ -1123,14 +1069,10 @@ void DiscordClient::FetchUserNote(Snowflake user_id, sigc::slot<void(std::string
});
}
void DiscordClient::SetUserNote(Snowflake user_id, std::string note) {
SetUserNote(user_id, note, [](auto) {});
}
void DiscordClient::SetUserNote(Snowflake user_id, std::string note, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::SetUserNote(Snowflake user_id, std::string note, const sigc::slot<void(DiscordError code)> &callback) {
UserSetNoteObject obj;
obj.Note = note;
m_http.MakePUT("/users/@me/notes/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
obj.Note = std::move(note);
m_http.MakePUT("/users/@me/notes/" + std::to_string(user_id), nlohmann::json(obj).dump(), [callback](const http::response_type &response) {
if (CheckCode(response, 204))
callback(DiscordError::NONE);
else
@@ -1138,7 +1080,7 @@ void DiscordClient::SetUserNote(Snowflake user_id, std::string note, sigc::slot<
});
}
void DiscordClient::FetchUserRelationships(Snowflake user_id, sigc::slot<void(std::vector<UserData>)> callback) {
void DiscordClient::FetchUserRelationships(Snowflake user_id, const sigc::slot<void(std::vector<UserData>)> &callback) {
m_http.MakeGET("/users/" + std::to_string(user_id) + "/relationships", [this, callback](const http::response_type &response) {
if (!CheckCode(response)) return;
RelationshipsData data = nlohmann::json::parse(response.text);
@@ -1148,26 +1090,26 @@ void DiscordClient::FetchUserRelationships(Snowflake user_id, sigc::slot<void(st
});
}
bool DiscordClient::IsVerificationRequired(Snowflake guild_id) {
bool DiscordClient::IsVerificationRequired(Snowflake guild_id) const {
const auto member = GetMember(GetUserData().ID, guild_id);
if (member.has_value() && member->IsPending.has_value())
return *member->IsPending;
return false;
}
void DiscordClient::GetVerificationGateInfo(Snowflake guild_id, sigc::slot<void(std::optional<VerificationGateInfoObject>)> callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/member-verification", [this, callback](const http::response_type &response) {
void DiscordClient::GetVerificationGateInfo(Snowflake guild_id, const sigc::slot<void(std::optional<VerificationGateInfoObject>)> &callback) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/member-verification", [callback](const http::response_type &response) {
if (!CheckCode(response)) return;
if (response.status_code == 204) callback(std::nullopt);
callback(nlohmann::json::parse(response.text).get<VerificationGateInfoObject>());
});
}
void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot<void(DiscordError code)> callback) {
void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, const sigc::slot<void(DiscordError code)> &callback) {
if (info.VerificationFields.has_value())
for (auto &field : *info.VerificationFields)
field.Response = true;
m_http.MakePUT("/guilds/" + std::to_string(guild_id) + "/requests/@me", nlohmann::json(info).dump(), [this, callback](const http::response_type &response) {
m_http.MakePUT("/guilds/" + std::to_string(guild_id) + "/requests/@me", nlohmann::json(info).dump(), [callback](const http::response_type &response) {
if (CheckCode(response))
callback(DiscordError::NONE);
else
@@ -1175,14 +1117,14 @@ void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateI
});
}
void DiscordClient::UpdateToken(std::string token) {
void DiscordClient::UpdateToken(const std::string &token) {
if (!IsStarted()) {
m_token = token;
m_http.SetAuth(token);
}
}
void DiscordClient::SetUserAgent(std::string agent) {
void DiscordClient::SetUserAgent(const std::string &agent) {
m_http.SetUserAgent(agent);
m_websocket.SetUserAgent(agent);
}
@@ -1328,7 +1270,7 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayOp::InvalidSession: {
HandleGatewayInvalidSession(m);
} break;
case GatewayOp::Event: {
case GatewayOp::Dispatch: {
auto iter = m_event_map.find(m.Type);
if (iter == m_event_map.end()) {
printf("Unknown event %s\n", m.Type.c_str());
@@ -1464,6 +1406,9 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
case GatewayEvent::USER_GUILD_SETTINGS_UPDATE: {
HandleGatewayUserGuildSettingsUpdate(m);
} break;
case GatewayEvent::GUILD_MEMBERS_CHUNK: {
HandleGatewayGuildMembersChunk(m);
} break;
}
} break;
default:
@@ -1480,7 +1425,7 @@ void DiscordClient::HandleGatewayHello(const GatewayMessage &msg) {
HelloMessageData d = msg.Data;
m_heartbeat_msec = d.HeartbeatInterval;
m_heartbeat_waiter.revive();
m_heartbeat_thread = std::thread(std::bind(&DiscordClient::HeartbeatThread, this));
m_heartbeat_thread = std::thread([this] { HeartbeatThread(); });
m_signal_connected.emit(); // socket is connected before this but emitting here should b fine
m_reconnecting = false; // maybe should go elsewhere?
if (m_wants_resume) {
@@ -1733,7 +1678,7 @@ void DiscordClient::HandleGatewayGuildRoleCreate(const GatewayMessage &msg) {
void DiscordClient::HandleGatewayGuildRoleDelete(const GatewayMessage &msg) {
GuildRoleDeleteObject data = msg.Data;
auto guild = *m_store.GetGuild(data.GuildID);
const auto pred = [this, id = data.RoleID](const RoleData &role) -> bool {
const auto pred = [id = data.RoleID](const RoleData &role) -> bool {
return role.ID == id;
};
guild.Roles->erase(std::remove_if(guild.Roles->begin(), guild.Roles->end(), pred), guild.Roles->end());
@@ -1823,12 +1768,12 @@ void DiscordClient::HandleGatewayInviteCreate(const GatewayMessage &msg) {
invite.CreatedAt = std::move(data.CreatedAt);
invite.Channel = *m_store.GetChannel(data.ChannelID);
invite.Inviter = std::move(data.Inviter);
invite.IsTemporary = std::move(data.IsTemporary);
invite.MaxAge = std::move(data.MaxAge);
invite.MaxUses = std::move(data.MaxUses);
invite.IsTemporary = data.IsTemporary;
invite.MaxAge = data.MaxAge;
invite.MaxUses = data.MaxUses;
invite.TargetUser = std::move(data.TargetUser);
invite.TargetUserType = std::move(data.TargetUserType);
invite.Uses = std::move(data.Uses);
invite.TargetUserType = data.TargetUserType;
invite.Uses = data.Uses;
if (data.GuildID.has_value())
invite.Guild = m_store.GetGuild(*data.GuildID);
m_signal_invite_create.emit(invite);
@@ -1890,7 +1835,7 @@ void DiscordClient::HandleGatewayRelationshipAdd(const GatewayMessage &msg) {
RelationshipAddData data = msg.Data;
m_store.SetUser(data.ID, data.User);
m_user_relationships[data.ID] = data.Type;
m_signal_relationship_add.emit(std::move(data));
m_signal_relationship_add.emit(data);
}
// remarkably this doesnt actually mean a thread was created
@@ -2049,6 +1994,14 @@ void DiscordClient::HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &m
}
}
void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) {
GuildMembersChunkData data = msg.Data;
m_store.BeginTransaction();
for (const auto &member : data.Members)
m_store.SetGuildMember(data.GuildID, member.User->ID, member);
m_store.EndTransaction();
}
void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) {
ReadySupplementalData data = msg.Data;
for (const auto &p : data.MergedPresences.Friends) {
@@ -2157,7 +2110,7 @@ void DiscordClient::HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg
has_sync = true;
for (const auto &item : *op.Items) {
if (item->Type == "member") {
auto member = static_cast<const GuildMemberListUpdateMessage::MemberItem *>(item.get());
auto member = dynamic_cast<const GuildMemberListUpdateMessage::MemberItem *>(item.get());
m_store.SetUser(member->User.ID, member->User);
AddUserToGuild(member->User.ID, data.GuildID);
m_store.SetGuildMember(data.GuildID, member->User.ID, member->GetAsMemberData());
@@ -2176,7 +2129,7 @@ void DiscordClient::HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg
}
} else if (op.Op == "UPDATE") {
if (op.OpItem.has_value() && op.OpItem.value()->Type == "member") {
const auto &m = static_cast<const GuildMemberListUpdateMessage::MemberItem *>(op.OpItem.value().get())->GetAsMemberData();
const auto &m = dynamic_cast<const GuildMemberListUpdateMessage::MemberItem *>(op.OpItem.value().get())->GetAsMemberData();
m_store.SetGuildMember(data.GuildID, m.User->ID, m);
m_signal_guild_member_update.emit(data.GuildID, m.User->ID); // cheeky
}
@@ -2461,12 +2414,6 @@ void DiscordClient::HandleReadyGuildSettings(const ReadyEventData &data) {
}
}
void DiscordClient::HandleUserGuildSettingsUpdateForDMs(const UserGuildSettingsUpdateData &data) {
const auto channels = GetPrivateChannels();
std::set<Snowflake> now_muted_channels;
const auto now = Snowflake::FromNow();
}
void DiscordClient::LoadEventMap() {
m_event_map["READY"] = GatewayEvent::READY;
m_event_map["MESSAGE_CREATE"] = GatewayEvent::MESSAGE_CREATE;
@@ -2511,6 +2458,7 @@ void DiscordClient::LoadEventMap() {
m_event_map["THREAD_MEMBER_LIST_UPDATE"] = GatewayEvent::THREAD_MEMBER_LIST_UPDATE;
m_event_map["MESSAGE_ACK"] = GatewayEvent::MESSAGE_ACK;
m_event_map["USER_GUILD_SETTINGS_UPDATE"] = GatewayEvent::USER_GUILD_SETTINGS_UPDATE;
m_event_map["GUILD_MEMBERS_CHUNK"] = GatewayEvent::GUILD_MEMBERS_CHUNK;
}
DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() {
@@ -2677,6 +2625,10 @@ DiscordClient::type_signal_message_ack DiscordClient::signal_message_ack() {
return m_signal_message_ack;
}
DiscordClient::type_signal_guild_members_chunk DiscordClient::signal_guild_members_chunk() {
return m_signal_guild_members_chunk;
}
DiscordClient::type_signal_added_to_thread DiscordClient::signal_added_to_thread() {
return m_signal_added_to_thread;
}

View File

@@ -55,7 +55,6 @@ public:
std::unordered_set<Snowflake> GetGuilds() const;
const UserData &GetUserData() const;
const UserSettings &GetUserSettings() const;
std::vector<Snowflake> GetUserSortedGuilds() const;
std::vector<Message> GetMessagesForChannel(Snowflake id, size_t limit = 50) const;
std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const;
@@ -63,8 +62,8 @@ public:
EPremiumType GetSelfPremiumType() const;
void FetchMessagesInChannel(Snowflake id, sigc::slot<void(const std::vector<Message> &)> cb);
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, sigc::slot<void(const std::vector<Message> &)> cb);
void FetchMessagesInChannel(Snowflake id, const sigc::slot<void(const std::vector<Message> &)> &cb);
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, const sigc::slot<void(const std::vector<Message> &)> &cb);
std::optional<Message> GetMessage(Snowflake id) const;
std::optional<ChannelData> GetChannel(Snowflake id) const;
std::optional<EmojiData> GetEmoji(Snowflake id) const;
@@ -73,17 +72,27 @@ public:
std::optional<RoleData> GetRole(Snowflake id) const;
std::optional<GuildData> GetGuild(Snowflake id) const;
std::optional<GuildMember> GetMember(Snowflake user_id, Snowflake guild_id) const;
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
std::optional<RoleData> GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
std::set<Snowflake> GetUsersInGuild(Snowflake id) const;
std::set<Snowflake> GetChannelsInGuild(Snowflake id) const;
std::vector<Snowflake> GetUsersInThread(Snowflake id) const;
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const;
void GetArchivedPublicThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback);
void GetArchivedPrivateThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback);
void GetArchivedPublicThreads(Snowflake channel_id, const sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> &callback);
void GetArchivedPrivateThreads(Snowflake channel_id, const sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> &callback);
std::vector<Snowflake> GetChildChannelIDs(Snowflake parent_id) const;
// get ids of given list of members for who we do not have the member data
template<typename Iter>
std::unordered_set<Snowflake> FilterUnknownMembersFrom(Snowflake guild_id, Iter begin, Iter end) {
std::unordered_set<Snowflake> ret;
const auto known = m_store.GetMembersInGuild(guild_id);
for (auto iter = begin; iter != end; iter++)
if (known.find(*iter) == known.end())
ret.insert(*iter);
return ret;
}
bool IsThreadJoined(Snowflake thread_id) const;
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;
@@ -93,7 +102,7 @@ public:
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
void ChatMessageCallback(std::string nonce, const http::response_type &response);
void ChatMessageCallback(const std::string &nonce, const http::response_type &response);
void SendChatMessage(const std::string &content, Snowflake channel);
void SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message);
@@ -101,60 +110,65 @@ public:
void EditMessage(Snowflake channel_id, Snowflake id, std::string content);
void SendLazyLoad(Snowflake id);
void SendThreadLazyLoad(Snowflake id);
void JoinGuild(std::string code);
void JoinGuild(const std::string &code);
void LeaveGuild(Snowflake id);
void KickUser(Snowflake user_id, Snowflake guild_id);
void BanUser(Snowflake user_id, Snowflake guild_id); // todo: reason, delete messages
void UpdateStatus(PresenceStatus status, bool is_afk);
void UpdateStatus(PresenceStatus status, bool is_afk, const ActivityData &obj);
void CreateDM(Snowflake user_id);
void CreateDM(Snowflake user_id, sigc::slot<void(DiscordError code, Snowflake channel_id)> callback);
void CreateDM(Snowflake user_id, const sigc::slot<void(DiscordError code, Snowflake channel_id)> &callback);
void CloseDM(Snowflake channel_id);
std::optional<Snowflake> FindDM(Snowflake user_id); // wont find group dms
void AddReaction(Snowflake id, Glib::ustring param);
void RemoveReaction(Snowflake id, Glib::ustring param);
void SetGuildName(Snowflake id, const Glib::ustring &name);
void SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
void SetGuildIcon(Snowflake id, const std::string &data);
void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(DiscordError code)> callback);
void UnbanUser(Snowflake guild_id, Snowflake user_id);
void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(DiscordError code)> callback);
void DeleteInvite(const std::string &code);
void DeleteInvite(const std::string &code, sigc::slot<void(DiscordError code)> callback);
void SetGuildName(Snowflake id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback);
void SetGuildIcon(Snowflake id, const std::string &data, const sigc::slot<void(DiscordError code)> &callback);
void UnbanUser(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(DiscordError code)> &callback);
void DeleteInvite(const std::string &code, const sigc::slot<void(DiscordError code)> &callback);
void AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
void RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
void ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, sigc::slot<void(DiscordError code)> callback);
void ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, sigc::slot<void(DiscordError code)> callback);
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::RGBA color, sigc::slot<void(DiscordError code)> callback);
void ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot<void(DiscordError code)> callback);
void ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
void DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot<void(DiscordError code)> callback);
std::optional<GuildApplicationData> GetGuildApplication(Snowflake guild_id) const;
void RemoveRelationship(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void SendFriendRequest(const Glib::ustring &username, int discriminator, sigc::slot<void(DiscordError code)> callback);
void PutRelationship(Snowflake id, sigc::slot<void(DiscordError code)> callback); // send fr by id, accept incoming
void Pin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback);
void Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback);
void LeaveThread(Snowflake channel_id, const std::string &location, sigc::slot<void(DiscordError code)> callback);
void ArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
void UnArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
void MarkChannelAsRead(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
void MarkGuildAsRead(Snowflake guild_id, sigc::slot<void(DiscordError code)> callback);
void MuteChannel(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
void UnmuteChannel(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
void MarkAllAsRead(sigc::slot<void(DiscordError code)> callback);
void MuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void UnmuteGuild(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void MuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void UnmuteThread(Snowflake id, sigc::slot<void(DiscordError code)> callback);
void ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, const sigc::slot<void(DiscordError code)> &callback);
void ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback);
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, const sigc::slot<void(DiscordError code)> &callback);
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, const Gdk::RGBA &color, const sigc::slot<void(DiscordError code)> &callback);
void ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, const sigc::slot<void(DiscordError code)> &callback);
void ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, const sigc::slot<void(DiscordError code)> &callback);
void DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, const sigc::slot<void(DiscordError code)> &callback);
void RemoveRelationship(Snowflake id, const sigc::slot<void(DiscordError code)> &callback);
void SendFriendRequest(const Glib::ustring &username, int discriminator, const sigc::slot<void(DiscordError code)> &callback);
void PutRelationship(Snowflake id, const sigc::slot<void(DiscordError code)> &callback); // send fr by id, accept incoming
void Pin(Snowflake channel_id, Snowflake message_id, const sigc::slot<void(DiscordError code)> &callback);
void Unpin(Snowflake channel_id, Snowflake message_id, const sigc::slot<void(DiscordError code)> &callback);
void LeaveThread(Snowflake channel_id, const std::string &location, const sigc::slot<void(DiscordError code)> &callback);
void ArchiveThread(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback);
void UnArchiveThread(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback);
void MarkChannelAsRead(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback);
void MarkGuildAsRead(Snowflake guild_id, const sigc::slot<void(DiscordError code)> &callback);
void MuteChannel(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback);
void UnmuteChannel(Snowflake channel_id, const sigc::slot<void(DiscordError code)> &callback);
void MuteGuild(Snowflake id, const sigc::slot<void(DiscordError code)> &callback);
void UnmuteGuild(Snowflake id, const sigc::slot<void(DiscordError code)> &callback);
void MuteThread(Snowflake id, const sigc::slot<void(DiscordError code)> &callback);
void UnmuteThread(Snowflake id, const sigc::slot<void(DiscordError code)> &callback);
bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const;
bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const;
// send op 8 to get member data for unknown members
template<typename Iter>
void RequestMembers(Snowflake guild_id, Iter begin, Iter end) {
if (std::distance(begin, end) == 0) return;
RequestGuildMembersMessage obj;
obj.GuildID = guild_id;
obj.Presences = false;
obj.UserIDs = { begin, end };
m_websocket.Send(obj);
}
// real client doesn't seem to use the single role endpoints so neither do we
template<typename Iter>
auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot<void(DiscordError code)> callback) {
auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, const sigc::slot<void(DiscordError code)> &callback) {
ModifyGuildMemberObject obj;
obj.Roles = { begin, end };
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/members/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
@@ -167,30 +181,29 @@ public:
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
std::vector<BanData> GetBansInGuild(Snowflake guild_id);
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback);
void FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback);
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, const sigc::slot<void(BanData)> &callback);
void FetchGuildBans(Snowflake guild_id, const sigc::slot<void(std::vector<BanData>)> &callback);
void FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback);
void FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback);
void FetchInvite(const std::string &code, const sigc::slot<void(std::optional<InviteData>)> &callback);
void FetchGuildInvites(Snowflake guild_id, const sigc::slot<void(std::vector<InviteData>)> &callback);
void FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogData)> callback);
void FetchAuditLog(Snowflake guild_id, const sigc::slot<void(AuditLogData)> &callback);
void FetchGuildEmojis(Snowflake guild_id, sigc::slot<void(std::vector<EmojiData>)> callback);
void FetchGuildEmojis(Snowflake guild_id, const sigc::slot<void(std::vector<EmojiData>)> &callback);
void FetchUserProfile(Snowflake user_id, sigc::slot<void(UserProfileData)> callback);
void FetchUserNote(Snowflake user_id, sigc::slot<void(std::string note)> callback);
void SetUserNote(Snowflake user_id, std::string note);
void SetUserNote(Snowflake user_id, std::string note, sigc::slot<void(DiscordError code)> callback);
void FetchUserRelationships(Snowflake user_id, sigc::slot<void(std::vector<UserData>)> callback);
void FetchUserProfile(Snowflake user_id, const sigc::slot<void(UserProfileData)> &callback);
void FetchUserNote(Snowflake user_id, const sigc::slot<void(std::string note)> &callback);
void SetUserNote(Snowflake user_id, std::string note, const sigc::slot<void(DiscordError code)> &callback);
void FetchUserRelationships(Snowflake user_id, const sigc::slot<void(std::vector<UserData>)> &callback);
void FetchPinned(Snowflake id, sigc::slot<void(std::vector<Message>, DiscordError code)> callback);
void FetchPinned(Snowflake id, const sigc::slot<void(std::vector<Message>, DiscordError code)> &callback);
bool IsVerificationRequired(Snowflake guild_id);
void GetVerificationGateInfo(Snowflake guild_id, sigc::slot<void(std::optional<VerificationGateInfoObject>)> callback);
void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot<void(DiscordError code)> callback);
bool IsVerificationRequired(Snowflake guild_id) const;
void GetVerificationGateInfo(Snowflake guild_id, const sigc::slot<void(std::optional<VerificationGateInfoObject>)> &callback);
void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, const sigc::slot<void(DiscordError code)> &callback);
void UpdateToken(std::string token);
void SetUserAgent(std::string agent);
void UpdateToken(const std::string &token);
void SetUserAgent(const std::string &agent);
bool IsChannelMuted(Snowflake id) const noexcept;
bool IsGuildMuted(Snowflake id) const noexcept;
@@ -210,8 +223,8 @@ private:
std::vector<uint8_t> m_decompress_buf;
z_stream m_zstream;
std::string GetAPIURL();
std::string GetGatewayURL();
static std::string GetAPIURL();
static std::string GetGatewayURL();
static DiscordError GetCodeFromResponse(const http::response_type &response);
@@ -262,6 +275,7 @@ private:
void HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg);
void HandleGatewayMessageAck(const GatewayMessage &msg);
void HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &msg);
void HandleGatewayGuildMembersChunk(const GatewayMessage &msg);
void HandleGatewayReadySupplemental(const GatewayMessage &msg);
void HandleGatewayReconnect(const GatewayMessage &msg);
void HandleGatewayInvalidSession(const GatewayMessage &msg);
@@ -272,16 +286,14 @@ private:
void HandleSocketOpen();
void HandleSocketClose(uint16_t code);
bool CheckCode(const http::response_type &r);
bool CheckCode(const http::response_type &r, int expected);
static bool CheckCode(const http::response_type &r);
static bool CheckCode(const http::response_type &r, int expected);
void StoreMessageData(Message &msg);
void HandleReadyReadState(const ReadyEventData &data);
void HandleReadyGuildSettings(const ReadyEventData &data);
void HandleUserGuildSettingsUpdateForDMs(const UserGuildSettingsUpdateData &data);
std::string m_token;
void AddUserToGuild(Snowflake user_id, Snowflake guild_id);
@@ -372,6 +384,7 @@ public:
typedef sigc::signal<void, ThreadUpdateData> type_signal_thread_update;
typedef sigc::signal<void, ThreadMemberListUpdateData> type_signal_thread_member_list_update;
typedef sigc::signal<void, MessageAckData> type_signal_message_ack;
typedef sigc::signal<void, GuildMembersChunkData> type_signal_guild_members_chunk;
// not discord dispatch events
typedef sigc::signal<void, Snowflake> type_signal_added_to_thread;
@@ -427,6 +440,7 @@ public:
type_signal_thread_update signal_thread_update();
type_signal_thread_member_list_update signal_thread_member_list_update();
type_signal_message_ack signal_message_ack();
type_signal_guild_members_chunk signal_guild_members_chunk();
type_signal_added_to_thread signal_added_to_thread();
type_signal_removed_from_thread signal_removed_from_thread();
@@ -479,6 +493,7 @@ protected:
type_signal_thread_update m_signal_thread_update;
type_signal_thread_member_list_update m_signal_thread_member_list_update;
type_signal_message_ack m_signal_message_ack;
type_signal_guild_members_chunk m_signal_guild_members_chunk;
type_signal_removed_from_thread m_signal_removed_from_thread;
type_signal_added_to_thread m_signal_added_to_thread;

View File

@@ -16,7 +16,7 @@ void to_json(nlohmann::json &j, const EmojiData &m) {
j["id"] = m.ID;
else
j["id"] = nullptr;
if (m.Name != "")
if (!m.Name.empty())
j["name"] = m.Name;
else
j["name"] = nullptr;

View File

@@ -77,7 +77,7 @@ void GuildData::update_from_json(const nlohmann::json &j) {
JS_RD("owner_id", OwnerID);
std::string tmp;
JS_RD("permissions", tmp);
if (tmp != "")
if (!tmp.empty())
Permissions = std::stoull(tmp);
JS_RD("region", VoiceRegion);
JS_RD("afk_channel_id", AFKChannelID);
@@ -119,7 +119,7 @@ void GuildData::update_from_json(const nlohmann::json &j) {
JS_RD("approximate_presence_count", ApproximatePresenceCount);
}
bool GuildData::HasFeature(const std::string &search_feature) {
bool GuildData::HasFeature(const std::string &search_feature) const {
if (!Features.has_value()) return false;
for (const auto &feature : *Features)
if (search_feature == feature)
@@ -128,66 +128,17 @@ bool GuildData::HasFeature(const std::string &search_feature) {
}
bool GuildData::HasIcon() const {
return Icon != "";
return !Icon.empty();
}
bool GuildData::HasAnimatedIcon() const {
return HasIcon() && Icon[0] == 'a' && Icon[1] == '_';
}
std::string GuildData::GetIconURL(std::string ext, std::string size) const {
std::string GuildData::GetIconURL(const std::string &ext, const std::string &size) const {
return "https://cdn.discordapp.com/icons/" + std::to_string(ID) + "/" + Icon + "." + ext + "?size=" + size;
}
std::vector<Snowflake> GuildData::GetSortedChannels(Snowflake ignore) const {
std::vector<Snowflake> ret;
const auto &discord = Abaddon::Get().GetDiscordClient();
auto channels = discord.GetChannelsInGuild(ID);
std::unordered_map<Snowflake, std::vector<ChannelData>> category_to_channels;
std::map<int, std::vector<ChannelData>> position_to_categories;
std::map<int, std::vector<ChannelData>> orphan_channels;
for (const auto &channel_id : channels) {
const auto data = discord.GetChannel(channel_id);
if (!data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS))
orphan_channels[*data->Position].push_back(*data);
else if (data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS))
category_to_channels[*data->ParentID].push_back(*data);
else if (data->Type == ChannelType::GUILD_CATEGORY)
position_to_categories[*data->Position].push_back(*data);
}
for (auto &[pos, channels] : orphan_channels) {
std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
return a.ID < b.ID;
});
for (const auto &chan : channels)
ret.push_back(chan.ID);
}
for (auto &[pos, categories] : position_to_categories) {
std::sort(categories.begin(), categories.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
return a.ID < b.ID;
});
for (const auto &category : categories) {
ret.push_back(category.ID);
if (ignore == category.ID) continue; // stupid hack to save me some time
auto it = category_to_channels.find(category.ID);
if (it == category_to_channels.end()) continue;
auto &channels = it->second;
std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
return a.Position < b.Position;
});
for (auto &channel : channels) {
ret.push_back(channel.ID);
}
}
}
return ret;
}
void from_json(const nlohmann::json &j, GuildApplicationData &m) {
JS_D("user_id", m.UserID);
JS_D("guild_id", m.GuildID);

View File

@@ -91,9 +91,8 @@ struct GuildData {
friend void from_json(const nlohmann::json &j, GuildData &m);
void update_from_json(const nlohmann::json &j);
bool HasFeature(const std::string &feature);
bool HasFeature(const std::string &feature) const;
bool HasIcon() const;
bool HasAnimatedIcon() const;
std::string GetIconURL(std::string ext = "png", std::string size = "32") const;
std::vector<Snowflake> GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const;
std::string GetIconURL(const std::string &ext = "png", const std::string &size = "32") const;
};

View File

@@ -1,5 +1,7 @@
#include "httpclient.hpp"
#include <utility>
//#define USE_LOCAL_PROXY
HTTPClient::HTTPClient() {
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
@@ -10,19 +12,19 @@ void HTTPClient::SetBase(const std::string &url) {
}
void HTTPClient::SetUserAgent(std::string agent) {
m_agent = agent;
m_agent = std::move(agent);
}
void HTTPClient::SetAuth(std::string auth) {
m_authorization = auth;
m_authorization = std::move(auth);
}
void HTTPClient::MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb) {
void HTTPClient::MakeDELETE(const std::string &path, const std::function<void(http::response_type r)> &cb) {
printf("DELETE %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_DELETE, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
@@ -34,13 +36,13 @@ void HTTPClient::MakeDELETE(const std::string &path, std::function<void(http::re
}));
}
void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb) {
printf("PATCH %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PATCH, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
@@ -53,13 +55,13 @@ void HTTPClient::MakePATCH(const std::string &path, const std::string &payload,
}));
}
void HTTPClient::MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
void HTTPClient::MakePOST(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb) {
printf("POST %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_POST, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
@@ -72,14 +74,14 @@ void HTTPClient::MakePOST(const std::string &path, const std::string &payload, s
}));
}
void HTTPClient::MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
void HTTPClient::MakePUT(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb) {
printf("PUT %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PUT, m_api_base + path);
req.set_header("Authorization", m_authorization);
if (payload != "")
if (!payload.empty())
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
@@ -92,13 +94,13 @@ void HTTPClient::MakePUT(const std::string &path, const std::string &payload, st
}));
}
void HTTPClient::MakeGET(const std::string &path, std::function<void(http::response_type r)> cb) {
void HTTPClient::MakeGET(const std::string &path, const std::function<void(http::response_type r)> &cb) {
printf("GET %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_GET, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_user_agent(!m_agent.empty() ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
@@ -126,11 +128,11 @@ void HTTPClient::RunCallbacks() {
m_mutex.unlock();
}
void HTTPClient::OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb) {
void HTTPClient::OnResponse(const http::response_type &r, const std::function<void(http::response_type r)> &cb) {
CleanupFutures();
try {
m_mutex.lock();
m_queue.push([this, r, cb] { cb(r); });
m_queue.push([r, cb] { cb(r); });
m_dispatcher.emit();
m_mutex.unlock();
} catch (const std::exception &e) {

View File

@@ -17,14 +17,14 @@ public:
void SetUserAgent(std::string agent);
void SetAuth(std::string auth);
void MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb);
void MakeGET(const std::string &path, std::function<void(http::response_type r)> cb);
void MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
void MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
void MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
void MakeDELETE(const std::string &path, const std::function<void(http::response_type r)> &cb);
void MakeGET(const std::string &path, const std::function<void(http::response_type r)> &cb);
void MakePATCH(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
void MakePOST(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
void MakePUT(const std::string &path, const std::string &payload, const std::function<void(http::response_type r)> &cb);
private:
void OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb);
void OnResponse(const http::response_type &r, const std::function<void(http::response_type r)> &cb);
void CleanupFutures();
mutable std::mutex m_mutex;

View File

@@ -14,10 +14,10 @@ enum class InteractionType {
};
struct MessageInteractionData {
Snowflake ID; // id of the interaction
InteractionType Type; // the type of interaction
std::string Name; // the name of the ApplicationCommand
UserData User; // the user who invoked the interaction
Snowflake ID; // id of the interaction
InteractionType Type; // the type of interaction
std::string Name; // the name of the ApplicationCommand
UserData User; // the user who invoked the interaction
// undocumented???
std::optional<GuildMember> Member; // the member who invoked the interaction (in a guild)

View File

@@ -141,8 +141,8 @@ inline void json_update_optional_nullable_default(const ::nlohmann::json &j, con
} while (0)
// set a json value from a std::optional only if it has a value
#define JS_IF(k, v) \
do { \
if (v.has_value()) \
j[k] = *v; \
#define JS_IF(k, v) \
do { \
if ((v).has_value()) \
j[k] = *(v); \
} while (0)

View File

@@ -19,7 +19,7 @@ std::vector<RoleData> GuildMember::GetSortedRoles() const {
for (const auto role_id : Roles) {
const auto role = Abaddon::Get().GetDiscordClient().GetRole(role_id);
if (!role.has_value()) continue;
roles.push_back(std::move(*role));
roles.push_back(*role);
}
std::sort(roles.begin(), roles.end(), [](const RoleData &a, const RoleData &b) {

View File

@@ -20,7 +20,7 @@ struct GuildMember {
// undocuemtned moment !!!1
std::optional<std::string> Avatar;
std::vector<RoleData> GetSortedRoles() const;
[[nodiscard]] std::vector<RoleData> GetSortedRoles() const;
void update_from_json(const nlohmann::json &j);
friend void from_json(const nlohmann::json &j, GuildMember &m);

View File

@@ -176,7 +176,7 @@ void to_json(nlohmann::json &j, const MessageApplicationData &m) {
j["id"] = m.ID;
JS_IF("cover_image", m.CoverImage);
j["description"] = m.Description;
if (m.Icon == "")
if (m.Icon.empty())
j["icon"] = nullptr;
else
j["icon"] = m.Icon;
@@ -230,7 +230,7 @@ void Message::from_json_edited(const nlohmann::json &j) {
JS_O("content", Content);
JS_O("timestamp", Timestamp);
JS_ON("edited_timestamp", EditedTimestamp);
if (EditedTimestamp.size() > 0)
if (!EditedTimestamp.empty())
SetEdited();
JS_O("tts", IsTTS);
JS_O("mention_everyone", DoesMentionEveryone);

View File

@@ -209,10 +209,10 @@ struct Message {
void SetDeleted();
void SetEdited();
bool IsDeleted() const;
bool IsEdited() const;
[[nodiscard]] bool IsDeleted() const;
[[nodiscard]] bool IsEdited() const;
bool DoesMention(Snowflake id) const noexcept;
[[nodiscard]] bool DoesMention(Snowflake id) const noexcept;
private:
bool m_deleted = false;

View File

@@ -77,7 +77,7 @@ void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m) {
}
void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) {
j["op"] = GatewayOp::LazyLoadRequest;
j["op"] = GatewayOp::GuildSubscriptions;
j["d"] = nlohmann::json::object();
j["d"]["guild_id"] = m.GuildID;
if (m.Channels.has_value()) {
@@ -98,7 +98,7 @@ void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) {
}
void to_json(nlohmann::json &j, const UpdateStatusMessage &m) {
j["op"] = GatewayOp::UpdateStatus;
j["op"] = GatewayOp::PresenceUpdate;
j["d"] = nlohmann::json::object();
j["d"]["since"] = m.Since;
j["d"]["activities"] = m.Activities;
@@ -119,6 +119,14 @@ void to_json(nlohmann::json &j, const UpdateStatusMessage &m) {
}
}
void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m) {
j["op"] = GatewayOp::RequestGuildMembers;
j["d"] = nlohmann::json::object();
j["d"]["guild_id"] = m.GuildID;
j["d"]["presences"] = m.Presences;
j["d"]["user_ids"] = m.UserIDs;
}
void from_json(const nlohmann::json &j, ReadStateEntry &m) {
JS_ON("mention_count", m.MentionCount);
JS_ON("last_message_id", m.LastMessageID);
@@ -243,7 +251,7 @@ void to_json(nlohmann::json &j, const IdentifyProperties &m) {
j["referring_domain_current"] = m.ReferringDomainCurrent;
j["release_channel"] = m.ReleaseChannel;
j["client_build_number"] = m.ClientBuildNumber;
if (m.ClientEventSource == "")
if (m.ClientEventSource.empty())
j["client_event_source"] = nullptr;
else
j["client_event_source"] = m.ClientEventSource;
@@ -282,7 +290,7 @@ void to_json(nlohmann::json &j, const CreateMessageObject &m) {
}
void to_json(nlohmann::json &j, const MessageEditObject &m) {
if (m.Content.size() > 0)
if (!m.Content.empty())
j["content"] = m.Content;
// todo EmbedData to_json
@@ -626,3 +634,8 @@ void to_json(nlohmann::json &j, const AckBulkData &m) {
void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m) {
m.Settings = j;
}
void from_json(const nlohmann::json &j, GuildMembersChunkData &m) {
JS_D("members", m.Members);
JS_D("guild_id", m.GuildID);
}

View File

@@ -24,16 +24,35 @@
// most stuff below should just be objects that get processed and thrown away immediately
enum class GatewayOp : int {
Event = 0,
Dispatch = 0,
Heartbeat = 1,
Identify = 2,
UpdateStatus = 3,
PresenceUpdate = 3,
VoiceStateUpdate = 4,
VoiceServerPing = 5,
Resume = 6,
Reconnect = 7,
RequestGuildMembers = 8,
InvalidSession = 9,
Hello = 10,
HeartbeatAck = 11,
LazyLoadRequest = 14,
// 12 unused
CallConnect = 13,
GuildSubscriptions = 14,
LobbyConnect = 15,
LobbyDisconnect = 16,
LobbyVoiceStatesUpdate = 17,
StreamCreate = 18,
StreamDelete = 19,
StreamWatch = 20,
StreamPing = 21,
StreamSetPaused = 22,
// 23 unused
RequestGuildApplicationCommands = 24,
EmbeddedActivityLaunch = 25,
EmbeddedActivityClose = 26,
EmbeddedActivityUpdate = 27,
RequestForumUnreads = 28,
};
enum class GatewayEvent : int {
@@ -80,6 +99,7 @@ enum class GatewayEvent : int {
THREAD_MEMBER_LIST_UPDATE,
MESSAGE_ACK,
USER_GUILD_SETTINGS_UPDATE,
GUILD_MEMBERS_CHUNK,
};
enum class GatewayCloseCode : uint16_t {
@@ -177,7 +197,7 @@ struct GuildMemberListUpdateMessage {
std::string HoistedRole; // null
bool IsDefeaned;
GuildMember GetAsMemberData() const;
[[nodiscard]] GuildMember GetAsMemberData() const;
friend void from_json(const nlohmann::json &j, MemberItem &m);
@@ -226,6 +246,14 @@ struct UpdateStatusMessage {
friend void to_json(nlohmann::json &j, const UpdateStatusMessage &m);
};
struct RequestGuildMembersMessage {
Snowflake GuildID;
bool Presences;
std::vector<Snowflake> UserIDs;
friend void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m);
};
struct ReadStateEntry {
int MentionCount;
Snowflake LastMessageID;
@@ -822,3 +850,16 @@ struct UserGuildSettingsUpdateData {
friend void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m);
};
struct GuildMembersChunkData {
/*
not needed so not deserialized
int ChunkCount;
int ChunkIndex;
std::vector<?> NotFound;
*/
Snowflake GuildID;
std::vector<GuildMember> Members;
friend void from_json(const nlohmann::json &j, GuildMembersChunkData &m);
};

View File

@@ -3,19 +3,19 @@
#include "user.hpp"
enum class RelationshipType {
None = 0,
Friend = 1,
Blocked = 2,
PendingIncoming = 3,
PendingOutgoing = 4,
Implicit = 5,
None = 0,
Friend = 1,
Blocked = 2,
PendingIncoming = 3,
PendingOutgoing = 4,
Implicit = 5,
};
struct RelationshipData {
// Snowflake UserID; this is the same as ID apparently but it looks new so i wont touch it
RelationshipType Type;
Snowflake ID;
// Unknown Nickname; // null
// Unknown Nickname; // null
friend void from_json(const nlohmann::json &j, RelationshipData &m);
friend void from_json(const nlohmann::json &j, RelationshipData &m);
};

View File

@@ -16,8 +16,8 @@ struct RoleData {
bool IsManaged;
bool IsMentionable;
bool HasColor() const noexcept;
Glib::ustring GetEscapedName() const;
[[nodiscard]] bool HasColor() const noexcept;
[[nodiscard]] Glib::ustring GetEscapedName() const;
friend void from_json(const nlohmann::json &j, RoleData &m);
};

View File

@@ -25,7 +25,7 @@ Snowflake::Snowflake(const Glib::ustring &str) {
m_num = std::strtoull(str.c_str(), nullptr, 10);
else
m_num = Invalid;
};
}
Snowflake Snowflake::FromNow() {
using namespace std::chrono;
@@ -53,14 +53,12 @@ bool Snowflake::IsValid() const {
return m_num != Invalid;
}
std::string Snowflake::GetLocalTimestamp() const {
Glib::ustring Snowflake::GetLocalTimestamp() const {
const time_t secs_since_epoch = (m_num / SecondsInterval) + DiscordEpochSeconds;
const std::tm tm = *localtime(&secs_since_epoch);
std::stringstream ss;
const static std::locale locale("");
ss.imbue(locale);
ss << std::put_time(&tm, "%X %x");
return ss.str();
std::array<char, 256> tmp {};
std::strftime(tmp.data(), sizeof(tmp), "%X %x", &tm);
return tmp.data();
}
void from_json(const nlohmann::json &j, Snowflake &s) {

View File

@@ -12,8 +12,8 @@ struct Snowflake {
static Snowflake FromNow(); // not thread safe
static Snowflake FromISO8601(std::string_view ts);
bool IsValid() const;
std::string GetLocalTimestamp() const;
[[nodiscard]] bool IsValid() const;
[[nodiscard]] Glib::ustring GetLocalTimestamp() const;
bool operator==(const Snowflake &s) const noexcept {
return m_num == s.m_num;

View File

@@ -22,15 +22,6 @@ void from_json(const nlohmann::json &j, StickerData &m) {
JS_D("format_type", m.FormatType);
}
std::string StickerData::GetURL() const {
if (!AssetHash.has_value()) return "";
if (FormatType == StickerFormatType::PNG || FormatType == StickerFormatType::APNG)
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".png?size=256";
else if (FormatType == StickerFormatType::LOTTIE)
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".json";
return "";
}
void to_json(nlohmann::json &j, const StickerItem &m) {
j["id"] = m.ID;
j["name"] = m.Name;

View File

@@ -24,8 +24,6 @@ struct StickerData {
friend void to_json(nlohmann::json &j, const StickerData &m);
friend void from_json(const nlohmann::json &j, StickerData &m);
std::string GetURL() const;
};
struct StickerItem {
@@ -36,5 +34,5 @@ struct StickerItem {
friend void to_json(nlohmann::json &j, const StickerItem &m);
friend void from_json(const nlohmann::json &j, StickerItem &m);
std::string GetURL() const;
[[nodiscard]] std::string GetURL() const;
};

View File

@@ -576,6 +576,23 @@ std::vector<Snowflake> Store::GetChannelIDsWithParentID(Snowflake channel_id) co
return ret;
}
std::unordered_set<Snowflake> Store::GetMembersInGuild(Snowflake guild_id) const {
auto &s = m_stmt_get_guild_member_ids;
s->Bind(1, guild_id);
std::unordered_set<Snowflake> ret;
while (s->FetchOne()) {
Snowflake x;
s->Get(0, x);
ret.insert(x);
}
s->Reset();
return ret;
}
void Store::AddReaction(const MessageReactionAddObject &data, bool byself) {
auto &s = m_stmt_add_reaction;
@@ -801,8 +818,8 @@ std::optional<GuildMember> Store::GetGuildMember(Snowflake guild_id, Snowflake u
s->Get(2, r.Nickname);
s->Get(3, r.JoinedAt);
s->Get(4, r.PremiumSince);
//s->Get(5, r.IsDeafened);
//s->Get(6, r.IsMuted);
// s->Get(5, r.IsDeafened);
// s->Get(6, r.IsMuted);
s->Get(7, r.Avatar);
s->Get(8, r.IsPending);
@@ -862,8 +879,8 @@ Message Store::GetMessageBound(std::unique_ptr<Statement> &s) const {
s->Get(4, r.Content);
s->Get(5, r.Timestamp);
s->Get(6, r.EditedTimestamp);
//s->Get(7, r.IsTTS);
//s->Get(8, r.DoesMentionEveryone);
// s->Get(7, r.IsTTS);
// s->Get(8, r.DoesMentionEveryone);
s->GetJSON(9, r.Embeds);
s->Get(10, r.IsPinned);
s->Get(11, r.WebhookID);
@@ -984,11 +1001,11 @@ std::optional<RoleData> Store::GetRole(Snowflake id) const {
return role;
}
RoleData Store::GetRoleBound(std::unique_ptr<Statement> &s) const {
RoleData Store::GetRoleBound(std::unique_ptr<Statement> &s) {
RoleData r;
s->Get(0, r.ID);
//s->Get(1, guild id);
// s->Get(1, guild id);
s->Get(2, r.Name);
s->Get(3, r.Color);
s->Get(4, r.IsHoisted);
@@ -2134,6 +2151,14 @@ bool Store::CreateStatements() {
return false;
}
m_stmt_get_guild_member_ids = std::make_unique<Statement>(m_db, R"(
SELECT user_id FROM members WHERE guild_id = ?
)");
if (!m_stmt_get_guild_member_ids->OK()) {
fprintf(stderr, "failed to prepare get guild member ids statement: %s\n", m_db.ErrStr());
return false;
}
return true;
}
@@ -2224,11 +2249,11 @@ int Store::Statement::Bind(int index, Snowflake id) {
int Store::Statement::Bind(int index, const char *str, size_t len) {
if (len == -1) len = strlen(str);
return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str, len, SQLITE_TRANSIENT));
return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str, static_cast<int>(len), SQLITE_TRANSIENT));
}
int Store::Statement::Bind(int index, const std::string &str) {
return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT));
return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str.c_str(), static_cast<int>(str.size()), SQLITE_TRANSIENT));
}
int Store::Statement::Bind(int index) {

View File

@@ -44,6 +44,8 @@ public:
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const; // public
std::vector<Snowflake> GetChannelIDsWithParentID(Snowflake channel_id) const;
std::unordered_set<Snowflake> GetMembersInGuild(Snowflake guild_id) const;
// ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data
void AddReaction(const MessageReactionAddObject &data, bool byself);
void RemoveReaction(const MessageReactionRemoveObject &data, bool byself);
@@ -99,7 +101,7 @@ private:
~Statement();
Statement &operator=(Statement &other) = delete;
bool OK() const;
[[nodiscard]] bool OK() const;
int Bind(int index, Snowflake id);
int Bind(int index, const char *str, size_t len = -1);
@@ -222,7 +224,7 @@ private:
*first++ = id.get<T>();
}
bool IsNull(int index) const;
[[nodiscard]] bool IsNull(int index) const;
int Step();
bool Insert();
bool FetchOne();
@@ -236,7 +238,7 @@ private:
};
Message GetMessageBound(std::unique_ptr<Statement> &stmt) const;
RoleData GetRoleBound(std::unique_ptr<Statement> &stmt) const;
static RoleData GetRoleBound(std::unique_ptr<Statement> &stmt);
void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction);
@@ -302,5 +304,6 @@ private:
STMT(sub_reaction);
STMT(get_reactions);
STMT(get_chan_ids_parent);
STMT(get_guild_member_ids);
#undef STMT
};

View File

@@ -17,7 +17,7 @@ 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] == '_')
return true;
else if (!member->Avatar.has_value())
else if (member.has_value() && !member->Avatar.has_value())
return HasAnimatedAvatar();
return false;
}
@@ -29,7 +29,7 @@ bool UserData::HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const
return HasAnimatedAvatar();
}
std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::string size) const {
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] == '_'))
@@ -43,14 +43,14 @@ std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::str
}
}
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext, std::string size) const {
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, const std::string &ext, std::string size) const {
if (guild_id.has_value())
return GetAvatarURL(*guild_id, ext, size);
else
return GetAvatarURL(ext, size);
}
std::string UserData::GetAvatarURL(std::string ext, std::string size) const {
std::string UserData::GetAvatarURL(const std::string &ext, std::string size) const {
if (HasAvatar())
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
else
@@ -107,7 +107,7 @@ void to_json(nlohmann::json &j, const UserData &m) {
j["id"] = m.ID;
j["username"] = m.Username;
j["discriminator"] = m.Discriminator;
if (m.Avatar == "")
if (m.Avatar.empty())
j["avatar"] = nullptr;
else
j["avatar"] = m.Avatar;

View File

@@ -60,22 +60,22 @@ struct UserData {
friend void to_json(nlohmann::json &j, const UserData &m);
void update_from_json(const nlohmann::json &j);
bool IsDeleted() const;
bool HasAvatar() const;
bool HasAnimatedAvatar() const noexcept;
bool HasAnimatedAvatar(Snowflake guild_id) const;
bool HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const;
std::string GetAvatarURL(Snowflake guild_id, std::string ext = "png", std::string size = "32") const;
std::string GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext = "png", std::string size = "32") const;
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;
std::string GetDefaultAvatarURL() const;
Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
std::string GetMention() const;
std::string GetEscapedName() const;
std::string GetEscapedBoldName() const;
std::string GetEscapedString() const;
[[nodiscard]] bool IsDeleted() const;
[[nodiscard]] bool HasAvatar() const;
[[nodiscard]] bool HasAnimatedAvatar() const noexcept;
[[nodiscard]] bool HasAnimatedAvatar(Snowflake guild_id) const;
[[nodiscard]] bool HasAnimatedAvatar(const std::optional<Snowflake> &guild_id) const;
[[nodiscard]] std::string GetAvatarURL(Snowflake guild_id, const std::string &ext = "png", std::string size = "32") const;
[[nodiscard]] std::string GetAvatarURL(const std::optional<Snowflake> &guild_id, const std::string &ext = "png", std::string size = "32") const;
[[nodiscard]] std::string GetAvatarURL(const std::string &ext = "png", std::string size = "32") const;
[[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>
inline std::string GetEscapedBoldString() const {
[[nodiscard]] inline std::string GetEscapedBoldString() const {
if constexpr (with_at)
return "<b>@" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
else

View File

@@ -6,7 +6,7 @@
struct UserSettingsGuildFoldersEntry {
int Color = -1; // null
std::vector<Snowflake> GuildIDs;
Snowflake ID; // null (this can be a snowflake as a string or an int that isnt a snowflake lol)
Snowflake ID; // null (this can be a snowflake as a string or an int that isnt a snowflake lol)
std::string Name; // null
friend void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m);
@@ -19,16 +19,16 @@ struct UserSettings {
std::string Status; //
bool ShouldShowCurrentGame; //
// std::vector<Unknown> RestrictedGuilds; //
bool ShouldRenderReactions; //
bool ShouldRenderEmbeds; //
bool IsNativePhoneIntegrationEnabled; //
bool ShouldMessageDisplayCompact; //
std::string Locale; //
bool ShouldInlineEmbedMedia; //
bool ShouldInlineAttachmentMedia; //
std::vector<Snowflake> GuildPositions; // deprecated?
bool ShouldRenderReactions; //
bool ShouldRenderEmbeds; //
bool IsNativePhoneIntegrationEnabled; //
bool ShouldMessageDisplayCompact; //
std::string Locale; //
bool ShouldInlineEmbedMedia; //
bool ShouldInlineAttachmentMedia; //
std::vector<Snowflake> GuildPositions; // deprecated?
std::vector<UserSettingsGuildFoldersEntry> GuildFolders; //
bool ShouldGIFAutoplay; //
bool ShouldGIFAutoplay; //
// Unknown FriendSourceFlags; //
int ExplicitContentFilter; //
bool IsTTSCommandEnabled; //

View File

@@ -1,18 +1,18 @@
#include "websocket.hpp"
#include <functional>
#include <utility>
Websocket::Websocket() {}
Websocket::Websocket() = default;
void Websocket::StartConnection(std::string url) {
void Websocket::StartConnection(const std::string &url) {
m_websocket.disableAutomaticReconnection();
m_websocket.setUrl(url);
m_websocket.setOnMessageCallback(std::bind(&Websocket::OnMessage, this, std::placeholders::_1));
m_websocket.setOnMessageCallback([this](auto &&msg) { OnMessage(std::forward<decltype(msg)>(msg)); });
m_websocket.setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works
m_websocket.start();
}
void Websocket::SetUserAgent(std::string agent) {
m_agent = agent;
m_agent = std::move(agent);
}
bool Websocket::GetPrintMessages() const noexcept {
@@ -31,11 +31,6 @@ void Websocket::Stop(uint16_t code) {
m_websocket.stop(code);
}
bool Websocket::IsOpen() const {
auto state = m_websocket.getReadyState();
return state == ix::ReadyState::Open;
}
void Websocket::Send(const std::string &str) {
if (m_print_messages)
printf("sending %s\n", str.c_str());

View File

@@ -9,7 +9,7 @@
class Websocket {
public:
Websocket();
void StartConnection(std::string url);
void StartConnection(const std::string &url);
void SetUserAgent(std::string agent);
@@ -20,7 +20,6 @@ public:
void Send(const nlohmann::json &j);
void Stop();
void Stop(uint16_t code);
bool IsOpen() const;
private:
void OnMessage(const ix::WebSocketMessagePtr &msg);

View File

@@ -1,8 +1,9 @@
#include "emojis.hpp"
#include <sstream>
#include <utility>
EmojiResource::EmojiResource(std::string filepath)
: m_filepath(filepath) {}
: m_filepath(std::move(filepath)) {}
bool EmojiResource::Load() {
m_fp = std::fopen(m_filepath.c_str(), "rb");
@@ -31,7 +32,7 @@ bool EmojiResource::Load() {
std::fread(&surrogates_count, 4, 1, m_fp);
std::string surrogates(surrogates_count, '\0');
std::fread(surrogates.data(), surrogates_count, 1, m_fp);
m_patterns.push_back(surrogates);
m_patterns.emplace_back(surrogates);
int data_size, data_offset;
std::fread(&data_size, 4, 1, m_fp);
@@ -52,7 +53,7 @@ bool EmojiResource::Load() {
Glib::RefPtr<Gdk::Pixbuf> EmojiResource::GetPixBuf(const Glib::ustring &pattern) {
const auto it = m_index.find(pattern);
if (it == m_index.end()) return Glib::RefPtr<Gdk::Pixbuf>();
if (it == m_index.end()) return {};
const int pos = it->second.first;
const int len = it->second.second;
std::fseek(m_fp, pos, SEEK_SET);
@@ -86,17 +87,17 @@ void EmojiResource::ReplaceEmojis(Glib::RefPtr<Gtk::TextBuffer> buf, int size) {
else
break;
}
searchpos = r + pattern.size();
searchpos = static_cast<int>(r + pattern.size());
const auto start_it = buf->get_iter_at_offset(r);
const auto end_it = buf->get_iter_at_offset(r + pattern.size());
const auto start_it = buf->get_iter_at_offset(static_cast<int>(r));
const auto end_it = buf->get_iter_at_offset(static_cast<int>(r + pattern.size()));
auto it = buf->erase(start_it, end_it);
buf->insert_pixbuf(it, pixbuf);
int alen = text.size();
int alen = static_cast<int>(text.size());
text = get_text();
int blen = text.size();
int blen = static_cast<int>(text.size());
searchpos -= (alen - blen);
}
}
@@ -109,10 +110,6 @@ std::string EmojiResource::GetShortCodeForPattern(const Glib::ustring &pattern)
return "";
}
const std::vector<Glib::ustring> &EmojiResource::GetPatterns() const {
return m_patterns;
}
const std::map<std::string, std::string> &EmojiResource::GetShortCodes() const {
return m_shortcode_index;
}

View File

@@ -12,7 +12,6 @@ public:
EmojiResource(std::string filepath);
bool Load();
Glib::RefPtr<Gdk::Pixbuf> GetPixBuf(const Glib::ustring &pattern);
const std::vector<Glib::ustring> &GetPatterns() const;
const std::map<std::string, std::string> &GetShortCodes() const;
void ReplaceEmojis(Glib::RefPtr<Gtk::TextBuffer> buf, int size = 24);
std::string GetShortCodeForPattern(const Glib::ustring &pattern);

View File

@@ -1,10 +1,12 @@
#include "abaddon.hpp"
#include "filecache.hpp"
#include <utility>
#include "MurmurHash3.h"
std::string GetCachedName(std::string str) {
std::string GetCachedName(const std::string &str) {
uint32_t out;
MurmurHash3_x86_32(str.c_str(), str.size(), 0, &out);
MurmurHash3_x86_32(str.c_str(), static_cast<int>(str.size()), 0, &out);
return std::to_string(out);
}
@@ -35,15 +37,15 @@ void Cache::ClearCache() {
std::filesystem::remove_all(path);
}
void Cache::RespondFromPath(std::filesystem::path path, callback_type cb) {
void Cache::RespondFromPath(const std::filesystem::path &path, const callback_type &cb) {
cb(path.string());
}
void Cache::GetFileFromURL(std::string url, callback_type cb) {
void Cache::GetFileFromURL(const std::string &url, const callback_type &cb) {
auto cache_path = m_tmp_path / GetCachedName(url);
if (std::filesystem::exists(cache_path)) {
m_mutex.lock();
m_futures.push_back(std::async(std::launch::async, [this, cache_path, cb]() { RespondFromPath(cache_path, cb); }));
m_futures.push_back(std::async(std::launch::async, [cache_path, cb]() { RespondFromPath(cache_path, cb); }));
m_mutex.unlock();
return;
}
@@ -58,7 +60,7 @@ void Cache::GetFileFromURL(std::string url, callback_type cb) {
}
}
std::string Cache::GetPathIfCached(std::string url) {
std::string Cache::GetPathIfCached(const std::string &url) {
auto cache_path = m_tmp_path / GetCachedName(url);
if (std::filesystem::exists(cache_path)) {
return cache_path.string();
@@ -94,13 +96,13 @@ void Cache::OnResponse(const std::string &url) {
void Cache::OnFetchComplete(const std::string &url) {
m_mutex.lock();
m_futures.push_back(std::async(std::launch::async, std::bind(&Cache::OnResponse, this, url)));
m_futures.push_back(std::async(std::launch::async, [this, url] { OnResponse(url); }));
m_mutex.unlock();
}
FileCacheWorkerThread::FileCacheWorkerThread() {
m_multi_handle = curl_multi_init();
m_thread = std::thread(std::bind(&FileCacheWorkerThread::loop, this));
m_thread = std::thread([this] { loop(); });
}
FileCacheWorkerThread::~FileCacheWorkerThread() {
@@ -116,7 +118,7 @@ void FileCacheWorkerThread::set_file_path(const std::filesystem::path &path) {
void FileCacheWorkerThread::add_image(const std::string &string, callback_type callback) {
m_queue_mutex.lock();
m_queue.push({ string, callback });
m_queue.push({ string, std::move(callback) });
m_cv.notify_one();
m_queue_mutex.unlock();
}
@@ -130,15 +132,14 @@ void FileCacheWorkerThread::stop() {
}
void FileCacheWorkerThread::loop() {
timeval timeout;
timeval timeout {};
timeout.tv_sec = 1;
timeout.tv_usec = 0;
while (!m_stop) {
if (m_handles.size() == 0) {
if (m_handles.empty()) {
std::unique_lock<std::mutex> lock(m_queue_mutex);
int s = m_queue.size();
if (s == 0)
if (m_queue.empty())
m_cv.wait(lock);
}
@@ -146,7 +147,7 @@ void FileCacheWorkerThread::loop() {
if (m_handles.size() < concurrency) {
std::optional<QueueEntry> entry;
m_queue_mutex.lock();
if (m_queue.size() > 0) {
if (!m_queue.empty()) {
entry = std::move(m_queue.front());
m_queue.pop();
}

View File

@@ -59,13 +59,13 @@ public:
~Cache();
using callback_type = std::function<void(std::string)>;
void GetFileFromURL(std::string url, callback_type cb);
std::string GetPathIfCached(std::string url);
void GetFileFromURL(const std::string &url, const callback_type &cb);
std::string GetPathIfCached(const std::string &url);
void ClearCache();
private:
void CleanupFutures();
void RespondFromPath(std::filesystem::path path, callback_type cb);
static void RespondFromPath(const std::filesystem::path &path, const callback_type &cb);
void OnResponse(const std::string &url);
void OnFetchComplete(const std::string &url);

View File

@@ -1,8 +1,10 @@
#include "http.hpp"
#include <utility>
namespace http {
request::request(EMethod method, const std::string &url)
: m_url(url) {
request::request(EMethod method, std::string url)
: m_url(std::move(url)) {
switch (method) {
case REQUEST_GET:
m_method = "GET";
@@ -99,7 +101,7 @@ void request::prepare() {
namespace detail {
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
const size_t n = size * nmemb;
static_cast<std::string*>(userdata)->append(static_cast<char*>(ptr), n);
static_cast<std::string *>(userdata)->append(static_cast<char *>(ptr), n);
return n;
}

View File

@@ -97,7 +97,7 @@ struct response {
};
struct request {
request(EMethod method, const std::string &url);
request(EMethod method, std::string url);
~request();
void set_verify_ssl(bool verify);

View File

@@ -1,4 +1,6 @@
#include "imgmanager.hpp"
#include <utility>
#include "util.hpp"
#include "abaddon.hpp"
@@ -6,17 +8,13 @@ ImageManager::ImageManager() {
m_cb_dispatcher.connect(sigc::mem_fun(*this, &ImageManager::RunCallbacks));
}
Cache &ImageManager::GetCache() {
return m_cache;
}
void ImageManager::ClearCache() {
m_cache.ClearCache();
}
Glib::RefPtr<Gdk::Pixbuf> ImageManager::ReadFileToPixbuf(std::string path) {
const auto &data = ReadWholeFile(path);
if (data.size() == 0) return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
const auto &data = ReadWholeFile(std::move(path));
if (data.empty()) return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader](int w, int h) {
int cw, ch;
@@ -29,8 +27,8 @@ Glib::RefPtr<Gdk::Pixbuf> ImageManager::ReadFileToPixbuf(std::string path) {
}
Glib::RefPtr<Gdk::PixbufAnimation> ImageManager::ReadFileToPixbufAnimation(std::string path, int w, int h) {
const auto &data = ReadWholeFile(path);
if (data.size() == 0) return Glib::RefPtr<Gdk::PixbufAnimation>(nullptr);
const auto &data = ReadWholeFile(std::move(path));
if (data.empty()) return Glib::RefPtr<Gdk::PixbufAnimation>(nullptr);
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader, w, h](int, int) {
loader->set_size(w, h);
@@ -40,10 +38,10 @@ Glib::RefPtr<Gdk::PixbufAnimation> ImageManager::ReadFileToPixbufAnimation(std::
return loader->get_animation();
}
void ImageManager::LoadFromURL(std::string url, callback_type cb) {
void ImageManager::LoadFromURL(const std::string &url, const callback_type &cb) {
sigc::signal<void(Glib::RefPtr<Gdk::Pixbuf>)> signal;
signal.connect(cb);
m_cache.GetFileFromURL(url, [this, url, signal](std::string path) {
m_cache.GetFileFromURL(url, [this, url, signal](const std::string &path) {
try {
auto buf = ReadFileToPixbuf(path);
if (!buf)
@@ -60,10 +58,10 @@ void ImageManager::LoadFromURL(std::string url, callback_type cb) {
});
}
void ImageManager::LoadAnimationFromURL(std::string url, int w, int h, callback_anim_type cb) {
void ImageManager::LoadAnimationFromURL(const std::string &url, int w, int h, const callback_anim_type &cb) {
sigc::signal<void(Glib::RefPtr<Gdk::PixbufAnimation>)> signal;
signal.connect(cb);
m_cache.GetFileFromURL(url, [this, url, signal, w, h](std::string path) {
m_cache.GetFileFromURL(url, [this, url, signal, w, h](const std::string &path) {
try {
auto buf = ReadFileToPixbufAnimation(path, w, h);
if (!buf)
@@ -80,7 +78,7 @@ void ImageManager::LoadAnimationFromURL(std::string url, int w, int h, callback_
});
}
void ImageManager::Prefetch(std::string url) {
void ImageManager::Prefetch(const std::string &url) {
m_cache.GetFileFromURL(url, [](const auto &) {});
}
@@ -91,22 +89,6 @@ void ImageManager::RunCallbacks() {
m_cb_mutex.unlock();
}
Glib::RefPtr<Gdk::Pixbuf> ImageManager::GetFromURLIfCached(std::string url) {
std::string path = m_cache.GetPathIfCached(url);
if (path != "")
return ReadFileToPixbuf(path);
return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
}
Glib::RefPtr<Gdk::PixbufAnimation> ImageManager::GetAnimationFromURLIfCached(std::string url, int w, int h) {
std::string path = m_cache.GetPathIfCached(url);
if (path != "")
return ReadFileToPixbufAnimation(path, w, h);
return Glib::RefPtr<Gdk::PixbufAnimation>(nullptr);
}
Glib::RefPtr<Gdk::Pixbuf> ImageManager::GetPlaceholder(int size) {
std::string name = "/placeholder" + std::to_string(size);
if (m_pixs.find(name) != m_pixs.end())

View File

@@ -13,19 +13,16 @@ public:
using callback_anim_type = sigc::slot<void(Glib::RefPtr<Gdk::PixbufAnimation>)>;
using callback_type = sigc::slot<void(Glib::RefPtr<Gdk::Pixbuf>)>;
Cache &GetCache();
void ClearCache();
void LoadFromURL(std::string url, callback_type cb);
void LoadFromURL(const std::string &url, const callback_type &cb);
// animations need dimensions before loading since there is no (easy) way to scale a PixbufAnimation
void LoadAnimationFromURL(std::string url, int w, int h, callback_anim_type cb);
void Prefetch(std::string url);
Glib::RefPtr<Gdk::Pixbuf> GetFromURLIfCached(std::string url);
Glib::RefPtr<Gdk::PixbufAnimation> GetAnimationFromURLIfCached(std::string url, int w, int h);
void LoadAnimationFromURL(const std::string &url, int w, int h, const callback_anim_type &cb);
void Prefetch(const std::string &url);
Glib::RefPtr<Gdk::Pixbuf> GetPlaceholder(int size);
private:
Glib::RefPtr<Gdk::Pixbuf> ReadFileToPixbuf(std::string path);
Glib::RefPtr<Gdk::PixbufAnimation> ReadFileToPixbufAnimation(std::string path, int w, int h);
static Glib::RefPtr<Gdk::Pixbuf> ReadFileToPixbuf(std::string path);
static Glib::RefPtr<Gdk::PixbufAnimation> ReadFileToPixbufAnimation(std::string path, int w, int h);
mutable std::mutex m_load_mutex;
void RunCallbacks();

View File

@@ -7,10 +7,14 @@
using namespace std::literals::string_literals;
#if defined(_WIN32) && defined(_MSC_VER)
#if defined(_WIN32)
#include <pango/pangocairo.h>
#include <pango/pangofc-fontmap.h>
#include <ShlObj_core.h>
#if defined(_MSC_VER)
#include <ShlObj_core.h>
#else
#include <shlobj.h>
#endif
#include <Shlwapi.h>
#include <Windows.h>
#pragma comment(lib, "Shlwapi.lib")
@@ -22,8 +26,8 @@ bool Platform::SetupFonts() {
{
// thanks @WorkingRobot for da help :^))
std::ifstream template_stream(buf + "\\fonts\\fonts.template.conf"s);
std::ofstream conf_stream(buf + "\\fonts\\fonts.conf"s);
std::ifstream template_stream(buf + R"(\fonts\fonts.template.conf)"s);
std::ofstream conf_stream(buf + R"(\fonts\fonts.conf)"s);
if (!template_stream.good()) {
printf("can't open fonts/fonts.template.conf\n");
return false;
@@ -36,7 +40,7 @@ bool Platform::SetupFonts() {
std::string line;
while (std::getline(template_stream, line)) {
if (line == "<!--(CONFD)-->")
conf_stream << "<include ignore_missing=\"no\">" << (buf + "\\fonts\\conf.d"s) << "</include>";
conf_stream << "<include ignore_missing=\"no\">" << (buf + R"(\fonts\conf.d)"s) << "</include>";
else
conf_stream << line;
conf_stream << '\n';
@@ -45,11 +49,11 @@ bool Platform::SetupFonts() {
auto fc = FcConfigCreate();
FcConfigSetCurrent(fc);
FcConfigParseAndLoad(fc, const_cast<FcChar8 *>(reinterpret_cast<const FcChar8 *>((buf + "\\fonts\\fonts.conf"s).c_str())), true);
FcConfigParseAndLoad(fc, const_cast<FcChar8 *>(reinterpret_cast<const FcChar8 *>((buf + R"(\fonts\fonts.conf)"s).c_str())), true);
FcConfigAppFontAddDir(fc, const_cast<FcChar8 *>(reinterpret_cast<const FcChar8 *>((buf + "\\fonts"s).c_str())));
char fonts_path[MAX_PATH];
if (SHGetFolderPathA(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, fonts_path) == S_OK) {
if (SHGetFolderPathA(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, fonts_path) == S_OK) {
FcConfigAppFontAddDir(fc, reinterpret_cast<FcChar8 *>(fonts_path));
}

View File

@@ -6,4 +6,4 @@ bool SetupFonts();
std::string FindResourceFolder();
std::string FindConfigFile();
std::string FindStateCacheFolder();
}
} // namespace Platform

View File

@@ -46,7 +46,7 @@ public:
SettingsManager(const std::string &filename);
void Close();
bool IsValid() const;
[[nodiscard]] bool IsValid() const;
Settings &GetSettings();
private:

View File

@@ -2,31 +2,14 @@
#include <array>
#include <filesystem>
Semaphore::Semaphore(int count)
: m_count(count) {}
void Semaphore::notify() {
std::unique_lock<std::mutex> lock(m_mutex);
m_count++;
lock.unlock();
m_cv.notify_one();
}
void Semaphore::wait() {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_count == 0)
m_cv.wait(lock);
m_count--;
}
void LaunchBrowser(Glib::ustring url) {
void LaunchBrowser(const Glib::ustring &url) {
GError *err = nullptr;
if (!gtk_show_uri_on_window(nullptr, url.c_str(), GDK_CURRENT_TIME, &err))
printf("failed to open uri: %s\n", err->message);
}
void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw, int clamph) {
const auto frac = static_cast<float>(inw) / inh;
const auto frac = static_cast<float>(inw) / static_cast<float>(inh);
outw = inw;
outh = inh;
@@ -42,7 +25,7 @@ void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw, int
}
}
std::vector<uint8_t> ReadWholeFile(std::string path) {
std::vector<uint8_t> ReadWholeFile(const std::string &path) {
std::vector<uint8_t> ret;
FILE *fp = std::fopen(path.c_str(), "rb");
if (fp == nullptr)
@@ -73,7 +56,7 @@ int GetTimezoneOffset() {
std::time_t local_secs = std::mktime(tptr);
tptr = std::gmtime(&secs);
std::time_t gmt_secs = std::mktime(tptr);
return local_secs - gmt_secs;
return static_cast<int>(local_secs - gmt_secs);
}
std::string FormatISO8601(const std::string &in, int extra_offset, const std::string &fmt) {
@@ -81,7 +64,7 @@ std::string FormatISO8601(const std::string &in, int extra_offset, const std::st
float milli;
std::sscanf(in.c_str(), "%d-%d-%dT%d:%d:%d%f+%d:%d",
&yr, &mon, &day, &hr, &min, &sec, &milli, &tzhr, &tzmin);
std::tm tm;
std::tm tm {};
tm.tm_year = yr - 1900;
tm.tm_mon = mon - 1;
tm.tm_mday = day;
@@ -94,11 +77,9 @@ std::string FormatISO8601(const std::string &in, int extra_offset, const std::st
int offset = GetTimezoneOffset();
tm.tm_sec += offset + extra_offset;
mktime(&tm);
std::stringstream ss;
const static std::locale locale("");
ss.imbue(locale);
ss << std::put_time(&tm, fmt.c_str());
return ss.str();
std::array<char, 512> tmp {};
std::strftime(tmp.data(), sizeof(tmp), fmt.c_str(), &tm);
return tmp.data();
}
void ScrollListBoxToSelected(Gtk::ListBox &list) {
@@ -145,13 +126,9 @@ Gdk::RGBA IntToRGBA(int color) {
return ret;
}
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu) {
AddWidgetMenuHandler(widget, menu, []() {});
}
// so widgets can modify the menu before it is displayed
// maybe theres a better way to do this idk
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, sigc::slot<void()> pre_callback) {
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, const sigc::slot<void()> &pre_callback) {
sigc::signal<void()> signal;
signal.connect(pre_callback);
widget->signal_button_press_event().connect([&menu, signal](GdkEventButton *ev) -> bool {
@@ -170,7 +147,7 @@ std::vector<std::string> StringSplit(const std::string &str, const char *delim)
std::vector<std::string> parts;
char *token = std::strtok(const_cast<char *>(str.c_str()), delim);
while (token != nullptr) {
parts.push_back(token);
parts.emplace_back(token);
token = std::strtok(nullptr, delim);
}
return parts;
@@ -179,7 +156,7 @@ std::vector<std::string> StringSplit(const std::string &str, const char *delim)
std::string GetExtension(std::string url) {
url = StringSplit(url, "?")[0];
url = StringSplit(url, "/").back();
return url.find(".") != std::string::npos ? url.substr(url.find_last_of(".")) : "";
return url.find('.') != std::string::npos ? url.substr(url.find_last_of('.')) : "";
}
bool IsURLViewableImage(const std::string &url) {

View File

@@ -31,28 +31,15 @@ bool IsFile(std::string_view path);
uint64_t TimeToEpoch(int year, int month, int day, int hour, int minute, int seconds);
} // namespace util
class Semaphore {
public:
Semaphore(int count = 0);
void notify();
void wait();
private:
std::mutex m_mutex;
std::condition_variable m_cv;
int m_count;
};
void LaunchBrowser(Glib::ustring url);
void LaunchBrowser(const Glib::ustring &url);
void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw = 400, int clamph = 300);
std::string IntToCSSColor(int color);
Gdk::RGBA IntToRGBA(int color);
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu);
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, sigc::slot<void()> pre_callback);
void AddWidgetMenuHandler(Gtk::Widget *widget, Gtk::Menu &menu, const sigc::slot<void()> &pre_callback);
std::vector<std::string> StringSplit(const std::string &str, const char *delim);
std::string GetExtension(std::string url);
bool IsURLViewableImage(const std::string &url);
std::vector<uint8_t> ReadWholeFile(std::string path);
std::vector<uint8_t> ReadWholeFile(const std::string &path);
std::string HumanReadableBytes(uint64_t bytes);
std::string FormatISO8601(const std::string &in, int extra_offset = 0, const std::string &fmt = "%x %X");
void AddPointerCursor(Gtk::Widget &widget);

View File

@@ -30,7 +30,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
auto &discord = Abaddon::Get().GetDiscordClient();
auto guild = *discord.GetGuild(GuildID);
for (const auto &entry : data.Entries) {
if (entry.TargetID == "") continue;
if (entry.TargetID.empty()) continue;
auto expander = Gtk::manage(new Gtk::Expander);
auto label = Gtk::manage(new Gtk::Label);
@@ -56,7 +56,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
if (entry.Changes.has_value())
for (const auto &change : *entry.Changes) {
if (change.Key == "icon_hash") {
extra_markup.push_back("Set the server icon");
extra_markup.emplace_back("Set the server icon");
} else if (change.Key == "name") {
auto new_name = change.NewValue;
if (new_name.has_value())
@@ -64,7 +64,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(new_name->get<std::string>()) +
"</b>");
else
extra_markup.push_back("Set the server name");
extra_markup.emplace_back("Set the server name");
}
}
} break;
@@ -82,8 +82,8 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
"</b>");
else if (change.Key == "nsfw" && change.NewValue.has_value())
extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") +
" the channel as NSFW"s);
extra_markup.emplace_back((*change.NewValue ? "Marked" : "Unmarked") +
" the channel as NSFW"s);
}
} break;
@@ -119,16 +119,16 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
"</b>");
else
extra_markup.push_back("Cleared the topic");
extra_markup.emplace_back("Cleared the topic");
} else if (change.Key == "nsfw" && change.NewValue.has_value()) {
extra_markup.push_back((*change.NewValue ? "Marked" : "Unmarked") + " the channel as NSFW"s);
extra_markup.emplace_back((*change.NewValue ? "Marked" : "Unmarked") + " the channel as NSFW"s);
} else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
const int secs = change.NewValue->get<int>();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
extra_markup.emplace_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to <b>" +
std::to_string(secs) + " seconds</b>");
extra_markup.emplace_back("Set slowmode to <b>" +
std::to_string(secs) + " seconds</b>");
}
}
} break;
@@ -186,9 +186,9 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
" pruned <b>" +
*entry.Options->MembersRemoved +
"</b> members";
extra_markup.push_back("For <b>" +
*entry.Options->DeleteMemberDays +
" days</b> of inactivity");
extra_markup.emplace_back("For <b>" +
*entry.Options->DeleteMemberDays +
" days</b> of inactivity");
} break;
case AuditLogActionType::MEMBER_BAN_ADD: {
const auto target_user = discord.GetUser(entry.TargetID);
@@ -213,13 +213,11 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
if (entry.Changes.has_value())
for (const auto &change : *entry.Changes) {
if (change.Key == "deaf" && change.NewValue.has_value())
extra_markup.push_back(
(change.NewValue->get<bool>() ? "<b>Deafened</b>"s : "<b>Undeafened</b>"s) +
" them");
extra_markup.emplace_back((change.NewValue->get<bool>() ? "<b>Deafened</b>"s : "<b>Undeafened</b>"s) +
" them");
else if (change.Key == "mute" && change.NewValue.has_value())
extra_markup.push_back(
(change.NewValue->get<bool>() ? "<b>Muted</b>"s : "<b>Unmuted</b>"s) +
" them");
extra_markup.emplace_back((change.NewValue->get<bool>() ? "<b>Muted</b>"s : "<b>Unmuted</b>"s) +
" them");
else if (change.Key == "nick" && change.NewValue.has_value())
extra_markup.push_back("Set their nickname to <b>" +
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
@@ -289,17 +287,17 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
} else if (change.Key == "color" && change.NewValue.has_value()) {
const auto col = change.NewValue->get<int>();
if (col == 0)
extra_markup.push_back("Removed the color");
extra_markup.emplace_back("Removed the color");
else
extra_markup.push_back("Set the color to <b>" +
IntToCSSColor(col) +
"</b>");
extra_markup.emplace_back("Set the color to <b>" +
IntToCSSColor(col) +
"</b>");
} else if (change.Key == "permissions") {
extra_markup.push_back("Updated the permissions");
extra_markup.emplace_back("Updated the permissions");
} else if (change.Key == "mentionable" && change.NewValue.has_value()) {
extra_markup.push_back(change.NewValue->get<bool>() ? "Mentionable" : "Not mentionable");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Mentionable" : "Not mentionable");
} else if (change.Key == "hoist" && change.NewValue.has_value()) {
extra_markup.push_back(change.NewValue->get<bool>() ? "Not hoisted" : "Hoisted");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Not hoisted" : "Hoisted");
}
}
} break;
@@ -324,13 +322,13 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
} else if (change.Key == "max_uses" && change.NewValue.has_value()) {
const auto uses = change.NewValue->get<int>();
if (uses == 0)
extra_markup.push_back("Which has <b>unlimited</b> uses");
extra_markup.emplace_back("Which has <b>unlimited</b> uses");
else
extra_markup.push_back("Which has <b>" + std::to_string(uses) + "</b> uses");
extra_markup.emplace_back("Which has <b>" + std::to_string(uses) + "</b> uses");
} else if (change.Key == "temporary" && change.NewValue.has_value()) {
extra_markup.push_back("With temporary <b>"s +
(change.NewValue->get<bool>() ? "on" : "off") +
"</b>");
extra_markup.emplace_back("With temporary <b>"s +
(change.NewValue->get<bool>() ? "on" : "off") +
"</b>");
} // no max_age cuz fuck time
}
} break;
@@ -378,7 +376,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
"</b>");
} else if (change.Key == "avatar_hash") {
extra_markup.push_back("Changed the avatar");
extra_markup.emplace_back("Changed the avatar");
} else if (change.Key == "channel_id" && change.NewValue.has_value()) {
const auto channel = discord.GetChannel(change.NewValue->get<Snowflake>());
if (channel.has_value()) {
@@ -386,7 +384,7 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(*channel->Name) +
"</b>");
} else {
extra_markup.push_back("Changed the channel");
extra_markup.emplace_back("Changed the channel");
}
}
}
@@ -552,18 +550,18 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
if (change.Key == "name")
extra_markup.push_back("Set the name to <b>" + Glib::Markup::escape_text(change.NewValue->get<std::string>()) + "</b>");
else if (change.Key == "archived")
extra_markup.push_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread");
else if (change.Key == "auto_archive_duration")
extra_markup.push_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s);
extra_markup.emplace_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s);
else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
const int secs = change.NewValue->get<int>();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
extra_markup.emplace_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to <b>" +
std::to_string(secs) + " seconds</b>");
extra_markup.emplace_back("Set slowmode to <b>" +
std::to_string(secs) + " seconds</b>");
} else if (change.Key == "locked")
extra_markup.push_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
}
}
} break;
@@ -584,19 +582,19 @@ void GuildSettingsAuditLogPane::OnAuditLogFetch(const AuditLogData &data) {
Glib::Markup::escape_text(change.NewValue->get<std::string>()) +
"</b>");
else if (change.Key == "auto_archive_duration")
extra_markup.push_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s);
extra_markup.emplace_back("Set auto archive duration to <b>"s + std::to_string(change.NewValue->get<int>()) + " minutes</b>"s);
else if (change.Key == "rate_limit_per_user" && change.NewValue.has_value()) {
const int secs = change.NewValue->get<int>();
if (secs == 0)
extra_markup.push_back("Disabled slowmode");
extra_markup.emplace_back("Disabled slowmode");
else
extra_markup.push_back("Set slowmode to <b>" +
std::to_string(secs) +
" seconds</b>");
extra_markup.emplace_back("Set slowmode to <b>" +
std::to_string(secs) +
" seconds</b>");
} else if (change.Key == "locked")
extra_markup.push_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Locked the thread, restricting it to only be unarchived by moderators" : "Unlocked the thread, allowing it to be unarchived by non-moderators");
else if (change.Key == "archived")
extra_markup.push_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread");
extra_markup.emplace_back(change.NewValue->get<bool>() ? "Archived the thread" : "Unarchived the thread");
}
} break;
case AuditLogActionType::THREAD_DELETE: {

View File

@@ -94,7 +94,7 @@ void GuildSettingsBansPane::OnMenuUnban() {
auto selected_row = *m_view.get_selection()->get_selected();
if (selected_row) {
Snowflake id = selected_row[m_columns.m_col_id];
auto cb = [this](DiscordError code) {
auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to unban user", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -119,7 +119,7 @@ bool GuildSettingsBansPane::OnTreeButtonPress(GdkEventButton *event) {
m_menu_unban.set_sensitive(can_ban);
auto selection = m_view.get_selection();
Gtk::TreeModel::Path path;
if (m_view.get_path_at_pos(event->x, event->y, path)) {
if (m_view.get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y), path)) {
m_view.get_selection()->select(path);
m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event));
}

View File

@@ -52,7 +52,7 @@ GuildSettingsEmojisPane::GuildSettingsEmojisPane(Snowflake guild_id)
m_filter->set_visible_func([this](const Gtk::TreeModel::const_iterator &iter) -> bool {
const auto text = m_search.get_text();
if (text == "") return true;
if (text.empty()) return true;
return StringContainsCaseless((*iter)[m_columns.m_col_name], text);
});
m_view.set_enable_search(false);
@@ -71,12 +71,12 @@ GuildSettingsEmojisPane::GuildSettingsEmojisPane(Snowflake guild_id)
column->pack_start(*renderer);
column->add_attribute(renderer->property_text(), m_columns.m_col_name);
renderer->property_editable() = true;
renderer->signal_edited().connect([this, renderer, column](const Glib::ustring &path, const Glib::ustring &text) {
renderer->signal_edited().connect([this](const Glib::ustring &path, const Glib::ustring &text) {
std::string new_str;
int size = 0;
for (const auto ch : text) {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_')
new_str += ch;
new_str += static_cast<char>(ch);
else if (ch == ' ')
new_str += '_';
if (++size == 32) break;
@@ -174,7 +174,7 @@ void GuildSettingsEmojisPane::OnFetchEmojis(std::vector<EmojiData> emojis) {
}
void GuildSettingsEmojisPane::OnEditName(Snowflake id, const std::string &name) {
const auto cb = [this](DiscordError code) {
const auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to set emoji name", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -197,7 +197,7 @@ void GuildSettingsEmojisPane::OnMenuDelete() {
const auto id = static_cast<Snowflake>(selected_row[m_columns.m_col_id]);
if (auto *window = dynamic_cast<Gtk::Window *>(get_toplevel()))
if (Abaddon::Get().ShowConfirm("Are you sure you want to delete " + name + "?", window)) {
const auto cb = [this](DiscordError code) {
const auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to delete emoji", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -234,7 +234,7 @@ bool GuildSettingsEmojisPane::OnTreeButtonPress(GdkEventButton *event) {
auto selection = m_view.get_selection();
Gtk::TreeModel::Path path;
if (m_view.get_path_at_pos(event->x, event->y, path)) {
if (m_view.get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y), path)) {
m_view.get_selection()->select(path);
m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event));
}

View File

@@ -3,8 +3,7 @@
#include <filesystem>
GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id)
: m_guild_icon_label("Guild icon")
, m_guild_name_label("Guild name")
: m_guild_name_label("Guild name")
, GuildID(id) {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto guild = *discord.GetGuild(id);
@@ -45,11 +44,8 @@ GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id)
m_guild_icon_ev.set_tooltip_text("Click to choose a file, right click to paste");
m_guild_icon_ev.signal_button_press_event().connect([this](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS) {
if (event->button == GDK_BUTTON_PRIMARY)
UpdateGuildIconPicker();
else if (event->button == GDK_BUTTON_SECONDARY)
UpdateGuildIconClipboard();
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
UpdateGuildIconPicker();
}
return false;
@@ -60,7 +56,7 @@ GuildSettingsInfoPane::GuildSettingsInfoPane(Snowflake id)
guild_icon_url = guild.GetIconURL("gif", "512");
else
guild_icon_url = guild.GetIconURL("png", "512");
m_guild_icon_ev.signal_button_press_event().connect([this, guild_icon_url](GdkEventButton *event) -> bool {
m_guild_icon_ev.signal_button_press_event().connect([guild_icon_url](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS)
if (event->button == GDK_BUTTON_PRIMARY)
LaunchBrowser(guild_icon_url);
@@ -114,7 +110,7 @@ void GuildSettingsInfoPane::UpdateGuildIconFromData(const std::vector<uint8_t> &
auto encoded = "data:" + mime + ";base64," + Glib::Base64::encode(std::string(data.begin(), data.end()));
auto &discord = Abaddon::Get().GetDiscordClient();
auto cb = [this](DiscordError code) {
auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to set guild icon", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -140,14 +136,12 @@ void GuildSettingsInfoPane::UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf>
}
void GuildSettingsInfoPane::UpdateGuildIconPicker() {
// this picker fucking sucks
Gtk::FileChooserDialog dlg("Choose new guild icon", Gtk::FILE_CHOOSER_ACTION_OPEN);
dlg.get_style_context()->remove_provider(Abaddon::Get().GetStyleProvider());
dlg.set_modal(true);
dlg.signal_response().connect([this, &dlg](int response) {
if (response == Gtk::RESPONSE_OK) {
auto data = ReadWholeFile(dlg.get_filename());
if (GetExtension(dlg.get_filename()) == ".gif")
auto dlg = Gtk::FileChooserNative::create("Choose new guild icon", Gtk::FILE_CHOOSER_ACTION_OPEN);
dlg->set_modal(true);
dlg->signal_response().connect([this, dlg](int response) {
if (response == Gtk::RESPONSE_ACCEPT) {
auto data = ReadWholeFile(dlg->get_filename());
if (GetExtension(dlg->get_filename()) == ".gif")
UpdateGuildIconFromData(data, "image/gif");
else
try {
@@ -160,15 +154,12 @@ void GuildSettingsInfoPane::UpdateGuildIconPicker() {
loader->write(data.data(), data.size());
loader->close();
UpdateGuildIconFromPixbuf(loader->get_pixbuf());
} catch (const std::exception &) {};
} catch (...) {}
}
});
dlg.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
auto filter_images = Gtk::FileFilter::create();
if (Abaddon::Get().GetDiscordClient().GetGuild(GuildID)->HasFeature("ANIMATED_ICON")) {
if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(GuildID); guild.has_value() && guild->HasFeature("ANIMATED_ICON")) {
filter_images->set_name("Supported images (*.jpg, *.jpeg, *.png, *.gif)");
filter_images->add_pattern("*.gif");
} else {
@@ -177,44 +168,12 @@ void GuildSettingsInfoPane::UpdateGuildIconPicker() {
filter_images->add_pattern("*.jpg");
filter_images->add_pattern("*.jpeg");
filter_images->add_pattern("*.png");
dlg.add_filter(filter_images);
dlg->add_filter(filter_images);
auto filter_all = Gtk::FileFilter::create();
filter_all->set_name("All files (*.*)");
filter_all->add_pattern("*.*");
dlg.add_filter(filter_all);
dlg->add_filter(filter_all);
dlg.run();
}
void GuildSettingsInfoPane::UpdateGuildIconClipboard() {
std::vector<uint8_t> icon_data;
auto cb = Gtk::Clipboard::get();
// query for file path then for actual image
if (cb->wait_is_text_available()) {
auto path = cb->wait_for_text();
if (!std::filesystem::exists(path.c_str())) return;
auto data = ReadWholeFile(path);
try {
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader](int inw, int inh) {
int w, h;
GetImageDimensions(inw, inh, w, h, 1024, 1024);
loader->set_size(w, h);
});
loader->write(data.data(), data.size());
loader->close();
auto pb = loader->get_pixbuf();
UpdateGuildIconFromPixbuf(pb);
return;
} catch (const std::exception &) {};
}
if (cb->wait_is_image_available()) {
auto pb = cb->wait_for_image();
UpdateGuildIconFromPixbuf(pb);
return;
}
dlg->run();
}

View File

@@ -13,9 +13,7 @@ private:
void UpdateGuildIconFromData(const std::vector<uint8_t> &data, const std::string &mime);
void UpdateGuildIconFromPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
void UpdateGuildIconPicker();
void UpdateGuildIconClipboard();
Gtk::Label m_guild_icon_label;
Gtk::EventBox m_guild_icon_ev; // necessary to make custom cursor behave properly
Gtk::Image m_guild_icon;

View File

@@ -96,7 +96,7 @@ void GuildSettingsInvitesPane::OnMenuDelete() {
auto selected_row = *m_view.get_selection()->get_selected();
if (selected_row) {
auto code = static_cast<Glib::ustring>(selected_row[m_columns.m_col_code]);
auto cb = [this](DiscordError code) {
auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to delete invite", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -115,7 +115,7 @@ bool GuildSettingsInvitesPane::OnTreeButtonPress(GdkEventButton *event) {
m_menu_delete.set_sensitive(can_manage);
auto selection = m_view.get_selection();
Gtk::TreeModel::Path path;
if (m_view.get_path_at_pos(event->x, event->y, path)) {
if (m_view.get_path_at_pos(static_cast<int>(event->x), static_cast<int>(event->y), path)) {
m_view.get_selection()->select(path);
m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event));
}

View File

@@ -64,7 +64,7 @@ GuildSettingsMembersPaneMembers::GuildSettingsMembersPaneMembers(Snowflake id)
m_list.set_filter_func([this](Gtk::ListBoxRow *row_) -> bool {
const auto search_term = m_search.get_text();
if (search_term.size() == 0) return true;
if (search_term.empty()) return true;
if (auto *row = dynamic_cast<GuildSettingsMembersListItem *>(row_))
return StringContainsCaseless(row->DisplayTerm, m_search.get_text());
return true;
@@ -208,12 +208,12 @@ void GuildSettingsMembersPaneInfo::SetUser(Snowflake user_id) {
m_id.set_text("User ID: " + std::to_string(user_id));
m_created.set_text("Account created: " + user_id.GetLocalTimestamp());
if (member.JoinedAt != "")
if (!member.JoinedAt.empty())
m_joined.set_text("Joined server: " + FormatISO8601(member.JoinedAt));
else
m_joined.set_text("Joined server: Unknown");
m_nickname.set_text("Nickname: " + member.Nickname);
m_nickname.set_visible(member.Nickname != "");
m_nickname.set_visible(!member.Nickname.empty());
if (member.PremiumSince.has_value()) {
m_boosting.set_text("Boosting since " + FormatISO8601(*member.PremiumSince));
m_boosting.show();
@@ -244,7 +244,7 @@ GuildSettingsMembersPaneRoles::GuildSettingsMembersPaneRoles(Snowflake guild_id)
}
}
m_list.set_sort_func([this](Gtk::ListBoxRow *a, Gtk::ListBoxRow *b) -> int {
m_list.set_sort_func([](Gtk::ListBoxRow *a, Gtk::ListBoxRow *b) -> int {
auto *rowa = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(a);
auto *rowb = dynamic_cast<GuildSettingsMembersPaneRolesItem *>(b);
return rowb->Position - rowa->Position;
@@ -319,8 +319,8 @@ void GuildSettingsMembersPaneRoles::OnRoleToggle(Snowflake role_id, bool new_set
// hack to prevent cb from being called if SetRoles is called before callback completion
sigc::signal<void, bool> tmp;
m_update_connection.push_back(tmp.connect(std::move(cb)));
const auto tmp_cb = [this, tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); };
m_update_connection.emplace_back(tmp.connect(std::move(cb)));
const auto tmp_cb = [tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); };
discord.SetMemberRoles(GuildID, UserID, m_set_role_ids.begin(), m_set_role_ids.end(), sigc::track_obj(tmp_cb, *this));
}

View File

@@ -61,14 +61,14 @@ GuildSettingsRolesPaneRoles::GuildSettingsRolesPaneRoles(Snowflake guild_id)
if (static_cast<size_t>(new_index) == num_rows) return true; // trying to move row below @everyone
// make sure it wont modify a neighbor role u dont have perms to modify
if (!discord.CanModifyRole(GuildID, row->RoleID)) return false;
const auto cb = [this](DiscordError code) {
const auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to set role position", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
dlg.run();
}
};
discord.ModifyRolePosition(GuildID, row->RoleID, new_pos, sigc::track_obj(cb, *this));
discord.ModifyRolePosition(GuildID, row->RoleID, static_cast<int>(new_pos), sigc::track_obj(cb, *this));
return true;
}
return false;
@@ -95,7 +95,7 @@ GuildSettingsRolesPaneRoles::GuildSettingsRolesPaneRoles(Snowflake guild_id)
}
}
m_list.set_sort_func([this](Gtk::ListBoxRow *rowa_, Gtk::ListBoxRow *rowb_) -> int {
m_list.set_sort_func([](Gtk::ListBoxRow *rowa_, Gtk::ListBoxRow *rowb_) -> int {
auto *rowa = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(rowa_);
auto *rowb = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(rowb_);
return rowb->Position - rowa->Position;
@@ -104,7 +104,7 @@ GuildSettingsRolesPaneRoles::GuildSettingsRolesPaneRoles(Snowflake guild_id)
m_list.set_filter_func([this](Gtk::ListBoxRow *row_) -> bool {
const auto search_term = m_search.get_text();
if (search_term.size() == 0) return true;
if (search_term.empty()) return true;
if (auto *row = dynamic_cast<GuildSettingsRolesPaneRolesListItem *>(row_))
return StringContainsCaseless(row->DisplayTerm, m_search.get_text());
return true;
@@ -380,8 +380,8 @@ void GuildSettingsRolesPaneInfo::OnPermissionToggle(Permission perm, bool new_se
m_perms &= ~perm;
sigc::signal<void, bool> tmp;
m_update_connections.push_back(tmp.connect(std::move(cb)));
const auto tmp_cb = [this, tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); };
m_update_connections.emplace_back(tmp.connect(std::move(cb)));
const auto tmp_cb = [tmp = std::move(tmp)](DiscordError code) { tmp.emit(code == DiscordError::NONE); };
discord.ModifyRolePermissions(GuildID, RoleID, m_perms, sigc::track_obj(tmp_cb, *this));
}

View File

@@ -12,113 +12,6 @@ MainWindow::MainWindow()
add_accel_group(m_accels);
m_menu_discord.set_label("Discord");
m_menu_discord.set_submenu(m_menu_discord_sub);
m_menu_discord_connect.set_label("Connect");
m_menu_discord_connect.set_sensitive(false);
m_menu_discord_disconnect.set_label("Disconnect");
m_menu_discord_disconnect.set_sensitive(false);
m_menu_discord_set_token.set_label("Set Token");
m_menu_discord_join_guild.set_label("Accept Invite");
m_menu_discord_join_guild.set_sensitive(false);
m_menu_discord_set_status.set_label("Set Status");
m_menu_discord_set_status.set_sensitive(false);
m_menu_discord_add_recipient.set_label("Add user to DM");
m_menu_discord_sub.append(m_menu_discord_connect);
m_menu_discord_sub.append(m_menu_discord_disconnect);
m_menu_discord_sub.append(m_menu_discord_set_token);
m_menu_discord_sub.append(m_menu_discord_join_guild);
m_menu_discord_sub.append(m_menu_discord_set_status);
m_menu_discord_sub.append(m_menu_discord_add_recipient);
m_menu_discord_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnDiscordSubmenuPopup)); // this gets called twice for some reason
m_menu_discord.set_submenu(m_menu_discord_sub);
m_menu_file.set_label("File");
m_menu_file.set_submenu(m_menu_file_sub);
m_menu_file_reload_css.set_label("Reload CSS");
m_menu_file_clear_cache.set_label("Clear file cache");
m_menu_file_sub.append(m_menu_file_reload_css);
m_menu_file_sub.append(m_menu_file_clear_cache);
m_menu_view.set_label("View");
m_menu_view.set_submenu(m_menu_view_sub);
m_menu_view_friends.set_label("Friends");
m_menu_view_pins.set_label("Pins");
m_menu_view_threads.set_label("Threads");
m_menu_view_mark_guild_as_read.set_label("Mark Server as Read");
m_menu_view_mark_guild_as_read.add_accelerator("activate", m_accels, GDK_KEY_Escape, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
m_menu_view_mark_all_as_read.set_label("Mark All as Read");
m_menu_view_sub.append(m_menu_view_friends);
m_menu_view_sub.append(m_menu_view_pins);
m_menu_view_sub.append(m_menu_view_threads);
m_menu_view_sub.append(m_menu_view_mark_guild_as_read);
m_menu_view_sub.append(m_menu_view_mark_all_as_read);
m_menu_view_sub.signal_popped_up().connect(sigc::mem_fun(*this, &MainWindow::OnViewSubmenuPopup));
m_menu_bar.append(m_menu_file);
m_menu_bar.append(m_menu_discord);
m_menu_bar.append(m_menu_view);
m_menu_bar.show_all();
m_menu_discord_connect.signal_activate().connect([this] {
m_signal_action_connect.emit();
});
m_menu_discord_disconnect.signal_activate().connect([this] {
m_signal_action_disconnect.emit();
});
m_menu_discord_set_token.signal_activate().connect([this] {
m_signal_action_set_token.emit();
});
m_menu_discord_join_guild.signal_activate().connect([this] {
m_signal_action_join_guild.emit();
});
m_menu_file_reload_css.signal_activate().connect([this] {
m_signal_action_reload_css.emit();
});
m_menu_discord_set_status.signal_activate().connect([this] {
m_signal_action_set_status.emit();
});
m_menu_file_clear_cache.signal_activate().connect([this] {
Abaddon::Get().GetImageManager().ClearCache();
});
m_menu_discord_add_recipient.signal_activate().connect([this] {
m_signal_action_add_recipient.emit(GetChatActiveChannel());
});
m_menu_view_friends.signal_activate().connect([this] {
UpdateChatActiveChannel(Snowflake::Invalid);
m_members.UpdateMemberList();
m_content_stack.set_visible_child("friends");
});
m_menu_view_pins.signal_activate().connect([this] {
m_signal_action_view_pins.emit(GetChatActiveChannel());
});
m_menu_view_threads.signal_activate().connect([this] {
m_signal_action_view_threads.emit(GetChatActiveChannel());
});
m_menu_view_mark_guild_as_read.signal_activate().connect([this] {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto channel_id = GetChatActiveChannel();
const auto channel = discord.GetChannel(channel_id);
if (channel.has_value() && channel->GuildID.has_value()) {
discord.MarkGuildAsRead(*channel->GuildID, NOOP_CALLBACK);
}
});
m_menu_view_mark_all_as_read.signal_activate().connect([this] {
Abaddon::Get().GetDiscordClient().MarkAllAsRead(NOOP_CALLBACK);
});
m_content_box.set_hexpand(true);
m_content_box.set_vexpand(true);
m_content_box.show();
@@ -171,6 +64,8 @@ MainWindow::MainWindow()
m_content_members_paned.show();
add(m_main_box);
SetupMenu();
}
void MainWindow::UpdateComponents() {
@@ -231,7 +126,7 @@ void MainWindow::UpdateChatPrependHistory(const std::vector<Message> &msgs) {
m_chat.AddNewHistory(msgs); // given vector should be sorted ascending
}
void MainWindow::InsertChatInput(std::string text) {
void MainWindow::InsertChatInput(const std::string &text) {
m_chat.InsertChatInput(text);
}
@@ -247,7 +142,7 @@ void MainWindow::UpdateChatReactionRemove(Snowflake id, const Glib::ustring &par
m_chat.UpdateReactions(id);
}
void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void MainWindow::OnDiscordSubmenuPopup() {
auto &discord = Abaddon::Get().GetDiscordClient();
auto channel_id = GetChatActiveChannel();
m_menu_discord_add_recipient.set_visible(false);
@@ -260,20 +155,19 @@ void MainWindow::OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const
const bool discord_active = Abaddon::Get().GetDiscordClient().IsStarted();
std::string token = Abaddon::Get().GetDiscordToken();
m_menu_discord_connect.set_sensitive(token.size() > 0 && !discord_active);
m_menu_discord_connect.set_sensitive(!token.empty() && !discord_active);
m_menu_discord_disconnect.set_sensitive(discord_active);
m_menu_discord_join_guild.set_sensitive(discord_active);
m_menu_discord_set_token.set_sensitive(!discord_active);
m_menu_discord_set_status.set_sensitive(discord_active);
}
void MainWindow::OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y) {
void MainWindow::OnViewSubmenuPopup() {
auto &discord = Abaddon::Get().GetDiscordClient();
const bool discord_active = discord.IsStarted();
m_menu_view_friends.set_sensitive(discord_active);
m_menu_view_mark_guild_as_read.set_sensitive(discord_active);
m_menu_view_mark_all_as_read.set_sensitive(discord_active);
auto channel_id = GetChatActiveChannel();
m_menu_view_pins.set_sensitive(false);
@@ -298,6 +192,113 @@ MemberList *MainWindow::GetMemberList() {
return &m_members;
}
void MainWindow::SetupMenu() {
m_menu_discord.set_label("Discord");
m_menu_discord.set_submenu(m_menu_discord_sub);
m_menu_discord_connect.set_label("Connect");
m_menu_discord_connect.set_sensitive(false);
m_menu_discord_disconnect.set_label("Disconnect");
m_menu_discord_disconnect.set_sensitive(false);
m_menu_discord_set_token.set_label("Set Token");
m_menu_discord_join_guild.set_label("Accept Invite");
m_menu_discord_join_guild.set_sensitive(false);
m_menu_discord_set_status.set_label("Set Status");
m_menu_discord_set_status.set_sensitive(false);
m_menu_discord_add_recipient.set_label("Add user to DM");
m_menu_discord_sub.append(m_menu_discord_connect);
m_menu_discord_sub.append(m_menu_discord_disconnect);
m_menu_discord_sub.append(m_menu_discord_set_token);
m_menu_discord_sub.append(m_menu_discord_join_guild);
m_menu_discord_sub.append(m_menu_discord_set_status);
m_menu_discord_sub.append(m_menu_discord_add_recipient);
m_menu_discord.set_submenu(m_menu_discord_sub);
m_menu_file.set_label("File");
m_menu_file.set_submenu(m_menu_file_sub);
m_menu_file_reload_css.set_label("Reload CSS");
m_menu_file_clear_cache.set_label("Clear file cache");
m_menu_file_sub.append(m_menu_file_reload_css);
m_menu_file_sub.append(m_menu_file_clear_cache);
m_menu_view.set_label("View");
m_menu_view.set_submenu(m_menu_view_sub);
m_menu_view_friends.set_label("Friends");
m_menu_view_pins.set_label("Pins");
m_menu_view_threads.set_label("Threads");
m_menu_view_mark_guild_as_read.set_label("Mark Server as Read");
m_menu_view_mark_guild_as_read.add_accelerator("activate", m_accels, GDK_KEY_Escape, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
m_menu_view_sub.append(m_menu_view_friends);
m_menu_view_sub.append(m_menu_view_pins);
m_menu_view_sub.append(m_menu_view_threads);
m_menu_view_sub.append(m_menu_view_mark_guild_as_read);
m_menu_bar.append(m_menu_file);
m_menu_bar.append(m_menu_discord);
m_menu_bar.append(m_menu_view);
m_menu_bar.show_all();
m_menu_bar.signal_event().connect([this](GdkEvent *ev) -> bool {
OnViewSubmenuPopup();
OnDiscordSubmenuPopup();
return false;
});
m_menu_discord_connect.signal_activate().connect([this] {
m_signal_action_connect.emit();
});
m_menu_discord_disconnect.signal_activate().connect([this] {
m_signal_action_disconnect.emit();
});
m_menu_discord_set_token.signal_activate().connect([this] {
m_signal_action_set_token.emit();
});
m_menu_discord_join_guild.signal_activate().connect([this] {
m_signal_action_join_guild.emit();
});
m_menu_file_reload_css.signal_activate().connect([this] {
m_signal_action_reload_css.emit();
});
m_menu_discord_set_status.signal_activate().connect([this] {
m_signal_action_set_status.emit();
});
m_menu_file_clear_cache.signal_activate().connect([] {
Abaddon::Get().GetImageManager().ClearCache();
});
m_menu_discord_add_recipient.signal_activate().connect([this] {
m_signal_action_add_recipient.emit(GetChatActiveChannel());
});
m_menu_view_friends.signal_activate().connect([this] {
UpdateChatActiveChannel(Snowflake::Invalid);
m_members.UpdateMemberList();
m_content_stack.set_visible_child("friends");
});
m_menu_view_pins.signal_activate().connect([this] {
m_signal_action_view_pins.emit(GetChatActiveChannel());
});
m_menu_view_threads.signal_activate().connect([this] {
m_signal_action_view_threads.emit(GetChatActiveChannel());
});
m_menu_view_mark_guild_as_read.signal_activate().connect([this] {
auto &discord = Abaddon::Get().GetDiscordClient();
const auto channel_id = GetChatActiveChannel();
const auto channel = discord.GetChannel(channel_id);
if (channel.has_value() && channel->GuildID.has_value()) {
discord.MarkGuildAsRead(*channel->GuildID, NOOP_CALLBACK);
}
});
}
MainWindow::type_signal_action_connect MainWindow::signal_action_connect() {
return m_signal_action_connect;
}

View File

@@ -19,7 +19,7 @@ public:
void UpdateChatMessageDeleted(Snowflake id, Snowflake channel_id);
void UpdateChatMessageUpdated(Snowflake id, Snowflake channel_id);
void UpdateChatPrependHistory(const std::vector<Message> &msgs);
void InsertChatInput(std::string text);
void InsertChatInput(const std::string &text);
Snowflake GetChatOldestListedMessage();
void UpdateChatReactionAdd(Snowflake id, const Glib::ustring &param);
void UpdateChatReactionRemove(Snowflake id, const Glib::ustring &param);
@@ -28,40 +28,9 @@ public:
ChatWindow *GetChatWindow();
MemberList *GetMemberList();
public:
typedef sigc::signal<void> type_signal_action_connect;
typedef sigc::signal<void> type_signal_action_disconnect;
typedef sigc::signal<void> type_signal_action_set_token;
typedef sigc::signal<void> type_signal_action_reload_css;
typedef sigc::signal<void> type_signal_action_join_guild;
typedef sigc::signal<void> type_signal_action_set_status;
// this should probably be removed
typedef sigc::signal<void, Snowflake> type_signal_action_add_recipient; // channel id
typedef sigc::signal<void, Snowflake> type_signal_action_view_pins; // channel id
typedef sigc::signal<void, Snowflake> type_signal_action_view_threads; // channel id
private:
void SetupMenu();
type_signal_action_connect signal_action_connect();
type_signal_action_disconnect signal_action_disconnect();
type_signal_action_set_token signal_action_set_token();
type_signal_action_reload_css signal_action_reload_css();
type_signal_action_join_guild signal_action_join_guild();
type_signal_action_set_status signal_action_set_status();
type_signal_action_add_recipient signal_action_add_recipient();
type_signal_action_view_pins signal_action_view_pins();
type_signal_action_view_threads signal_action_view_threads();
protected:
type_signal_action_connect m_signal_action_connect;
type_signal_action_disconnect m_signal_action_disconnect;
type_signal_action_set_token m_signal_action_set_token;
type_signal_action_reload_css m_signal_action_reload_css;
type_signal_action_join_guild m_signal_action_join_guild;
type_signal_action_set_status m_signal_action_set_status;
type_signal_action_add_recipient m_signal_action_add_recipient;
type_signal_action_view_pins m_signal_action_view_pins;
type_signal_action_view_threads m_signal_action_view_threads;
protected:
Gtk::Box m_main_box;
Gtk::Box m_content_box;
Gtk::Paned m_chan_content_paned;
@@ -85,7 +54,7 @@ protected:
Gtk::MenuItem m_menu_discord_join_guild;
Gtk::MenuItem m_menu_discord_set_status;
Gtk::MenuItem m_menu_discord_add_recipient; // move me somewhere else some day
void OnDiscordSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnDiscordSubmenuPopup();
Gtk::MenuItem m_menu_file;
Gtk::Menu m_menu_file_sub;
@@ -98,6 +67,38 @@ protected:
Gtk::MenuItem m_menu_view_pins;
Gtk::MenuItem m_menu_view_threads;
Gtk::MenuItem m_menu_view_mark_guild_as_read;
Gtk::MenuItem m_menu_view_mark_all_as_read;
void OnViewSubmenuPopup(const Gdk::Rectangle *flipped_rect, const Gdk::Rectangle *final_rect, bool flipped_x, bool flipped_y);
void OnViewSubmenuPopup();
public:
typedef sigc::signal<void> type_signal_action_connect;
typedef sigc::signal<void> type_signal_action_disconnect;
typedef sigc::signal<void> type_signal_action_set_token;
typedef sigc::signal<void> type_signal_action_reload_css;
typedef sigc::signal<void> type_signal_action_join_guild;
typedef sigc::signal<void> type_signal_action_set_status;
// this should probably be removed
typedef sigc::signal<void, Snowflake> type_signal_action_add_recipient; // channel id
typedef sigc::signal<void, Snowflake> type_signal_action_view_pins; // channel id
typedef sigc::signal<void, Snowflake> type_signal_action_view_threads; // channel id
type_signal_action_connect signal_action_connect();
type_signal_action_disconnect signal_action_disconnect();
type_signal_action_set_token signal_action_set_token();
type_signal_action_reload_css signal_action_reload_css();
type_signal_action_join_guild signal_action_join_guild();
type_signal_action_set_status signal_action_set_status();
type_signal_action_add_recipient signal_action_add_recipient();
type_signal_action_view_pins signal_action_view_pins();
type_signal_action_view_threads signal_action_view_threads();
private:
type_signal_action_connect m_signal_action_connect;
type_signal_action_disconnect m_signal_action_disconnect;
type_signal_action_set_token m_signal_action_set_token;
type_signal_action_reload_css m_signal_action_reload_css;
type_signal_action_join_guild m_signal_action_join_guild;
type_signal_action_set_status m_signal_action_set_status;
type_signal_action_add_recipient m_signal_action_add_recipient;
type_signal_action_view_pins m_signal_action_view_pins;
type_signal_action_view_threads m_signal_action_view_threads;
};

View File

@@ -39,8 +39,8 @@ ConnectionItem::ConnectionItem(const ConnectionData &conn)
m_name.set_single_line_mode(true);
m_name.set_ellipsize(Pango::ELLIPSIZE_END);
m_box.add(m_name);
if (url != "") {
auto cb = [this, url](GdkEventButton *event) -> bool {
if (!url.empty()) {
auto cb = [url](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
LaunchBrowser(url);
return true;
@@ -102,7 +102,7 @@ void ConnectionsContainer::SetConnections(const std::vector<ConnectionData> &con
if (supported_services.find(conn.Type) == supported_services.end()) continue;
auto widget = Gtk::manage(new ConnectionItem(conn));
widget->show();
attach(*widget, i % 2, i / 2, 1, 1);
attach(*widget, static_cast<int>(i % 2), static_cast<int>(i / 2), 1, 1);
}
set_halign(Gtk::ALIGN_FILL);
@@ -185,7 +185,7 @@ ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID)
m_created.get_style_context()->add_class("profile-info-created");
m_note.signal_update_note().connect([this](const Glib::ustring &note) {
auto cb = [this](DiscordError code) {
auto cb = [](DiscordError code) {
if (code != DiscordError::NONE) {
Gtk::MessageDialog dlg("Failed to set note", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
dlg.set_position(Gtk::WIN_POS_CENTER);
@@ -196,7 +196,7 @@ ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID)
});
auto &discord = Abaddon::Get().GetDiscordClient();
auto note_update_cb = [this](Snowflake id, std::string note) {
auto note_update_cb = [this](Snowflake id, const std::string &note) {
if (id == UserID)
m_note.SetNote(note);
};
@@ -225,7 +225,7 @@ ProfileUserInfoPane::ProfileUserInfoPane(Snowflake ID)
}
void ProfileUserInfoPane::SetProfile(const UserProfileData &data) {
if (data.User.Bio.has_value() && *data.User.Bio != "") {
if (data.User.Bio.has_value() && !data.User.Bio->empty()) {
m_bio.SetBio(*data.User.Bio);
m_bio.show();
} else {

View File

@@ -34,7 +34,7 @@ ProfileWindow::ProfileWindow(Snowflake user_id)
if (user.HasAvatar())
AddPointerCursor(m_avatar_ev);
m_avatar_ev.signal_button_press_event().connect([this, user](GdkEventButton *event) -> bool {
m_avatar_ev.signal_button_press_event().connect([user](GdkEventButton *event) -> bool {
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) {
if (user.HasAnimatedAvatar())
LaunchBrowser(user.GetAvatarURL("gif", "512"));

View File

@@ -62,7 +62,7 @@ ThreadsWindow::ThreadsWindow(const ChannelData &channel)
add(m_box);
}
bool ThreadsWindow::ListFilterFunc(Gtk::ListBoxRow *row_) {
bool ThreadsWindow::ListFilterFunc(Gtk::ListBoxRow *row_) const {
if (auto *row = dynamic_cast<ThreadListRow *>(row_))
return (m_filter_mode == FILTER_PUBLIC && (row->Type == ChannelType::GUILD_PUBLIC_THREAD || row->Type == ChannelType::GUILD_NEWS_THREAD)) ||
(m_filter_mode == FILTER_PRIVATE && row->Type == ChannelType::GUILD_PRIVATE_THREAD);

View File

@@ -43,7 +43,7 @@ public:
private:
// this filtering is rather cringe but idk what a better alternative would be
bool ListFilterFunc(Gtk::ListBoxRow *row_);
bool ListFilterFunc(Gtk::ListBoxRow *row_) const;
enum FilterMode {
FILTER_PUBLIC = 0,