feat: allow Linux/Windows users to set notification timeout (#20153)
* feat: allow Linux users to set notification timeout * implement on windows
This commit is contained in:
parent
5e11be6898
commit
f80a17c5be
7 changed files with 92 additions and 3 deletions
|
@ -35,6 +35,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
|
||||||
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
|
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
|
||||||
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
|
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
|
||||||
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
|
||||||
|
* `timeoutType` String (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
|
||||||
* `replyPlaceholder` String (optional) _macOS_ - The placeholder to write in the inline reply input field.
|
* `replyPlaceholder` String (optional) _macOS_ - The placeholder to write in the inline reply input field.
|
||||||
* `sound` String (optional) _macOS_ - The name of the sound file to play when the notification is shown.
|
* `sound` String (optional) _macOS_ - The name of the sound file to play when the notification is shown.
|
||||||
* `urgency` String (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
* `urgency` String (optional) _Linux_ - The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
|
||||||
|
@ -151,6 +152,12 @@ A `String` property representing the urgency level of the notification. Can be '
|
||||||
|
|
||||||
Default is 'low' - see [NotifyUrgency](https://developer.gnome.org/notification-spec/#urgency-levels) for more information.
|
Default is 'low' - see [NotifyUrgency](https://developer.gnome.org/notification-spec/#urgency-levels) for more information.
|
||||||
|
|
||||||
|
#### `notification.timeoutType` _Linux_ _Windows_
|
||||||
|
|
||||||
|
A `String` property representing the type of timeout duration for the notification. Can be 'default' or 'never'.
|
||||||
|
|
||||||
|
If `timeoutType` is set to 'never', the notification never expires. It stays open until closed by the calling API or the user.
|
||||||
|
|
||||||
#### `notification.actions`
|
#### `notification.actions`
|
||||||
|
|
||||||
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.
|
||||||
|
|
|
@ -69,6 +69,7 @@ Notification::Notification(v8::Local<v8::Object> wrapper,
|
||||||
opts.Get("replyPlaceholder", &reply_placeholder_);
|
opts.Get("replyPlaceholder", &reply_placeholder_);
|
||||||
opts.Get("urgency", &urgency_);
|
opts.Get("urgency", &urgency_);
|
||||||
opts.Get("hasReply", &has_reply_);
|
opts.Get("hasReply", &has_reply_);
|
||||||
|
opts.Get("timeoutType", &timeout_type_);
|
||||||
opts.Get("actions", &actions_);
|
opts.Get("actions", &actions_);
|
||||||
opts.Get("sound", &sound_);
|
opts.Get("sound", &sound_);
|
||||||
opts.Get("closeButtonText", &close_button_text_);
|
opts.Get("closeButtonText", &close_button_text_);
|
||||||
|
@ -111,6 +112,10 @@ bool Notification::GetHasReply() const {
|
||||||
return has_reply_;
|
return has_reply_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base::string16 Notification::GetTimeoutType() const {
|
||||||
|
return timeout_type_;
|
||||||
|
}
|
||||||
|
|
||||||
base::string16 Notification::GetReplyPlaceholder() const {
|
base::string16 Notification::GetReplyPlaceholder() const {
|
||||||
return reply_placeholder_;
|
return reply_placeholder_;
|
||||||
}
|
}
|
||||||
|
@ -152,6 +157,10 @@ void Notification::SetHasReply(bool new_has_reply) {
|
||||||
has_reply_ = new_has_reply;
|
has_reply_ = new_has_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Notification::SetTimeoutType(const base::string16& new_timeout_type) {
|
||||||
|
timeout_type_ = new_timeout_type;
|
||||||
|
}
|
||||||
|
|
||||||
void Notification::SetReplyPlaceholder(const base::string16& new_placeholder) {
|
void Notification::SetReplyPlaceholder(const base::string16& new_placeholder) {
|
||||||
reply_placeholder_ = new_placeholder;
|
reply_placeholder_ = new_placeholder;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +225,7 @@ void Notification::Show() {
|
||||||
options.icon = icon_.AsBitmap();
|
options.icon = icon_.AsBitmap();
|
||||||
options.silent = silent_;
|
options.silent = silent_;
|
||||||
options.has_reply = has_reply_;
|
options.has_reply = has_reply_;
|
||||||
|
options.timeout_type = timeout_type_;
|
||||||
options.reply_placeholder = reply_placeholder_;
|
options.reply_placeholder = reply_placeholder_;
|
||||||
options.actions = actions_;
|
options.actions = actions_;
|
||||||
options.sound = sound_;
|
options.sound = sound_;
|
||||||
|
@ -246,6 +256,8 @@ void Notification::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent)
|
.SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent)
|
||||||
.SetProperty("hasReply", &Notification::GetHasReply,
|
.SetProperty("hasReply", &Notification::GetHasReply,
|
||||||
&Notification::SetHasReply)
|
&Notification::SetHasReply)
|
||||||
|
.SetProperty("timeoutType", &Notification::GetTimeoutType,
|
||||||
|
&Notification::SetTimeoutType)
|
||||||
.SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder,
|
.SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder,
|
||||||
&Notification::SetReplyPlaceholder)
|
&Notification::SetReplyPlaceholder)
|
||||||
.SetProperty("urgency", &Notification::GetUrgency,
|
.SetProperty("urgency", &Notification::GetUrgency,
|
||||||
|
|
|
@ -54,6 +54,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||||
base::string16 GetBody() const;
|
base::string16 GetBody() const;
|
||||||
bool GetSilent() const;
|
bool GetSilent() const;
|
||||||
bool GetHasReply() const;
|
bool GetHasReply() const;
|
||||||
|
base::string16 GetTimeoutType() const;
|
||||||
base::string16 GetReplyPlaceholder() const;
|
base::string16 GetReplyPlaceholder() const;
|
||||||
base::string16 GetUrgency() const;
|
base::string16 GetUrgency() const;
|
||||||
base::string16 GetSound() const;
|
base::string16 GetSound() const;
|
||||||
|
@ -67,6 +68,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||||
void SetSilent(bool new_silent);
|
void SetSilent(bool new_silent);
|
||||||
void SetHasReply(bool new_has_reply);
|
void SetHasReply(bool new_has_reply);
|
||||||
void SetUrgency(const base::string16& new_urgency);
|
void SetUrgency(const base::string16& new_urgency);
|
||||||
|
void SetTimeoutType(const base::string16& new_timeout_type);
|
||||||
void SetReplyPlaceholder(const base::string16& new_reply_placeholder);
|
void SetReplyPlaceholder(const base::string16& new_reply_placeholder);
|
||||||
void SetSound(const base::string16& sound);
|
void SetSound(const base::string16& sound);
|
||||||
void SetActions(const std::vector<electron::NotificationAction>& actions);
|
void SetActions(const std::vector<electron::NotificationAction>& actions);
|
||||||
|
@ -81,6 +83,7 @@ class Notification : public mate::TrackableObject<Notification>,
|
||||||
bool has_icon_ = false;
|
bool has_icon_ = false;
|
||||||
bool silent_ = false;
|
bool silent_ = false;
|
||||||
bool has_reply_ = false;
|
bool has_reply_ = false;
|
||||||
|
base::string16 timeout_type_;
|
||||||
base::string16 reply_placeholder_;
|
base::string16 reply_placeholder_;
|
||||||
base::string16 sound_;
|
base::string16 sound_;
|
||||||
base::string16 urgency_;
|
base::string16 urgency_;
|
||||||
|
|
|
@ -114,11 +114,14 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
|
||||||
GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(options.icon);
|
GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(options.icon);
|
||||||
libnotify_loader_.notify_notification_set_image_from_pixbuf(notification_,
|
libnotify_loader_.notify_notification_set_image_from_pixbuf(notification_,
|
||||||
pixbuf);
|
pixbuf);
|
||||||
libnotify_loader_.notify_notification_set_timeout(notification_,
|
|
||||||
NOTIFY_EXPIRES_DEFAULT);
|
|
||||||
g_object_unref(pixbuf);
|
g_object_unref(pixbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the timeout duration for the notification
|
||||||
|
bool neverTimeout = options.timeout_type == base::ASCIIToUTF16("never");
|
||||||
|
int timeout = (neverTimeout) ? NOTIFY_EXPIRES_NEVER : NOTIFY_EXPIRES_DEFAULT;
|
||||||
|
libnotify_loader_.notify_notification_set_timeout(notification_, timeout);
|
||||||
|
|
||||||
if (!options.tag.empty()) {
|
if (!options.tag.empty()) {
|
||||||
GQuark id = g_quark_from_string(options.tag.c_str());
|
GQuark id = g_quark_from_string(options.tag.c_str());
|
||||||
g_object_set(G_OBJECT(notification_), "id", id, NULL);
|
g_object_set(G_OBJECT(notification_), "id", id, NULL);
|
||||||
|
|
|
@ -32,6 +32,7 @@ struct NotificationOptions {
|
||||||
GURL icon_url;
|
GURL icon_url;
|
||||||
SkBitmap icon;
|
SkBitmap icon;
|
||||||
bool has_reply;
|
bool has_reply;
|
||||||
|
base::string16 timeout_type;
|
||||||
base::string16 reply_placeholder;
|
base::string16 reply_placeholder;
|
||||||
base::string16 sound;
|
base::string16 sound;
|
||||||
base::string16 urgency; // Linux
|
base::string16 urgency; // Linux
|
||||||
|
|
|
@ -94,7 +94,8 @@ void WindowsToastNotification::Show(const NotificationOptions& options) {
|
||||||
|
|
||||||
ComPtr<IXmlDocument> toast_xml;
|
ComPtr<IXmlDocument> toast_xml;
|
||||||
if (FAILED(GetToastXml(toast_manager_.Get(), options.title, options.msg,
|
if (FAILED(GetToastXml(toast_manager_.Get(), options.title, options.msg,
|
||||||
icon_path, options.silent, &toast_xml))) {
|
icon_path, options.timeout_type, options.silent,
|
||||||
|
&toast_xml))) {
|
||||||
NotificationFailed();
|
NotificationFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -149,6 +150,7 @@ bool WindowsToastNotification::GetToastXml(
|
||||||
const std::wstring& title,
|
const std::wstring& title,
|
||||||
const std::wstring& msg,
|
const std::wstring& msg,
|
||||||
const std::wstring& icon_path,
|
const std::wstring& icon_path,
|
||||||
|
const std::wstring& timeout_type,
|
||||||
bool silent,
|
bool silent,
|
||||||
IXmlDocument** toast_xml) {
|
IXmlDocument** toast_xml) {
|
||||||
ABI::Windows::UI::Notifications::ToastTemplateType template_type;
|
ABI::Windows::UI::Notifications::ToastTemplateType template_type;
|
||||||
|
@ -183,6 +185,15 @@ bool WindowsToastNotification::GetToastXml(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure the toast's timeout settings
|
||||||
|
if (timeout_type == base::ASCIIToUTF16("never")) {
|
||||||
|
if (FAILED(SetXmlScenarioReminder(*toast_xml))) {
|
||||||
|
if (IsDebuggingNotifications())
|
||||||
|
LOG(INFO) << "Setting \"scenario\" option on notification failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the toast's notification sound
|
// Configure the toast's notification sound
|
||||||
if (silent) {
|
if (silent) {
|
||||||
if (FAILED(SetXmlAudioSilent(*toast_xml))) {
|
if (FAILED(SetXmlAudioSilent(*toast_xml))) {
|
||||||
|
@ -201,6 +212,56 @@ bool WindowsToastNotification::GetToastXml(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WindowsToastNotification::SetXmlScenarioReminder(IXmlDocument* doc) {
|
||||||
|
ScopedHString tag(L"toast");
|
||||||
|
if (!tag.success())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlNodeList> node_list;
|
||||||
|
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check that root "toast" node exists
|
||||||
|
ComPtr<IXmlNode> root;
|
||||||
|
if (FAILED(node_list->Item(0, &root)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// get attributes of root "toast" node
|
||||||
|
ComPtr<IXmlNamedNodeMap> attributes;
|
||||||
|
if (FAILED(root->get_Attributes(&attributes)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlAttribute> scenario_attribute;
|
||||||
|
ScopedHString scenario_str(L"scenario");
|
||||||
|
if (FAILED(doc->CreateAttribute(scenario_str, &scenario_attribute)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlNode> scenario_attribute_node;
|
||||||
|
if (FAILED(scenario_attribute.As(&scenario_attribute_node)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ScopedHString scenario_value(L"reminder");
|
||||||
|
if (!scenario_value.success())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlText> scenario_text;
|
||||||
|
if (FAILED(doc->CreateTextNode(scenario_value, &scenario_text)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlNode> scenario_node;
|
||||||
|
if (FAILED(scenario_text.As(&scenario_node)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlNode> child_node;
|
||||||
|
if (FAILED(scenario_attribute_node->AppendChild(scenario_node.Get(),
|
||||||
|
&child_node)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ComPtr<IXmlNode> scenario_attribute_pnode;
|
||||||
|
return SUCCEEDED(attributes.Get()->SetNamedItem(scenario_attribute_node.Get(),
|
||||||
|
&scenario_attribute_pnode));
|
||||||
|
}
|
||||||
|
|
||||||
bool WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
|
bool WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
|
||||||
ScopedHString tag(L"toast");
|
ScopedHString tag(L"toast");
|
||||||
if (!tag.success())
|
if (!tag.success())
|
||||||
|
|
|
@ -63,9 +63,11 @@ class WindowsToastNotification : public Notification {
|
||||||
const std::wstring& title,
|
const std::wstring& title,
|
||||||
const std::wstring& msg,
|
const std::wstring& msg,
|
||||||
const std::wstring& icon_path,
|
const std::wstring& icon_path,
|
||||||
|
const std::wstring& timeout_type,
|
||||||
const bool silent,
|
const bool silent,
|
||||||
ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml);
|
ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml);
|
||||||
bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
||||||
|
bool SetXmlScenarioReminder(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
|
||||||
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
const std::wstring& text);
|
const std::wstring& text);
|
||||||
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
|
||||||
|
|
Loading…
Reference in a new issue