Merge pull request #11647 from sethlu/accept-additional-notification-actions

feat: Accept additional notification actions
This commit is contained in:
Cheng Zhao 2018-02-15 15:46:36 +09:00 committed by GitHub
commit fdda1c55c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 20 deletions

View file

@ -7,6 +7,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@ -27,7 +28,8 @@ class CocoaNotification : public Notification {
void NotificationDisplayed(); void NotificationDisplayed();
void NotificationReplied(const std::string& reply); void NotificationReplied(const std::string& reply);
void NotificationButtonClicked(); void NotificationActivated();
void NotificationActivated(NSUserNotificationAction* action);
NSUserNotification* notification() const { return notification_; } NSUserNotification* notification() const { return notification_; }
@ -35,7 +37,8 @@ class CocoaNotification : public Notification {
void LogAction(const char* action); void LogAction(const char* action);
base::scoped_nsobject<NSUserNotification> notification_; base::scoped_nsobject<NSUserNotification> notification_;
int action_index_; std::map<std::string, unsigned> additional_action_indices_;
unsigned action_index_;
DISALLOW_COPY_AND_ASSIGN(CocoaNotification); DISALLOW_COPY_AND_ASSIGN(CocoaNotification);
}; };

View file

@ -29,20 +29,18 @@ CocoaNotification::~CocoaNotification() {
void CocoaNotification::Show(const NotificationOptions& options) { void CocoaNotification::Show(const NotificationOptions& options) {
notification_.reset([[NSUserNotification alloc] init]); notification_.reset([[NSUserNotification alloc] init]);
NSString* identifier = [NSString stringWithFormat:@"%s%d", "ElectronNotification", g_identifier_]; NSString* identifier = [NSString stringWithFormat:@"ElectronNotification%d", g_identifier_++];
[notification_ setTitle:base::SysUTF16ToNSString(options.title)]; [notification_ setTitle:base::SysUTF16ToNSString(options.title)];
[notification_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)]; [notification_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)];
[notification_ setInformativeText:base::SysUTF16ToNSString(options.msg)]; [notification_ setInformativeText:base::SysUTF16ToNSString(options.msg)];
[notification_ setIdentifier:identifier]; [notification_ setIdentifier:identifier];
g_identifier_++;
if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) { if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) {
LOG(INFO) << "Notification created (" << [identifier UTF8String] << ")"; LOG(INFO) << "Notification created (" << [identifier UTF8String] << ")";
} }
if ([notification_ respondsToSelector:@selector(setContentImage:)] && if (!options.icon.drawsNothing()) {
!options.icon.drawsNothing()) {
NSImage* image = skia::SkBitmapToNSImageWithColorSpace( NSImage* image = skia::SkBitmapToNSImageWithColorSpace(
options.icon, base::mac::GetGenericRGBColorSpace()); options.icon, base::mac::GetGenericRGBColorSpace());
[notification_ setContentImage:image]; [notification_ setContentImage:image];
@ -50,23 +48,40 @@ void CocoaNotification::Show(const NotificationOptions& options) {
if (options.silent) { if (options.silent) {
[notification_ setSoundName:nil]; [notification_ setSoundName:nil];
} else if (options.sound != nil) { } else if (options.sound.empty()) {
[notification_ setSoundName:base::SysUTF16ToNSString(options.sound)];
} else {
[notification_ setSoundName:NSUserNotificationDefaultSoundName]; [notification_ setSoundName:NSUserNotificationDefaultSoundName];
} else {
[notification_ setSoundName:base::SysUTF16ToNSString(options.sound)];
} }
[notification_ setHasActionButton:false]; [notification_ setHasActionButton:false];
int i = 0; int i = 0;
action_index_ = UINT_MAX;
NSMutableArray* additionalActions = [[[NSMutableArray alloc] init] autorelease];
for (const auto& action : options.actions) { for (const auto& action : options.actions) {
if (action.type == base::ASCIIToUTF16("button")) { if (action.type == base::ASCIIToUTF16("button")) {
if (action_index_ == UINT_MAX) {
// First button observed is the displayed action
[notification_ setHasActionButton:true]; [notification_ setHasActionButton:true];
[notification_ setActionButtonTitle:base::SysUTF16ToNSString(action.text)]; [notification_ setActionButtonTitle:base::SysUTF16ToNSString(action.text)];
action_index_ = i; action_index_ = i;
} else {
// All of the rest are appended to the list of additional actions
NSString* actionIdentifier = [NSString stringWithFormat:@"%@Action%d", identifier, i];
NSUserNotificationAction* notificationAction =
[NSUserNotificationAction actionWithIdentifier:actionIdentifier
title:base::SysUTF16ToNSString(action.text)];
[additionalActions addObject:notificationAction];
additional_action_indices_.insert(std::make_pair(base::SysNSStringToUTF8(actionIdentifier), i));
}
} }
i++; i++;
} }
if ([additionalActions count] > 0 &&
[notification_ respondsToSelector:@selector(setAdditionalActions:)]) {
[notification_ setAdditionalActions:additionalActions]; // Requires macOS 10.10
}
if (options.has_reply) { if (options.has_reply) {
[notification_ setResponsePlaceholder:base::SysUTF16ToNSString(options.reply_placeholder)]; [notification_ setResponsePlaceholder:base::SysUTF16ToNSString(options.reply_placeholder)];
@ -101,13 +116,30 @@ void CocoaNotification::NotificationReplied(const std::string& reply) {
this->LogAction("replied to"); this->LogAction("replied to");
} }
void CocoaNotification::NotificationButtonClicked() { void CocoaNotification::NotificationActivated() {
if (delegate()) if (delegate())
delegate()->NotificationAction(action_index_); delegate()->NotificationAction(action_index_);
this->LogAction("button clicked"); this->LogAction("button clicked");
} }
void CocoaNotification::NotificationActivated(NSUserNotificationAction* action) {
if (delegate()) {
unsigned index = action_index_;
std::string identifier = base::SysNSStringToUTF8(action.identifier);
for (const auto& it : additional_action_indices_) {
if (it.first == identifier) {
index = it.second;
break;
}
}
delegate()->NotificationAction(index);
}
this->LogAction("button clicked");
}
void CocoaNotification::LogAction(const char* action) { void CocoaNotification::LogAction(const char* action) {
if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) { if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) {
NSString* identifier = [notification_ valueForKey:@"identifier"]; NSString* identifier = [notification_ valueForKey:@"identifier"];

View file

@ -34,12 +34,15 @@
} }
if (notification) { if (notification) {
if (notif.activationType == NSUserNotificationActivationTypeReplied) { // Ref: https://developer.apple.com/documentation/foundation/nsusernotificationactivationtype?language=objc
notification->NotificationReplied([notif.response.string UTF8String]); if (notif.activationType == NSUserNotificationActivationTypeContentsClicked) {
} else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
notification->NotificationButtonClicked();
} else if (notif.activationType == NSUserNotificationActivationTypeContentsClicked) {
notification->NotificationClicked(); notification->NotificationClicked();
} else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
notification->NotificationActivated();
} else if (notif.activationType == NSUserNotificationActivationTypeReplied) {
notification->NotificationReplied([notif.response.string UTF8String]);
} else if (notif.activationType == NSUserNotificationActivationTypeAdditionalActionClicked) {
notification->NotificationActivated([notif additionalActivationAction]);
} }
} }
} }

View file

@ -7,7 +7,7 @@
| Action Type | Platform Support | Usage of `text` | Default `text` | Limitations | | Action Type | Platform Support | Usage of `text` | Default `text` | Limitations |
|-------------|------------------|-----------------|----------------|-------------| |-------------|------------------|-----------------|----------------|-------------|
| `button` | macOS | Used as the label for the button | "Show" | Maximum of one button, if multiple are provided only the last is used. This action is also incompatible with `hasReply` and will be ignored if `hasReply` is `true`. | | `button` | macOS | Used as the label for the button | "Show" (or a localized string by system default if first of such `button`, otherwise empty) | Only the first one is used. If multiple are provided, those beyond the first will be listed as additional actions (displayed when mouse active over the action button). Any such action also is incompatible with `hasReply` and will be ignored if `hasReply` is `true`. |
### Button support on macOS ### Button support on macOS
@ -15,6 +15,6 @@ In order for extra notification buttons to work on macOS your app must meet the
following criteria. following criteria.
* App is signed * App is signed
* App has it's `NSUserNotificationAlertStyle` set to `alert` in the `info.plist`. * 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. If either of these requirements are not met the button simply won't appear.