diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 791d574774ca..fa7d86d1e7af 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -14,8 +14,35 @@ #include "brightray/browser/browser_client.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" +#include "native_mate/object_template_builder.h" #include "url/gurl.h" +namespace mate { +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + brightray::NotificationAction* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + + if (!dict.Get("type", &(out->type))) { + return false; + } + dict.Get("label", &(out->label)); + return true; + } + + static v8::Local ToV8(v8::Isolate* isolate, + brightray::NotificationAction val) { + mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); + dict.Set("label", val.label); + dict.Set("type", val.type); + return dict.GetHandle(); + } +}; +} // namespace mate + namespace atom { namespace api { @@ -38,6 +65,7 @@ Notification::Notification(v8::Isolate* isolate, opts.Get("silent", &silent_); opts.Get("replyPlaceholder", &reply_placeholder_); opts.Get("hasReply", &has_reply_); + opts.Get("actions", &actions_); } } @@ -97,6 +125,19 @@ void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; } +void Notification::SetActions( + const std::vector actions) { + actions_ = actions; +} + +std::vector Notification::GetActions() { + return actions_; +} + +void Notification::NotificationAction(int index) { + Emit("action", index); +} + void Notification::NotificationClick() { Emit("click"); } @@ -122,7 +163,7 @@ void Notification::Show() { notification_ = presenter_->CreateNotification(this); if (notification_) { notification_->Show(title_, body_, "", GURL(), icon_.AsBitmap(), silent_, - has_reply_, reply_placeholder_); + has_reply_, reply_placeholder_, actions_); } } } @@ -144,7 +185,9 @@ void Notification::BuildPrototype(v8::Isolate* isolate, .SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder, &Notification::SetReplyPlaceholder) .SetProperty("hasReply", &Notification::GetHasReply, - &Notification::SetHasReply); + &Notification::SetHasReply) + .SetProperty("actions", &Notification::GetActions, + &Notification::SetActions); } } // namespace api diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 9e1c47eeb74e..9b933fc44d26 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -31,6 +31,7 @@ class Notification : public mate::TrackableObject, v8::Local prototype); // NotificationDelegate: + void NotificationAction(int index) override; void NotificationClick() override; void NotificationReplied(const std::string& reply) override; void NotificationDisplayed() override; @@ -51,6 +52,7 @@ class Notification : public mate::TrackableObject, bool GetSilent(); base::string16 GetReplyPlaceholder(); bool GetHasReply(); + std::vector GetActions(); // Prop Setters void SetTitle(const base::string16& new_title); @@ -58,6 +60,7 @@ class Notification : public mate::TrackableObject, void SetSilent(bool new_silent); void SetReplyPlaceholder(const base::string16& new_reply_placeholder); void SetHasReply(bool new_has_reply); + void SetActions(const std::vector actions); private: base::string16 title_; @@ -68,6 +71,7 @@ class Notification : public mate::TrackableObject, bool silent_ = false; base::string16 reply_placeholder_; bool has_reply_ = false; + std::vector actions_; brightray::NotificationPresenter* presenter_; diff --git a/brightray/browser/linux/libnotify_notification.cc b/brightray/browser/linux/libnotify_notification.cc index ff3e61110101..f93030656cae 100644 --- a/brightray/browser/linux/libnotify_notification.cc +++ b/brightray/browser/linux/libnotify_notification.cc @@ -4,6 +4,8 @@ #include "brightray/browser/linux/libnotify_notification.h" +#include + #include "base/files/file_enumerator.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -90,7 +92,9 @@ void LibnotifyNotification::Show(const base::string16& title, const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) { + const base::string16& reply_placeholder, + const std::vector actions + ) { notification_ = libnotify_loader_.notify_notification_new( base::UTF16ToUTF8(title).c_str(), base::UTF16ToUTF8(body).c_str(), diff --git a/brightray/browser/linux/libnotify_notification.h b/brightray/browser/linux/libnotify_notification.h index dbc34f27ae44..3616051f055d 100644 --- a/brightray/browser/linux/libnotify_notification.h +++ b/brightray/browser/linux/libnotify_notification.h @@ -6,6 +6,7 @@ #define BRIGHTRAY_BROWSER_LINUX_LIBNOTIFY_NOTIFICATION_H_ #include +#include #include "brightray/browser/linux/libnotify_loader.h" #include "brightray/browser/notification.h" @@ -29,7 +30,8 @@ class LibnotifyNotification : public Notification { const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) override; + const base::string16& reply_placeholder, + const std::vector actions) override; void Dismiss() override; private: diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index 4e043dd7d8fc..9eabbd7b5fd4 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -8,6 +8,7 @@ #import #include +#include #include "base/mac/scoped_nsobject.h" #include "brightray/browser/notification.h" @@ -28,16 +29,19 @@ class CocoaNotification : public Notification { const SkBitmap& icon, bool silent, const bool has_reply, - const base::string16& reply_placeholder) override; + const base::string16& reply_placeholder, + const std::vector actions) override; void Dismiss() override; void NotificationDisplayed(); void NotificationReplied(const std::string& reply); + void NotificationButtonClicked(); NSUserNotification* notification() const { return notification_; } private: base::scoped_nsobject notification_; + int actionIndex_; DISALLOW_COPY_AND_ASSIGN(CocoaNotification); }; diff --git a/brightray/browser/mac/cocoa_notification.mm b/brightray/browser/mac/cocoa_notification.mm index 5a16d40d1a7a..8e0d176caece 100644 --- a/brightray/browser/mac/cocoa_notification.mm +++ b/brightray/browser/mac/cocoa_notification.mm @@ -6,6 +6,7 @@ #include "base/mac/mac_util.h" #include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" #include "brightray/browser/notification_delegate.h" #include "brightray/browser/notification_presenter.h" #include "skia/ext/skia_utils_mac.h" @@ -30,7 +31,8 @@ void CocoaNotification::Show(const base::string16& title, const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) { + const base::string16& reply_placeholder, + const std::vector actions) { notification_.reset([[NSUserNotification alloc] init]); [notification_ setTitle:base::SysUTF16ToNSString(title)]; [notification_ setInformativeText:base::SysUTF16ToNSString(body)]; @@ -48,6 +50,18 @@ void CocoaNotification::Show(const base::string16& title, [notification_ setSoundName:NSUserNotificationDefaultSoundName]; } + [notification_ setHasActionButton:false]; + + for (size_t i = 0; i < actions.size(); i++) { + NotificationAction action = actions[i]; + + if (action.type == base::UTF8ToUTF16("button")) { + [notification_ setHasActionButton:true]; + [notification_ setActionButtonTitle:base::SysUTF16ToNSString(action.label)]; + actionIndex_ = i; + } + } + if (has_reply) { [notification_ setResponsePlaceholder:base::SysUTF16ToNSString(reply_placeholder)]; [notification_ setHasReplyButton:true]; @@ -74,4 +88,9 @@ void CocoaNotification::NotificationReplied(const std::string& reply) { delegate()->NotificationReplied(reply); } +void CocoaNotification::NotificationButtonClicked() { + if (delegate()) + delegate()->NotificationAction(actionIndex_); +} + } // namespace brightray diff --git a/brightray/browser/mac/notification_center_delegate.mm b/brightray/browser/mac/notification_center_delegate.mm index e0b4dfa0e8fd..834b9e5ee43b 100644 --- a/brightray/browser/mac/notification_center_delegate.mm +++ b/brightray/browser/mac/notification_center_delegate.mm @@ -29,8 +29,10 @@ didActivateNotification:(NSUserNotification *)notif { auto notification = presenter_->GetNotification(notif); if (notification) { - if (notif.activationType == NSUserNotificationActivationTypeReplied){ + if (notif.activationType == NSUserNotificationActivationTypeReplied) { notification->NotificationReplied([notif.response.string UTF8String]); + } else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) { + notification->NotificationButtonClicked(); } else { notification->NotificationClicked(); } diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h index f5e00a23a9b7..6af0f907cc9f 100644 --- a/brightray/browser/notification.h +++ b/brightray/browser/notification.h @@ -6,6 +6,7 @@ #define BRIGHTRAY_BROWSER_NOTIFICATION_H_ #include +#include #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" @@ -18,6 +19,11 @@ namespace brightray { class NotificationDelegate; class NotificationPresenter; +struct NotificationAction { + base::string16 type; + base::string16 label; +}; + class Notification { public: virtual ~Notification(); @@ -30,7 +36,8 @@ class Notification { const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) = 0; + const base::string16& reply_placeholder, + const std::vector actions) = 0; // Closes the notification, this instance will be destroyed after the // notification gets closed. virtual void Dismiss() = 0; diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h index 8493fdc3a5aa..5f1e369bdc7b 100644 --- a/brightray/browser/notification_delegate.h +++ b/brightray/browser/notification_delegate.h @@ -21,6 +21,7 @@ class NotificationDelegate : public content::DesktopNotificationDelegate { // Notification was replied to virtual void NotificationReplied(const std::string& reply) {} + virtual void NotificationAction(int index) {} }; } // namespace brightray diff --git a/brightray/browser/platform_notification_service.cc b/brightray/browser/platform_notification_service.cc index 964fa67f5218..4e38b89fa754 100644 --- a/brightray/browser/platform_notification_service.cc +++ b/brightray/browser/platform_notification_service.cc @@ -32,7 +32,7 @@ void OnWebNotificationAllowed(base::WeakPtr notification, if (allowed) notification->Show(data.title, data.body, data.tag, data.icon, icon, audio_muted ? true : data.silent, false, - base::UTF8ToUTF16("")); + base::UTF8ToUTF16(""), {}); else notification->Destroy(); } diff --git a/brightray/browser/win/win32_notification.cc b/brightray/browser/win/win32_notification.cc index ea3b35ab2c4f..aaaa26c78d6d 100644 --- a/brightray/browser/win/win32_notification.cc +++ b/brightray/browser/win/win32_notification.cc @@ -4,6 +4,7 @@ #include #include +#include #include "third_party/skia/include/core/SkBitmap.h" @@ -13,7 +14,8 @@ void Win32Notification::Show( const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, bool silent, - bool has_reply, const base::string16& reply_placeholder) { + bool has_reply, const base::string16& reply_placeholder, + const std::vector actions) { auto presenter = static_cast(this->presenter()); if (!presenter) return; diff --git a/brightray/browser/win/win32_notification.h b/brightray/browser/win/win32_notification.h index 3ec6dfb17e56..4cebdee36eb6 100644 --- a/brightray/browser/win/win32_notification.h +++ b/brightray/browser/win/win32_notification.h @@ -13,7 +13,8 @@ class Win32Notification : public brightray::Notification { void Show(const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, bool silent, - bool has_reply, const base::string16& reply_placeholder) override; + bool has_reply, const base::string16& reply_placeholder, + const std::vector actions) override; void Dismiss() override; const DesktopNotificationController::Notification& GetRef() const { diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc index 335b2d924fdc..4a44706dd592 100644 --- a/brightray/browser/win/windows_toast_notification.cc +++ b/brightray/browser/win/windows_toast_notification.cc @@ -9,6 +9,7 @@ #include "brightray/browser/win/windows_toast_notification.h" #include +#include #include "base/strings/utf_string_conversions.h" #include "brightray/browser/notification_delegate.h" @@ -91,7 +92,10 @@ void WindowsToastNotification::Show(const base::string16& title, const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) { + const base::string16& reply_placeholder, + const std::vector< + NotificationAction + > actions) { auto presenter_win = static_cast(presenter()); std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url); diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h index 0b96f43c34f9..ae5e604ad7b8 100644 --- a/brightray/browser/win/windows_toast_notification.h +++ b/brightray/browser/win/windows_toast_notification.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "brightray/browser/notification.h" @@ -57,7 +58,8 @@ class WindowsToastNotification : public Notification { const SkBitmap& icon, bool silent, bool has_reply, - const base::string16& reply_placeholder) override; + const base::string16& reply_placeholder, + const std::vector actions) override; void Dismiss() override; private: diff --git a/docs/api/notification.md b/docs/api/notification.md index 469535ed99a4..1e339a43c94c 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -36,6 +36,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu * `icon` [NativeImage](native-image.md) - (optional) An icon to use in the notification * `hasReply` Boolean - (optional) Whether or not to add an inline reply option to the notification. _macOS_ * `replyPlaceholder` String - (optional) The placeholder to write in the inline reply input field. _macOS_ + * `actions` [NotificationAction[]](structures/notification-action.md) - Actions to add to the notification. Please read the available actions and limitations in the `NotificationAction` documentation _macOS_ ### Instance Events @@ -83,6 +84,13 @@ Returns: Emitted when the user clicks the "Reply" button on a notification with `hasReply: true`. +#### Event: 'action' _macOS_ + +Returns: + +* `event` Event +* `index` Number - The index of the action that was activated + ### Instance Methods Objects created with `new Notification` have the following instance methods: diff --git a/docs/api/structures/notification-action.md b/docs/api/structures/notification-action.md new file mode 100644 index 000000000000..59a0fb566736 --- /dev/null +++ b/docs/api/structures/notification-action.md @@ -0,0 +1,20 @@ +# NotificationAction Object + +* `type` String - The type of action, can be `button`. +* `label` String - The label for the given action. + +## Platform / Action Support + +| Action Type | Platform Support | Limitations | +|-------------|------------------|-------------| +| `button` | macOS | Maximum of one button, if multiple are provided only the last is used | + +### Button support on macOS + +In order for extra notification buttons to work on macOS your app must meet the +following criteria. + +* App is signed +* App has it's `NSUserNotificationAlertStyle` set to `alert` in the `info.plist`. + +If either of these requirements are not met the button simply won't appear.