diff --git a/CMakeLists.txt b/CMakeLists.txt index cf44f0a..3d6f73f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ file(GLOB_RECURSE ABADDON_SOURCES "src/*.cpp" ) +list(FILTER ABADDON_SOURCES EXCLUDE REGEX ".*notifier_gio\\.cpp$") + add_executable(abaddon ${ABADDON_SOURCES}) target_include_directories(abaddon PUBLIC ${PROJECT_SOURCE_DIR}/src) target_include_directories(abaddon PUBLIC ${PROJECT_BINARY_DIR}) @@ -64,6 +66,10 @@ if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR target_link_libraries(abaddon stdc++fs) endif () +if (NOT WIN32) + target_sources(abaddon PRIVATE src/notifications/notifier_gio.cpp) +endif () + if (IXWebSocket_LIBRARIES) target_link_libraries(abaddon ${IXWebSocket_LIBRARIES}) find_library(MBEDTLS_X509_LIBRARY mbedx509) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index ba553bb..4daa53c 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -15,6 +15,7 @@ #include "windows/pinnedwindow.hpp" #include "windows/threadswindow.hpp" #include "startup.hpp" +#include "notifications/notifications.hpp" #ifdef WITH_LIBHANDY #include @@ -272,6 +273,14 @@ int Abaddon::StartGTK() { m_main_window->UpdateMenus(); + auto action_go_to_channel = Gio::SimpleAction::create("go-to-channel", Glib::VariantType("s")); + action_go_to_channel->signal_activate().connect([this](const Glib::VariantBase ¶m) { + const auto id_str = Glib::VariantBase::cast_dynamic>(param); + const Snowflake id = id_str.get(); + ActionChannelOpened(id, false); + }); + m_gtk_app->add_action(action_go_to_channel); + m_gtk_app->hold(); m_main_window->show(); @@ -329,6 +338,7 @@ void Abaddon::DiscordOnReady() { void Abaddon::DiscordOnMessageCreate(const Message &message) { m_main_window->UpdateChatNewMessage(message); + m_notifications.CheckMessage(message); } void Abaddon::DiscordOnMessageDelete(Snowflake id, Snowflake channel_id) { @@ -682,6 +692,10 @@ std::string Abaddon::GetStateCachePath(const std::string &path) { return GetStateCachePath() + path; } +Glib::RefPtr Abaddon::GetApp() { + return m_gtk_app; +} + void Abaddon::ActionConnect() { if (!m_discord.IsStarted()) StartDiscord(); diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 8b14699..56f5e04 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -3,11 +3,15 @@ #include #include #include +#include +#include +#include #include "discord/discord.hpp" #include "windows/mainwindow.hpp" #include "settings.hpp" #include "imgmanager.hpp" #include "emojis.hpp" +#include "notifications/notifications.hpp" #define APP_TITLE "Abaddon" @@ -92,6 +96,8 @@ public: static std::string GetResPath(const std::string &path); static std::string GetStateCachePath(const std::string &path); + [[nodiscard]] Glib::RefPtr GetApp(); + protected: void RunFirstTimeDiscordStartup(); @@ -149,4 +155,6 @@ private: Glib::RefPtr m_css_low_provider; // registered with a lower priority to allow better customization Glib::RefPtr m_tray; std::unique_ptr m_main_window; // wah wah cant create a gtkstylecontext fuck you + + Notifications m_notifications; }; diff --git a/src/notifications/notifications.cpp b/src/notifications/notifications.cpp new file mode 100644 index 0000000..ed4a879 --- /dev/null +++ b/src/notifications/notifications.cpp @@ -0,0 +1,33 @@ +#include "notifications.hpp" +#include "discord/message.hpp" + +Notifications::Notifications() { +} + +void Notifications::CheckMessage(const Message &message) { + // ignore if our status is do not disturb + if (IsDND()) return; + auto &discord = Abaddon::Get().GetDiscordClient(); + // ignore if the channel is muted + if (discord.IsChannelMuted(message.ChannelID)) return; + // notify messages in DMs + const auto channel = discord.GetChannel(message.ChannelID); + if (channel->IsDM()) { + NotifyMessage(message); + } +} + +void Notifications::NotifyMessage(const Message &message) { + Glib::ustring default_action = "app.go-to-channel"; + default_action += "::"; + default_action += std::to_string(message.ChannelID); + const auto title = message.Author.Username; + const auto body = message.Content; + m_notifier.Notify(title, body, default_action); +} + +bool Notifications::IsDND() const { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto status = discord.GetUserStatus(discord.GetUserData().ID); + return status == PresenceStatus::DND; +} diff --git a/src/notifications/notifications.hpp b/src/notifications/notifications.hpp new file mode 100644 index 0000000..fb71349 --- /dev/null +++ b/src/notifications/notifications.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "notifier.hpp" + +class Message; + +class Notifications { +public: + Notifications(); + + void CheckMessage(const Message &message); + +private: + void NotifyMessage(const Message &message); + + [[nodiscard]] bool IsDND() const; + + Notifier m_notifier; +}; diff --git a/src/notifications/notifier.hpp b/src/notifications/notifier.hpp new file mode 100644 index 0000000..48e881f --- /dev/null +++ b/src/notifications/notifier.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +class Notifier { +public: + Notifier(); + + void Notify(const Glib::ustring &title, const Glib::ustring &text, const Glib::ustring &default_action); +}; diff --git a/src/notifications/notifier_gio.cpp b/src/notifications/notifier_gio.cpp new file mode 100644 index 0000000..6106114 --- /dev/null +++ b/src/notifications/notifier_gio.cpp @@ -0,0 +1,11 @@ +#include "notifier.hpp" +#include + +Notifier::Notifier() {} + +void Notifier::Notify(const Glib::ustring &title, const Glib::ustring &text, const Glib::ustring &default_action) { + auto n = Gio::Notification::create(title); + n->set_body(text); + n->set_default_action(default_action); + Abaddon::Get().GetApp()->send_notification(n); +}