Skip to content

Commit f80a17c

Browse files
codebytereJohn Kleinschmidt
authored andcommitted
feat: allow Linux/Windows users to set notification timeout (electron#20153)
* feat: allow Linux users to set notification timeout * implement on windows
1 parent 5e11be6 commit f80a17c

7 files changed

Lines changed: 92 additions & 3 deletions

File tree

docs/api/notification.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Returns `Boolean` - Whether or not desktop notifications are supported on the cu
3535
* `silent` Boolean (optional) - Whether or not to emit an OS notification noise when showing the notification.
3636
* `icon` (String | [NativeImage](native-image.md)) (optional) - An icon to use in the notification.
3737
* `hasReply` Boolean (optional) _macOS_ - Whether or not to add an inline reply option to the notification.
38+
* `timeoutType` String (optional) _Linux_ _Windows_ - The timeout duration of the notification. Can be 'default' or 'never'.
3839
* `replyPlaceholder` String (optional) _macOS_ - The placeholder to write in the inline reply input field.
3940
* `sound` String (optional) _macOS_ - The name of the sound file to play when the notification is shown.
4041
* `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 '
151152

152153
Default is 'low' - see [NotifyUrgency](https://developer.gnome.org/notification-spec/#urgency-levels) for more information.
153154

155+
#### `notification.timeoutType` _Linux_ _Windows_
156+
157+
A `String` property representing the type of timeout duration for the notification. Can be 'default' or 'never'.
158+
159+
If `timeoutType` is set to 'never', the notification never expires. It stays open until closed by the calling API or the user.
160+
154161
#### `notification.actions`
155162

156163
A [`NotificationAction[]`](structures/notification-action.md) property representing the actions of the notification.

shell/browser/api/atom_api_notification.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Notification::Notification(v8::Local<v8::Object> wrapper,
6969
opts.Get("replyPlaceholder", &reply_placeholder_);
7070
opts.Get("urgency", &urgency_);
7171
opts.Get("hasReply", &has_reply_);
72+
opts.Get("timeoutType", &timeout_type_);
7273
opts.Get("actions", &actions_);
7374
opts.Get("sound", &sound_);
7475
opts.Get("closeButtonText", &close_button_text_);
@@ -111,6 +112,10 @@ bool Notification::GetHasReply() const {
111112
return has_reply_;
112113
}
113114

115+
base::string16 Notification::GetTimeoutType() const {
116+
return timeout_type_;
117+
}
118+
114119
base::string16 Notification::GetReplyPlaceholder() const {
115120
return reply_placeholder_;
116121
}
@@ -152,6 +157,10 @@ void Notification::SetHasReply(bool new_has_reply) {
152157
has_reply_ = new_has_reply;
153158
}
154159

160+
void Notification::SetTimeoutType(const base::string16& new_timeout_type) {
161+
timeout_type_ = new_timeout_type;
162+
}
163+
155164
void Notification::SetReplyPlaceholder(const base::string16& new_placeholder) {
156165
reply_placeholder_ = new_placeholder;
157166
}
@@ -216,6 +225,7 @@ void Notification::Show() {
216225
options.icon = icon_.AsBitmap();
217226
options.silent = silent_;
218227
options.has_reply = has_reply_;
228+
options.timeout_type = timeout_type_;
219229
options.reply_placeholder = reply_placeholder_;
220230
options.actions = actions_;
221231
options.sound = sound_;
@@ -246,6 +256,8 @@ void Notification::BuildPrototype(v8::Isolate* isolate,
246256
.SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent)
247257
.SetProperty("hasReply", &Notification::GetHasReply,
248258
&Notification::SetHasReply)
259+
.SetProperty("timeoutType", &Notification::GetTimeoutType,
260+
&Notification::SetTimeoutType)
249261
.SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder,
250262
&Notification::SetReplyPlaceholder)
251263
.SetProperty("urgency", &Notification::GetUrgency,

shell/browser/api/atom_api_notification.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class Notification : public mate::TrackableObject<Notification>,
5454
base::string16 GetBody() const;
5555
bool GetSilent() const;
5656
bool GetHasReply() const;
57+
base::string16 GetTimeoutType() const;
5758
base::string16 GetReplyPlaceholder() const;
5859
base::string16 GetUrgency() const;
5960
base::string16 GetSound() const;
@@ -67,6 +68,7 @@ class Notification : public mate::TrackableObject<Notification>,
6768
void SetSilent(bool new_silent);
6869
void SetHasReply(bool new_has_reply);
6970
void SetUrgency(const base::string16& new_urgency);
71+
void SetTimeoutType(const base::string16& new_timeout_type);
7072
void SetReplyPlaceholder(const base::string16& new_reply_placeholder);
7173
void SetSound(const base::string16& sound);
7274
void SetActions(const std::vector<electron::NotificationAction>& actions);
@@ -81,6 +83,7 @@ class Notification : public mate::TrackableObject<Notification>,
8183
bool has_icon_ = false;
8284
bool silent_ = false;
8385
bool has_reply_ = false;
86+
base::string16 timeout_type_;
8487
base::string16 reply_placeholder_;
8588
base::string16 sound_;
8689
base::string16 urgency_;

shell/browser/notifications/linux/libnotify_notification.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,14 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
114114
GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(options.icon);
115115
libnotify_loader_.notify_notification_set_image_from_pixbuf(notification_,
116116
pixbuf);
117-
libnotify_loader_.notify_notification_set_timeout(notification_,
118-
NOTIFY_EXPIRES_DEFAULT);
119117
g_object_unref(pixbuf);
120118
}
121119

120+
// Set the timeout duration for the notification
121+
bool neverTimeout = options.timeout_type == base::ASCIIToUTF16("never");
122+
int timeout = (neverTimeout) ? NOTIFY_EXPIRES_NEVER : NOTIFY_EXPIRES_DEFAULT;
123+
libnotify_loader_.notify_notification_set_timeout(notification_, timeout);
124+
122125
if (!options.tag.empty()) {
123126
GQuark id = g_quark_from_string(options.tag.c_str());
124127
g_object_set(G_OBJECT(notification_), "id", id, NULL);

shell/browser/notifications/notification.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct NotificationOptions {
3232
GURL icon_url;
3333
SkBitmap icon;
3434
bool has_reply;
35+
base::string16 timeout_type;
3536
base::string16 reply_placeholder;
3637
base::string16 sound;
3738
base::string16 urgency; // Linux

shell/browser/notifications/win/windows_toast_notification.cc

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ void WindowsToastNotification::Show(const NotificationOptions& options) {
9494

9595
ComPtr<IXmlDocument> toast_xml;
9696
if (FAILED(GetToastXml(toast_manager_.Get(), options.title, options.msg,
97-
icon_path, options.silent, &toast_xml))) {
97+
icon_path, options.timeout_type, options.silent,
98+
&toast_xml))) {
9899
NotificationFailed();
99100
return;
100101
}
@@ -149,6 +150,7 @@ bool WindowsToastNotification::GetToastXml(
149150
const std::wstring& title,
150151
const std::wstring& msg,
151152
const std::wstring& icon_path,
153+
const std::wstring& timeout_type,
152154
bool silent,
153155
IXmlDocument** toast_xml) {
154156
ABI::Windows::UI::Notifications::ToastTemplateType template_type;
@@ -183,6 +185,15 @@ bool WindowsToastNotification::GetToastXml(
183185
}
184186
}
185187

188+
// Configure the toast's timeout settings
189+
if (timeout_type == base::ASCIIToUTF16("never")) {
190+
if (FAILED(SetXmlScenarioReminder(*toast_xml))) {
191+
if (IsDebuggingNotifications())
192+
LOG(INFO) << "Setting \"scenario\" option on notification failed";
193+
return false;
194+
}
195+
}
196+
186197
// Configure the toast's notification sound
187198
if (silent) {
188199
if (FAILED(SetXmlAudioSilent(*toast_xml))) {
@@ -201,6 +212,56 @@ bool WindowsToastNotification::GetToastXml(
201212
return true;
202213
}
203214

215+
bool WindowsToastNotification::SetXmlScenarioReminder(IXmlDocument* doc) {
216+
ScopedHString tag(L"toast");
217+
if (!tag.success())
218+
return false;
219+
220+
ComPtr<IXmlNodeList> node_list;
221+
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
222+
return false;
223+
224+
// Check that root "toast" node exists
225+
ComPtr<IXmlNode> root;
226+
if (FAILED(node_list->Item(0, &root)))
227+
return false;
228+
229+
// get attributes of root "toast" node
230+
ComPtr<IXmlNamedNodeMap> attributes;
231+
if (FAILED(root->get_Attributes(&attributes)))
232+
return false;
233+
234+
ComPtr<IXmlAttribute> scenario_attribute;
235+
ScopedHString scenario_str(L"scenario");
236+
if (FAILED(doc->CreateAttribute(scenario_str, &scenario_attribute)))
237+
return false;
238+
239+
ComPtr<IXmlNode> scenario_attribute_node;
240+
if (FAILED(scenario_attribute.As(&scenario_attribute_node)))
241+
return false;
242+
243+
ScopedHString scenario_value(L"reminder");
244+
if (!scenario_value.success())
245+
return false;
246+
247+
ComPtr<IXmlText> scenario_text;
248+
if (FAILED(doc->CreateTextNode(scenario_value, &scenario_text)))
249+
return false;
250+
251+
ComPtr<IXmlNode> scenario_node;
252+
if (FAILED(scenario_text.As(&scenario_node)))
253+
return false;
254+
255+
ComPtr<IXmlNode> child_node;
256+
if (FAILED(scenario_attribute_node->AppendChild(scenario_node.Get(),
257+
&child_node)))
258+
return false;
259+
260+
ComPtr<IXmlNode> scenario_attribute_pnode;
261+
return SUCCEEDED(attributes.Get()->SetNamedItem(scenario_attribute_node.Get(),
262+
&scenario_attribute_pnode));
263+
}
264+
204265
bool WindowsToastNotification::SetXmlAudioSilent(IXmlDocument* doc) {
205266
ScopedHString tag(L"toast");
206267
if (!tag.success())

shell/browser/notifications/win/windows_toast_notification.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ class WindowsToastNotification : public Notification {
6363
const std::wstring& title,
6464
const std::wstring& msg,
6565
const std::wstring& icon_path,
66+
const std::wstring& timeout_type,
6667
const bool silent,
6768
ABI::Windows::Data::Xml::Dom::IXmlDocument** toastXml);
6869
bool SetXmlAudioSilent(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
70+
bool SetXmlScenarioReminder(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc);
6971
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,
7072
const std::wstring& text);
7173
bool SetXmlText(ABI::Windows::Data::Xml::Dom::IXmlDocument* doc,

0 commit comments

Comments
 (0)