Skip to content

Commit

Permalink
Merge pull request electron#11647 from sethlu/accept-additional-notif…
Browse files Browse the repository at this point in the history
…ication-actions

feat: Accept additional notification actions
  • Loading branch information
zcbenz authored Feb 15, 2018
2 parents 7e2f760 + e3b70dd commit fdda1c5
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 20 deletions.
7 changes: 5 additions & 2 deletions brightray/browser/mac/cocoa_notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import <Foundation/Foundation.h>

#include <map>
#include <string>
#include <vector>

Expand All @@ -27,15 +28,17 @@ class CocoaNotification : public Notification {

void NotificationDisplayed();
void NotificationReplied(const std::string& reply);
void NotificationButtonClicked();
void NotificationActivated();
void NotificationActivated(NSUserNotificationAction* action);

NSUserNotification* notification() const { return notification_; }

private:
void LogAction(const char* action);

base::scoped_nsobject<NSUserNotification> notification_;
int action_index_;
std::map<std::string, unsigned> additional_action_indices_;
unsigned action_index_;

DISALLOW_COPY_AND_ASSIGN(CocoaNotification);
};
Expand Down
54 changes: 43 additions & 11 deletions brightray/browser/mac/cocoa_notification.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,59 @@
void CocoaNotification::Show(const NotificationOptions& options) {
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_ setSubtitle:base::SysUTF16ToNSString(options.subtitle)];
[notification_ setInformativeText:base::SysUTF16ToNSString(options.msg)];
[notification_ setIdentifier:identifier];
g_identifier_++;

if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) {
LOG(INFO) << "Notification created (" << [identifier UTF8String] << ")";
}

if ([notification_ respondsToSelector:@selector(setContentImage:)] &&
!options.icon.drawsNothing()) {
if (!options.icon.drawsNothing()) {
NSImage* image = skia::SkBitmapToNSImageWithColorSpace(
options.icon, base::mac::GetGenericRGBColorSpace());
[notification_ setContentImage:image];
}

if (options.silent) {
[notification_ setSoundName:nil];
} else if (options.sound != nil) {
[notification_ setSoundName:base::SysUTF16ToNSString(options.sound)];
} else {
} else if (options.sound.empty()) {
[notification_ setSoundName:NSUserNotificationDefaultSoundName];
} else {
[notification_ setSoundName:base::SysUTF16ToNSString(options.sound)];
}

[notification_ setHasActionButton:false];

int i = 0;
action_index_ = UINT_MAX;
NSMutableArray* additionalActions = [[[NSMutableArray alloc] init] autorelease];
for (const auto& action : options.actions) {
if (action.type == base::ASCIIToUTF16("button")) {
[notification_ setHasActionButton:true];
[notification_ setActionButtonTitle:base::SysUTF16ToNSString(action.text)];
action_index_ = i;
if (action_index_ == UINT_MAX) {
// First button observed is the displayed action
[notification_ setHasActionButton:true];
[notification_ setActionButtonTitle:base::SysUTF16ToNSString(action.text)];
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++;
}
if ([additionalActions count] > 0 &&
[notification_ respondsToSelector:@selector(setAdditionalActions:)]) {
[notification_ setAdditionalActions:additionalActions]; // Requires macOS 10.10
}

if (options.has_reply) {
[notification_ setResponsePlaceholder:base::SysUTF16ToNSString(options.reply_placeholder)];
Expand Down Expand Up @@ -101,13 +116,30 @@
this->LogAction("replied to");
}

void CocoaNotification::NotificationButtonClicked() {
void CocoaNotification::NotificationActivated() {
if (delegate())
delegate()->NotificationAction(action_index_);

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) {
if (getenv("ELECTRON_DEBUG_NOTIFICATIONS")) {
NSString* identifier = [notification_ valueForKey:@"identifier"];
Expand Down
13 changes: 8 additions & 5 deletions brightray/browser/mac/notification_center_delegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ - (void)userNotificationCenter:(NSUserNotificationCenter*)center
}

if (notification) {
if (notif.activationType == NSUserNotificationActivationTypeReplied) {
notification->NotificationReplied([notif.response.string UTF8String]);
} else if (notif.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
notification->NotificationButtonClicked();
} else if (notif.activationType == NSUserNotificationActivationTypeContentsClicked) {
// Ref: https://developer.apple.com/documentation/foundation/nsusernotificationactivationtype?language=objc
if (notif.activationType == NSUserNotificationActivationTypeContentsClicked) {
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]);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/api/structures/notification-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

| 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

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`.
* 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.

0 comments on commit fdda1c5

Please sign in to comment.