mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-04 02:02:55 +08:00
217 lines
7.3 KiB
Go
217 lines
7.3 KiB
Go
// Package notifications provides cross-platform notification capabilities for desktop applications.
|
|
// It supports macOS, Windows, and Linux with a consistent API while handling platform-specific
|
|
// differences internally. Key features include:
|
|
// - Basic notifications with title, subtitle, and body
|
|
// - Interactive notifications with buttons and actions
|
|
// - Notification categories for reusing configurations
|
|
// - User feedback handling with a unified callback system
|
|
//
|
|
// Platform-specific notes:
|
|
// - macOS: Requires a properly bundled and signed application
|
|
// - Windows: Uses Windows Toast notifications
|
|
// - Linux: Uses D-Bus and does not support text inputs
|
|
package notifications
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/wailsapp/wails/v3/pkg/application"
|
|
)
|
|
|
|
type platformNotifier interface {
|
|
// Lifecycle methods
|
|
Startup(ctx context.Context, options application.ServiceOptions) error
|
|
Shutdown() error
|
|
|
|
// Core notification methods
|
|
RequestNotificationAuthorization() (bool, error)
|
|
CheckNotificationAuthorization() (bool, error)
|
|
SendNotification(options NotificationOptions) error
|
|
SendNotificationWithActions(options NotificationOptions) error
|
|
|
|
// Category management
|
|
RegisterNotificationCategory(category NotificationCategory) error
|
|
RemoveNotificationCategory(categoryID string) error
|
|
|
|
// Notification management
|
|
RemoveAllPendingNotifications() error
|
|
RemovePendingNotification(identifier string) error
|
|
RemoveAllDeliveredNotifications() error
|
|
RemoveDeliveredNotification(identifier string) error
|
|
RemoveNotification(identifier string) error
|
|
}
|
|
|
|
// Service represents the notifications service
|
|
type Service struct {
|
|
impl platformNotifier
|
|
|
|
// notificationResponseCallback is called when a notification result is received.
|
|
// Only one callback can be assigned at a time.
|
|
notificationResultCallback func(result NotificationResult)
|
|
|
|
callbackLock sync.RWMutex
|
|
}
|
|
|
|
var (
|
|
notificationServiceOnce sync.Once
|
|
NotificationService *Service
|
|
notificationServiceLock sync.RWMutex
|
|
)
|
|
|
|
// NotificationAction represents an action button for a notification.
|
|
type NotificationAction struct {
|
|
ID string `json:"id,omitempty"`
|
|
Title string `json:"title,omitempty"`
|
|
Destructive bool `json:"destructive,omitempty"` // (macOS-specific)
|
|
}
|
|
|
|
// NotificationCategory groups actions for notifications.
|
|
type NotificationCategory struct {
|
|
ID string `json:"id,omitempty"`
|
|
Actions []NotificationAction `json:"actions,omitempty"`
|
|
HasReplyField bool `json:"hasReplyField,omitempty"`
|
|
ReplyPlaceholder string `json:"replyPlaceholder,omitempty"`
|
|
ReplyButtonTitle string `json:"replyButtonTitle,omitempty"`
|
|
}
|
|
|
|
// NotificationOptions contains configuration for a notification
|
|
type NotificationOptions struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Subtitle string `json:"subtitle,omitempty"` // (macOS and Linux only)
|
|
Body string `json:"body,omitempty"`
|
|
CategoryID string `json:"categoryId,omitempty"`
|
|
Data map[string]interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
const DefaultActionIdentifier = "DEFAULT_ACTION"
|
|
|
|
// NotificationResponse represents the response sent by interacting with a notification.
|
|
type NotificationResponse struct {
|
|
ID string `json:"id,omitempty"`
|
|
ActionIdentifier string `json:"actionIdentifier,omitempty"`
|
|
CategoryID string `json:"categoryIdentifier,omitempty"`
|
|
Title string `json:"title,omitempty"`
|
|
Subtitle string `json:"subtitle,omitempty"` // (macOS and Linux only)
|
|
Body string `json:"body,omitempty"`
|
|
UserText string `json:"userText,omitempty"`
|
|
UserInfo map[string]interface{} `json:"userInfo,omitempty"`
|
|
}
|
|
|
|
// NotificationResult represents the result of a notification response,
|
|
// returning the response or any errors that occurred.
|
|
type NotificationResult struct {
|
|
Response NotificationResponse
|
|
Error error
|
|
}
|
|
|
|
// ServiceName returns the name of the service.
|
|
func (ns *Service) ServiceName() string {
|
|
return "github.com/wailsapp/wails/v3/services/notifications"
|
|
}
|
|
|
|
// OnNotificationResponse registers a callback function that will be called when
|
|
// a notification response is received from the user.
|
|
//
|
|
//wails:ignore
|
|
func (ns *Service) OnNotificationResponse(callback func(result NotificationResult)) {
|
|
ns.callbackLock.Lock()
|
|
defer ns.callbackLock.Unlock()
|
|
|
|
ns.notificationResultCallback = callback
|
|
}
|
|
|
|
// handleNotificationResponse is an internal method to handle notification responses
|
|
// and invoke the registered callback if one exists.
|
|
func (ns *Service) handleNotificationResult(result NotificationResult) {
|
|
ns.callbackLock.RLock()
|
|
callback := ns.notificationResultCallback
|
|
ns.callbackLock.RUnlock()
|
|
|
|
if callback != nil {
|
|
callback(result)
|
|
}
|
|
}
|
|
|
|
// ServiceStartup is called when the service is loaded.
|
|
func (ns *Service) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
|
return ns.impl.Startup(ctx, options)
|
|
}
|
|
|
|
// ServiceShutdown is called when the service is unloaded.
|
|
func (ns *Service) ServiceShutdown() error {
|
|
return ns.impl.Shutdown()
|
|
}
|
|
|
|
// Public methods that delegate to the implementation.
|
|
func (ns *Service) RequestNotificationAuthorization() (bool, error) {
|
|
return ns.impl.RequestNotificationAuthorization()
|
|
}
|
|
|
|
func (ns *Service) CheckNotificationAuthorization() (bool, error) {
|
|
return ns.impl.CheckNotificationAuthorization()
|
|
}
|
|
|
|
func (ns *Service) SendNotification(options NotificationOptions) error {
|
|
if err := validateNotificationOptions(options); err != nil {
|
|
return err
|
|
}
|
|
return ns.impl.SendNotification(options)
|
|
}
|
|
|
|
func (ns *Service) SendNotificationWithActions(options NotificationOptions) error {
|
|
if err := validateNotificationOptions(options); err != nil {
|
|
return err
|
|
}
|
|
return ns.impl.SendNotificationWithActions(options)
|
|
}
|
|
|
|
func (ns *Service) RegisterNotificationCategory(category NotificationCategory) error {
|
|
return ns.impl.RegisterNotificationCategory(category)
|
|
}
|
|
|
|
func (ns *Service) RemoveNotificationCategory(categoryID string) error {
|
|
return ns.impl.RemoveNotificationCategory(categoryID)
|
|
}
|
|
|
|
func (ns *Service) RemoveAllPendingNotifications() error {
|
|
return ns.impl.RemoveAllPendingNotifications()
|
|
}
|
|
|
|
func (ns *Service) RemovePendingNotification(identifier string) error {
|
|
return ns.impl.RemovePendingNotification(identifier)
|
|
}
|
|
|
|
func (ns *Service) RemoveAllDeliveredNotifications() error {
|
|
return ns.impl.RemoveAllDeliveredNotifications()
|
|
}
|
|
|
|
func (ns *Service) RemoveDeliveredNotification(identifier string) error {
|
|
return ns.impl.RemoveDeliveredNotification(identifier)
|
|
}
|
|
|
|
func (ns *Service) RemoveNotification(identifier string) error {
|
|
return ns.impl.RemoveNotification(identifier)
|
|
}
|
|
|
|
func getNotificationService() *Service {
|
|
notificationServiceLock.RLock()
|
|
defer notificationServiceLock.RUnlock()
|
|
return NotificationService
|
|
}
|
|
|
|
// validateNotificationOptions validates an ID and Title are provided for notifications.
|
|
func validateNotificationOptions(options NotificationOptions) error {
|
|
if options.ID == "" {
|
|
return fmt.Errorf("notification ID cannot be empty")
|
|
}
|
|
|
|
if options.Title == "" {
|
|
return fmt.Errorf("notification title cannot be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|