wails
wails copied to clipboard
Notification support
Is your feature request related to a problem? Please describe.
We want to show a notification toast to hint information to users, but it seems no such function in Wails and its roadmap.
Describe the solution you'd like
Notification should be added.
Describe alternatives you've considered
Toast function which is similiar to Android Toas.
Additional context
No response
I don't know if there is a plan for notifications, but beeep is a good package for native notifications IMO: https://github.com/gen2brain/beeep .
Added to Roadmap 👍
As @leaanthony suggested, I'm trying to put together an API proposal and I'm looking at the notification capabilities of Windows, macOs and linux. Windows has the most extensive functionality, linux and macOS has a "simpler" API.
My question would be, what is the goal, a common API with functions that work everywhere, or one where each OS can have its own settings, or some form of hybrid from the two with an override style solution.
Case one: A common API for all supported OS with "limited" functionality.
- title
- message
- icon
- image (only windows)
- action buttons
We may lose some functionality on windows, but when you create a notification it would behave the same way on every OS.
Case two: An API with separate options for each OS.
One would have to configure every notification for each OS they want to support, probably the only common options for a notification would be the title and a message (and maybe action buttons).
Case three: An API with a "limited" common functionality but with separate options for each OS.
The main API would work as described in the first case with the basic functionality, but has OS specific options like timing, sound, position, or for windows status/download bars, etc.
Awesome @Lyimmi ! As far as API goes, it's ok to have a single struct of options for all platforms where options are ignored if not supported 👍
Here is my first draft of the Notification API. I think if we want to have more than a title, message and an icon, we need to have OS specific options. As far as I can tell Electron only supports these three options as default.
Even the timeout is different on each os, on linux it's set in milliseconds, on windows it's a dateTime, on macOs its not available. Handling the actions / buttons is totally different as well.
Maybe the click on the notification could be handled commonly too.
It is more of a conversation starter than a complete API.
// LinuxActionInvokedHandler handles signals returned by activating actions
type LinuxActionInvokedHandler func(signal *LinuxNotificationActionInvokedSignal, options ...interface{})
// LinuxNotificationAction represents a Notification action for a notification
type LinuxNotificationAction struct {
// Key is the actions's identifier
Key string
// Label is shown on the notification
Label string
}
// LinuxNotificationActionInvokedSignal holds data from any signal received regarding Actions invoked
type LinuxNotificationActionInvokedSignal struct {
// ID of the Notification the action was invoked for
NotificationID uint32
// Key from the activated action
ActionKey string
}
type LinuxNotificationOptions struct {
// ReplacesID is used to replace an existing notification.
ReplacesID uint32
// Actions to be shown.
Actions []LinuxNotificationAction
// OnAction handles the activated action's signal.
OnAction LinuxActionInvokedHandler
// Timeout of the notification
Timeout time.Duration
}
// NotificationProgressBar shows a progress bar. (Windows only)
type NotificationProgressBar struct {
// Gets or sets an optional title string. Supports data binding.
Title string
// Gets or sets the value of the progress bar. Supports data binding.
// Defaults to 0. Can either be a double between 0.0 and 1.0, AdaptiveProgressBarValue.
// Indeterminate, or new BindableProgressBarValue("myProgressValue").
Value float32
// Gets or sets an optional string to be displayed instead of the default percentage string.
// If this isn't provided, something like "70%" will be displayed.
ValueStringOverride string
// Gets or sets a status string (required), which is displayed underneath the progress bar on the left.
// This string should reflect the status of the operation, like "Downloading..." or "Installing..."
Status string
}
// NotificationHeroImage A featured "hero" image that is displayed on the toast and within Action Center. (Windows only)
type NotificationHeroImage struct {
// The URL to the image. ms-appx, ms-appdata, and http are supported.
// Http images must be 200 KB or less in size.
Source string
// Alternate text describing the image, used for accessibility purposes.
AlternateText string
// Set to "true" to allow Windows to append a query string to the image URL
// supplied in the toast notification. Use this attribute if your server hosts
// images and can handle query strings, either by retrieving an image
// variant based on the query strings or by ignoring the query string and
// returning the image as specified without the query string.
//
// This query string specifies scale, contrast setting, and language; for instance,
// a value of "www.website.com/images/hello.png" given in the notification becomes
// "www.website.com/images/hello.png?ms-scale=100&ms-contrast=standard&ms-lang=en-us"
AddImageQuery bool
}
type WindowsNotificationOptions struct {
// HeroImage shows a full size image.
HeroImage *NotificationHeroImage
// ProgressBar shows a progress bar style notification.
ProgressBar *NotificationProgressBar
// ExpirationTime sets the expiration time for the notification.
ExpirationTime time.Time
}
// NotificationOptions sets up a notification to be sent.
type NotificationOptions struct {
// Applications identifier.
AppID string
// AppIcon used to show an icon on the notification. Absolute path to the icon.
AppIcon string
// Title is a summary for the notification.
Title string
// Message is the notification's message.
Message string
// LinuxOptions holds the linux specific options for a notification.
LinuxOptions *LinuxNotificationOptions
// WindowsOptions holds the windows specific options for a notification.
WindowsOptions *WindowsNotificationOptions
// Timeout is the duration for how long a notification is shown.
Timeout time.Duration
}
Usage would look something like this:
no := NotificationOptions{
AppID: "Wails test",
AppIcon: "/wails/build/appicon.png",
Title: "wails test",
Message: name,
LinuxOptions: &LinuxNotificationOptions{
ReplacesID: 0,
Actions: []LinuxNotificationAction{
{Key: "btn-1", Label: "Button 1"},
{Key: "btn-2", Label: "Button 2"},
},
OnAction: func(signal *LinuxNotificationActionInvokedSignal, options ...interface{}) {
fmt.Printf("key: %v, label: %v", signal.NotificationID, signal.ActionKey)
},
Timeout: time.Second * 10,
},
WindowsOptions: &WindowsNotificationOptions{
HeroImage: &NotificationHeroImage{
Source: "/path/to/image.jpg",
AlternateText: "alternate text",
AddImageQuery: false,
},
ProgressBar: &NotificationProgressBar{
Title: "title",
Value: 0.5,
ValueStringOverride: "10 out of a 100",
Status: "progress...",
},
ExpirationTime: time.Now().Add(24 * time.Hour),
},
}
runtime.SendNotification(ctx, no)
For a work side project, I needed something to send notifications on macOS. All of the existing libraries I found were either deprecated or no longer maintained (and so features didn't work on recent versions of macOS), or the beeep one suggested earlier in this issue would cause script manager to launch when used, which isn't nice UX.
However I found a library / binary (written in Objective-C) that is maintained and fairly feature complete but needed a Go library to work with it. So I created one that I'm using.
https://github.com/willdot/gomacosnotify
It does require the Alterer binary to be embedded and then installed into a temp location on the users machine, however I'm sure there's a way to stop that so that it's opt in (ie you want notifications and so the binary then gets used), possibly via build tags?
Happy to work together to get something working for notifications working (on macOS) using the library I've created and open to API changes etc.
Hi, @leaanthony, @stffabi, @willdot
I started implementing the notification support on this branch: https://github.com/Lyimmi/wails/tree/feature/1788_notification_support
It is still in early stages and currently only supports linux/dbus (started the nofiy-send and kdialog fallbacks but with a minimal functionality).
Should I make a draft pull request, or wait for a more complete stage? It would be nice if someone else looked at it. Whether it's going in the right direction at all.
Thanks!
Edit: Made some changes to the API, currently this is how it looks like.
res, err := runtime.SendNotification(a.ctx, runtime.NotificationOptions{
AppIcon: "/wailst/build/appicon.png",
Title: "This is a title",
Message: "This is a message",
Timeout: 60 * time.Second,
LinuxOptions: &runtime.LinuxNotificationOptions{
Actions: []runtime.LinuxNotificationAction{
{
Key: "maximize",
Label: "Maximize",
OnAction: func(signal *runtime.LinuxNotificationActionInvokedSignal) {
runtime.WindowMaximise(a.ctx)
},
},
{
Key: "minimize",
Label: "Minimize",
OnAction: func(signal *runtime.LinuxNotificationActionInvokedSignal) {
runtime.WindowMinimise(a.ctx)
},
},
},
},
})
Thanks for the work on this @Lyimmi! I'm happy for you to push on with this. One question: is the path to the icon something that'll work at runtime or should we be using a []byte
?
@leaanthony all supported OSes are using absolute paths as default and all of them have some optional unique way too. This is one of my pain points as well, because we cannot use embedded resources.
One way could be to use []byte as input and save the images to tmp if its not supported and then point to it?
I think there's no option because how does it work from a distribution point of view?
Well, yes. But it raises a few of questions.
- Should all notification generate a tmp image or use some sort of hash to create images only once? eg. md5 the image check if a file exists in tmp, if exists use that?
- Should we impose a size limit?
- Should we check for mime-type, jpg,jpeg,png,gif etc?
- When should we clean up these files, or leave it to the OS? If a program shows 10 notifications with the same image, does it break when we delete the image that is showing?
Another advantage of the []byte route is that we could create a standard api for showing images via http.
Another subject. For windows I can't figure out, how to listen to toast action signals... Probably we'll need a direct winapi call and the last time I poked at that was about 8-10 years ago.
The documentation is rather confusing to me. https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-notifications-overview To be fair this is even wors: https://specifications.freedesktop.org/notification-spec/latest/index.html#introduction
As a simple drop in for windows I used this https://github.com/martinlindhe/notify, but this package only handles opening other applications and web pages.
Hey there 👋
Regarding the Q's, My approach would be to generate a random filename prefix at startup so icons are unique to the process. Before issuing the notification just write the file again. It sounds inefficient but notifications will be few and far between and the cost of writing a 32k icon will be miniscule vs the overhead of reading, hashing and potentially writing anyway, plus writing and testing code for all that. We can defer a delete but it's not even critical to do that as temp files will be vacuumed at some point by the OS. Regarding file type, the approach I've been taking is to use PNGs as source and generate OS appropriate file types from that if needed. I wouldn't bother with file limit unless you are required to by the OS.
I can probably sort out the windows side. Managed to get in the zone over the last 6 months.
Hi @leaanthony, it was a good idea, works nicely on Windows and linux as well.
Hi, am at a stage where Windows, macOS and Linux can show notifications. Windows and macOS needs more work. Linux is almost there I think. I wrote the docs that represent the current state of things.
- On macOS, I used @willdot's package gomacosnotify wrapped with the runtime. Unfortunately gomacosnotify cannot show icons on the notificaitons.
- On Windows, I used go-toast/toast this package is quite limited. Same as on macOS it is wrapped by the runtime. This is more like a placeholder than a real solution.
- On linux, notifications are handled with dbus and fallback to libnotify via os.Exec(). I tested it on PopOS 22.04, Ubuntu 22.04, Linux Mint 21 Cinnamon, I need to test it on some KDE distros as well.
This is awesome! I may be able to help with the MacOS notification icons by implementing it in Objective-C. I'll be moving to mac in the next week or so to do tray menus so can fit this in at the same time 👍
@Lyimmi amazing work, I had the same issue trying to work for actually listening to toast events for Windows. While it's cool.. the actual underlying API for toasts doesn't seem very thought out in terms of other apps, other than uwp using toast notifications. The few times I did get the listener working for the notification, (which I had to do with c# mind you) it was almost impossible to get the related action data. Like what button was clicked. What text was entered, what radio button was selected.. I think it's crucial to get events from notifications but in terms of a alpha feature.. don't focus too much on getting the events working. It'll only give you headaches. I'm willing to help, but my knowledge of windows apis, and c++/c in general is very very limited..
@Lyimmi amazing work, I had the same issue trying to work for actually listening to toast events for Windows. While it's cool.. the actual underlying API for toasts doesn't seem very thought out in terms of other apps, other than uwp using toast notifications. The few times I did get the listener working for the notification, (which I had to do with c# mind you) it was almost impossible to get the related action data. Like what button was clicked. What text was entered, what radio button was selected.. I think it's crucial to get events from notifications but in terms of a alpha feature.. don't focus too much on getting the events working. It'll only give you headaches. I'm willing to help, but my knowledge of windows apis, and c++/c in general is very very limited..
It might be worth actually contacting a Microsoft developer about this. It doesn't seem like even their website explains how to get data from toasts/receive events?
Here's a developer support email: [email protected]
For OSX it is possible to schedule and cancel notifications: https://developer.apple.com/documentation/usernotifications/scheduling_a_notification_locally_from_your_app
Hey folks :)
Do you have any news about this feature? Is it still on-going?
No updates. I believe it's pretty difficult beyond very basic notifications. I'm hoping we can get a basic plugin working for v3
What about https://github.com/gen2brain/beeep ?
Or should we just use native apis directly instead of a library?
No updates. I believe it's pretty difficult beyond very basic notifications. I'm hoping we can get a basic plugin working for v3
I found this function that should work at least in Mac, although it just uses the osascript
mac binary.
https://github.com/wailsapp/wails/blob/12d633642115f077a67dac5a65180581062eb5e0/v2/pkg/mac/notification_darwin.go#L14
I'm guessing the difficult part to come up with a more-or-less uniform API across systems?
any updates on the progress of this? looks like this is the only blocker for v3