diff --git a/plugins/windows-notification/CMakeLists.txt b/plugins/windows-notification/CMakeLists.txt index 83b94dff..eacc8405 100644 --- a/plugins/windows-notification/CMakeLists.txt +++ b/plugins/windows-notification/CMakeLists.txt @@ -1,3 +1,8 @@ +set(GETTEXT_PACKAGE "dino-windows-notifications") +find_package(Gettext) +include(${GETTEXT_USE_FILE}) +gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../main/po TARGET_NAME ${GETTEXT_PACKAGE}-translations) + find_packages(WINDOWS_NOTIFICATION_PACKAGES REQUIRED Gee GLib @@ -10,8 +15,7 @@ vala_precompile(WINDOWS_NOTIFICATION_VALA_C SOURCES src/plugin.vala src/register_plugin.vala - src/helper.vala - src/wintoast.vala + src/win_notification_provider.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi @@ -49,8 +53,9 @@ COMMENT Copy header file DinoWinToastDllExport.h ) -add_definitions(${VALA_CFLAGS}) +add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\") add_library(windows-notification SHARED ${WINDOWS_NOTIFICATION_VALA_C} ${CMAKE_BINARY_DIR}/exports/DinoWinToastLib.h ${CMAKE_BINARY_DIR}/exports/DinoWinToastTemplate.h ${CMAKE_BINARY_DIR}/exports/DinoWinToastDllExport.h) +add_dependencies(omemo ${GETTEXT_PACKAGE}-translations) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(WINTOASTLIB "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastLib_AMD64.lib") diff --git a/plugins/windows-notification/src/helper.vala b/plugins/windows-notification/src/helper.vala deleted file mode 100644 index 40b4bf2b..00000000 --- a/plugins/windows-notification/src/helper.vala +++ /dev/null @@ -1,113 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; -using Xmpp; - -namespace Dino.Ui.Util { - -private static Jid get_relevant_jid(StreamInteractor stream_interactor, Account account, Jid jid, Conversation? conversation = null) { - Conversation conversation_ = conversation ?? stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, account); - if (conversation_ != null && conversation_.type_ == Conversation.Type.GROUPCHAT) { - Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, account); - if (real_jid != null) { - return real_jid.bare_jid; - } - } else { - return jid.bare_jid; - } - return jid; -} - -public static string get_conversation_display_name(StreamInteractor stream_interactor, Conversation conversation) { - if (conversation.type_ == Conversation.Type.CHAT) { - string? display_name = get_real_display_name(stream_interactor, conversation.account, conversation.counterpart); - if (display_name != null) return display_name; - return conversation.counterpart.to_string(); - } - if (conversation.type_ == Conversation.Type.GROUPCHAT) { - return get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart); - } - if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { - return "%s from %s".printf(get_occupant_display_name(stream_interactor, conversation, conversation.counterpart), get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart.bare_jid)); - } - return conversation.counterpart.to_string(); -} - -public static string get_participant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid participant, bool me_is_me = false) { - if (me_is_me) { - if (conversation.account.bare_jid.equals_bare(participant) || - (conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM) && - conversation.nickname != null && participant.equals_bare(conversation.counterpart) && conversation.nickname == participant.resourcepart) { - return "Me"; - } - } - if (conversation.type_ == Conversation.Type.CHAT) { - return get_real_display_name(stream_interactor, conversation.account, participant, me_is_me) ?? participant.bare_jid.to_string(); - } - if ((conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM) && conversation.counterpart.equals_bare(participant)) { - return get_occupant_display_name(stream_interactor, conversation, participant); - } - return participant.bare_jid.to_string(); -} - -private static string? get_real_display_name(StreamInteractor stream_interactor, Account account, Jid jid, bool me_is_me = false) { - if (jid.equals_bare(account.bare_jid)) { - if (me_is_me || account.alias == null || account.alias.length == 0) { - return "Me"; - } - return account.alias; - } - Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid); - if (roster_item != null && roster_item.name != null && roster_item.name != "") { - return roster_item.name; - } - return null; -} - -private static string get_groupchat_display_name(StreamInteractor stream_interactor, Account account, Jid jid) { - MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); - string room_name = muc_manager.get_room_name(account, jid); - if (room_name != null && room_name != jid.localpart) { - return room_name; - } - if (muc_manager.is_private_room(account, jid)) { - Gee.List? other_occupants = muc_manager.get_other_offline_members(jid, account); - if (other_occupants != null && other_occupants.size > 0) { - var builder = new StringBuilder (); - foreach(Jid occupant in other_occupants) { - if (builder.len != 0) { - builder.append(", "); - } - builder.append((get_real_display_name(stream_interactor, account, occupant) ?? occupant.localpart ?? occupant.domainpart).split(" ")[0]); - } - return builder.str; - } - } - return jid.to_string(); -} - -private static string get_occupant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid jid, bool me_is_me = false, bool muc_real_name = false) { - if (muc_real_name) { - MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); - if (muc_manager.is_private_room(conversation.account, jid.bare_jid)) { - Jid? real_jid = muc_manager.get_real_jid(jid, conversation.account); - if (real_jid != null) { - string? display_name = get_real_display_name(stream_interactor, conversation.account, real_jid, me_is_me); - if (display_name != null) return display_name; - } - } - } - - // If it's us (jid=our real full JID), display our nick - if (conversation.type_ == Conversation.Type.GROUPCHAT_PM && conversation.account.bare_jid.equals_bare(jid)) { - var muc_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conversation.counterpart.bare_jid, conversation.account, Conversation.Type.GROUPCHAT); - if (muc_conv != null && muc_conv.nickname != null) { - return muc_conv.nickname; - } - } - - return jid.resourcepart ?? jid.to_string(); -} - -} diff --git a/plugins/windows-notification/src/plugin.vala b/plugins/windows-notification/src/plugin.vala index 428f462d..a0f335af 100644 --- a/plugins/windows-notification/src/plugin.vala +++ b/plugins/windows-notification/src/plugin.vala @@ -4,64 +4,14 @@ using Dino.Entities; namespace Dino.Plugins.WindowsNotification { public class Plugin : RootInterface, Object { - private Dino.Application app; - private ulong signal_handler = 0; - private WinToast toaster; - - private void onclick_callback(int conv_id) { - this.app.activate_action("open-conversation", conv_id); - } - public void registered(Dino.Application app) { - this.app = app; - this.toaster = new WinToast(); - if (toaster.valid) { - signal_handler = app.stream_interactor.get_module(NotificationEvents.IDENTITY).notify_content_item.connect(on_notify); + var provider = WindowsNotificationProvider.try_create(app); + if (provider != null) { + app.stream_interactor.get_module(NotificationEvents.IDENTITY).register_notification_provider(provider); } } public void shutdown() { - if (signal_handler > 0) { - app.stream_interactor.get_module(NotificationEvents.IDENTITY).notify_content_item.disconnect(on_notify); - } - } - - private void on_notify(ContentItem content_item, Conversation conversation) { - string display_name = Dino.Ui.Util.get_conversation_display_name(app.stream_interactor, conversation); - string text = ""; - switch (content_item.type_) { - case MessageItem.TYPE: - var message_item = (content_item as MessageItem); - if (message_item != null) { - Message message = message_item.message; - if (message != null) { - text = message.body; - } - } - break; - case FileItem.TYPE: - FileItem file_item = content_item as FileItem; - if (file_item != null) { - FileTransfer transfer = file_item.file_transfer; - - bool file_is_image = transfer.mime_type != null && transfer.mime_type.has_prefix("image"); - if (transfer.direction == Message.DIRECTION_SENT) { - text = file_is_image ? "Image sent" : "File sent"; - } else { - text = file_is_image ? "Image received" : "File received"; - } - } - break; - } - if (app.stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(conversation.counterpart, conversation.account)) { - string muc_occupant = Dino.Ui.Util.get_participant_display_name(app.stream_interactor, conversation, content_item.jid); - text = @"$muc_occupant: $text"; - } - var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY); - var avatar = avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart); - if (!toaster.show_message(display_name, text, avatar, conversation.id, onclick_callback)) { - stderr.printf("Error sending notification."); - }; } } diff --git a/plugins/windows-notification/src/win_notification_provider.vala b/plugins/windows-notification/src/win_notification_provider.vala new file mode 100644 index 00000000..9563ee88 --- /dev/null +++ b/plugins/windows-notification/src/win_notification_provider.vala @@ -0,0 +1,226 @@ +using Dino; +using Dino.Entities; +using DinoWinToast; +using Xmpp; + +namespace Dino.Plugins.WindowsNotification { + public class WindowsNotificationProvider : NotificationProvider, Object { + + // TODO: + // 1. Actions + // 2. Dismissed + + private StreamInteractor stream_interactor; + private Dino.Application app; + + private WindowsNotificationProvider(Dino.Application app) { + this.stream_interactor = app.stream_interactor; + this.app = app; + } + + public static WindowsNotificationProvider? try_create(Dino.Application app) { + var valid = Init() == 0; + if (valid) { + return new WindowsNotificationProvider(app); + } + warning("Unable to initialize Windows notification provider"); + return null; + } + + public double get_priority() { + return 2; + } + + public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) { + yield notify_content_item(conversation, conversation_display_name, participant_display_name, message.body); + } + + public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) { + string text = ""; + if (file_transfer.direction == Message.DIRECTION_SENT) { + text = is_image ? _("Image sent") : _("File sent"); + } else { + text = is_image ? _("Image received") : _("File received"); + } + + yield notify_content_item(conversation, conversation_display_name, participant_display_name, text); + } + + public async void notify_subscription_request(Conversation conversation) { + string summary = _("Subscription request"); + string body = Markup.escape_text(conversation.counterpart.to_string()); + + if (!show_message(summary, body, get_avatar(conversation), conversation.id, stub)) { // missing actions + warning("Failed showing subscription request notification"); + } + + // HashTable hash_table = new HashTable(null, null); + // hash_table["image-data"] = yield get_conversation_icon(conversation); + // string[] actions = new string[] {"default", "Open conversation", "accept", _("Accept"), "deny", _("Deny")}; + // try { + // uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0); + + // if (!conversation_notifications.has_key(conversation)) { + // conversation_notifications[conversation] = new ArrayList(); + // } + // conversation_notifications[conversation].add(notification_id); + + // add_action_listener(notification_id, "default", () => { + // GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); + // }); + // add_action_listener(notification_id, "accept", () => { + // GLib.Application.get_default().activate_action("accept-subscription", new Variant.int32(conversation.id)); + // }); + // add_action_listener(notification_id, "deny", () => { + // GLib.Application.get_default().activate_action("deny-subscription", new Variant.int32(conversation.id)); + // }); + // } catch (Error e) { + // warning("Failed showing subscription request notification: %s", e.message); + // } + } + + public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) { + string summary = _("Could not connect to %s").printf(account.bare_jid.domainpart); + string body = ""; + switch (error.source) { + case ConnectionManager.ConnectionError.Source.SASL: + body = _("Wrong password"); + break; + case ConnectionManager.ConnectionError.Source.TLS: + body = _("Invalid TLS certificate"); + break; + case ConnectionManager.ConnectionError.Source.STREAM_ERROR: + body = "Stream Error"; + break; + case ConnectionManager.ConnectionError.Source.CONNECTION: + body = "Connection"; + break; + } + + if (!show_message(summary, body, null, 0, stub)) { + warning("Failed showing connection error notification"); + } + } + + public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) { + Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); + + string display_room = room_jid.bare_jid.to_string(); + string summary = _("Invitation to %s").printf(display_room); + string body = _("%s invited you to %s").printf(inviter_display_name, display_room); + + Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); + if (!show_message(summary, body, get_avatar(direct_conversation), group_conversation.id, stub)) { // action not enabled yet + warning("Failed showing muc invite notification"); + } + + // HashTable hash_table = new HashTable(null, null); + // hash_table["image-data"] = yield get_conversation_icon(direct_conversation); + // string[] actions = new string[] {"default", "", "reject", _("Reject"), "accept", _("Accept")}; + + // try { + // uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0); + + // Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); + // add_action_listener(notification_id, "default", () => { + // GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); + // }); + // add_action_listener(notification_id, "accept", () => { + // GLib.Application.get_default().activate_action("deny-invite", new Variant.int32(group_conversation.id)); + // }); + // add_action_listener(notification_id, "deny", () => { + // GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); + // }); + // } catch (Error e) { + + // } + } + + public async void notify_voice_request(Conversation conversation, Jid from_jid) { + + string display_name = Dino.get_participant_display_name(stream_interactor, conversation, from_jid); + string display_room = Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s")); + string summary = _("Permission request"); + string body = _("%s requests the permission to write in %s").printf(display_name, display_room); + + if (!show_message(summary, body, get_avatar(conversation), conversation.id, stub)) { // missing actions + warning("Failed showing voice request notification"); + } + + // HashTable hash_table = new HashTable(null, null); + // hash_table["image-data"] = yield get_conversation_icon(conversation); + // string[] actions = new string[] {"deny", _("Deny"), "accept", _("Accept")}; + + // try { + // uint32 notification_id = dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0); + + // add_action_listener(notification_id, "accept", () => { + // GLib.Application.get_default().activate_action("deny-invite", new Variant.int32(conversation.id)); + // }); + // add_action_listener(notification_id, "deny", () => { + // GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(conversation.id)); + // }); + // } catch (Error e) { + // warning("Failed showing voice request notification: %s", e.message); + // } + } + + public async void retract_content_item_notifications() { + // if (content_notifications != null) { + // foreach (uint32 id in content_notifications.values) { + // try { + // dbus_notifications.close_notification(id); + // } catch (Error e) { } + // } + // content_notifications.clear(); + // } + } + + public async void retract_conversation_notifications(Conversation conversation) { + // if (content_notifications.has_key(conversation)) { + // try { + // dbus_notifications.close_notification(content_notifications[conversation]); + // } catch (Error e) { } + // } + // content_notifications.unset(conversation); + } + + private bool show_message(string sender, string message, string? image_path, int conv_id, NotificationCallback callback) { + DinoWinToastTemplate template; + if (image_path != null) { + template = new DinoWinToastTemplate(TemplateType.ImageAndText02); + template.setImagePath(image_path); + } else { + template = new DinoWinToastTemplate(TemplateType.Text02); + } + + template.setTextField(sender, TextField.FirstLine); + template.setTextField(message, TextField.SecondLine); + return ShowMessage(template, conv_id, callback) == 0; + } + + private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) { + string body = body_; + if (participant_display_name != null) { + body = @"$participant_display_name: $body"; + } + + var avatar = get_avatar(conversation); + if (!show_message(conversation_display_name, body, avatar, conversation.id, onclick_callback)) { + warning("Failed showing content item notification"); + } + } + + private void onclick_callback(int conv_id) { + this.app.activate_action("open-conversation", conv_id); + } + + private void stub(int conv_id) { + } + + private string? get_avatar(Conversation conversation) { + var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY); + return avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart); + } + } +} \ No newline at end of file diff --git a/plugins/windows-notification/src/wintoast.vala b/plugins/windows-notification/src/wintoast.vala deleted file mode 100644 index a4e1b622..00000000 --- a/plugins/windows-notification/src/wintoast.vala +++ /dev/null @@ -1,28 +0,0 @@ -using DinoWinToast; - -namespace Dino.Plugins.WindowsNotification { - public class WinToast { - public bool valid { get; private set; } - - public WinToast() { - valid = Init() == 0; - } - - public bool show_message(string sender, string message, string? image_path, int conv_id, NotificationCallback callback) { - if (valid) { - DinoWinToastTemplate template; - if (image_path != null) { - template = new DinoWinToastTemplate(TemplateType.ImageAndText02); - template.setImagePath(image_path); - } else { - template = new DinoWinToastTemplate(TemplateType.Text02); - } - - template.setTextField(sender, TextField.FirstLine); - template.setTextField(message, TextField.SecondLine); - return DinoWinToast.ShowMessage(template, conv_id, callback) == 0; - } - return false; - } - } -} \ No newline at end of file