5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-10 02:40:26 +08:00

refactor/simplify linux

This commit is contained in:
popaprozac 2025-03-20 16:32:53 -07:00
parent 9adeef2bdf
commit 5dc7bd97f2

View File

@ -17,12 +17,36 @@ import (
) )
type linuxNotifier struct { type linuxNotifier struct {
// Categories
categories map[string]NotificationCategory categories map[string]NotificationCategory
categoriesLock sync.RWMutex categoriesLock sync.RWMutex
// App info
appName string appName string
internal *internalNotifier
notificationInitErr error // Notification system
sync.Mutex
method string
dbusConn *dbus.Conn
sendPath string
activeNotifs map[string]uint32 // Maps our notification IDs to system IDs
contexts map[string]*notificationContext // Stores notification contexts by our ID
// Listener management
listenerCtx context.Context
listenerCancel context.CancelFunc
listenerRunning bool
// Initialization
initOnce sync.Once initOnce sync.Once
initialized bool
}
type notificationContext struct {
ID string
SystemID uint32
Actions map[string]string // Maps action keys to display labels
UserData map[string]interface{} // The original user data
} }
const ( const (
@ -35,38 +59,19 @@ const (
MethodNotifySend = "notify-send" MethodNotifySend = "notify-send"
MethodDbus = "dbus" MethodDbus = "dbus"
MethodKdialog = "kdialog"
notifyChannelBufferSize = 25 notifyChannelBufferSize = 25
) )
type closedReason uint32 type closedReason uint32
// internalNotifier handles the actual notification sending via dbus or command line
type notificationContext struct {
ID string
SystemID uint32
Actions map[string]string // Maps action keys to display labels
UserData map[string]interface{} // The original user data
}
type internalNotifier struct {
sync.Mutex
method string
dbusConn *dbus.Conn
sendPath string
activeNotifs map[string]uint32 // Maps our notification IDs to system IDs
contexts map[string]*notificationContext // Stores notification contexts by our ID
listenerCtx context.Context
listenerCancel context.CancelFunc
listenerRunning bool
}
// New creates a new Notifications Service // New creates a new Notifications Service
func New() *Service { func New() *Service {
notificationServiceOnce.Do(func() { notificationServiceOnce.Do(func() {
impl := &linuxNotifier{ impl := &linuxNotifier{
categories: make(map[string]NotificationCategory), categories: make(map[string]NotificationCategory),
activeNotifs: make(map[string]uint32),
contexts: make(map[string]*notificationContext),
} }
NotificationService = &Service{ NotificationService = &Service{
@ -77,97 +82,96 @@ func New() *Service {
} }
// Startup is called when the service is loaded // Startup is called when the service is loaded
func (ls *linuxNotifier) Startup(ctx context.Context) error { func (ln *linuxNotifier) Startup(ctx context.Context) error {
ls.appName = application.Get().Config().Name ln.appName = application.Get().Config().Name
if err := ls.loadCategories(); err != nil { if err := ln.loadCategories(); err != nil {
fmt.Printf("Failed to load notification categories: %v\n", err) fmt.Printf("Failed to load notification categories: %v\n", err)
} }
// Initialize the internal notifier
ls.internal = &internalNotifier{
activeNotifs: make(map[string]uint32),
contexts: make(map[string]*notificationContext),
}
var err error var err error
ls.initOnce.Do(func() { ln.initOnce.Do(func() {
// Initialize notification system err = ln.initNotificationSystem()
err = ls.initNotificationSystem() ln.initialized = err == nil
}) })
return err return err
} }
// Shutdown is called when the service is unloaded // initNotificationSystem initializes the notification system
func (ls *linuxNotifier) Shutdown() error { func (ln *linuxNotifier) initNotificationSystem() error {
if ls.internal != nil { ln.Lock()
ls.internal.Lock() defer ln.Unlock()
defer ls.internal.Unlock()
// Cancel the listener context if it's running // Cancel any existing listener
if ls.internal.listenerCancel != nil { if ln.listenerCancel != nil {
ls.internal.listenerCancel() ln.listenerCancel()
ls.internal.listenerCancel = nil ln.listenerCancel = nil
}
// Close the connection
if ls.internal.dbusConn != nil {
ls.internal.dbusConn.Close()
ls.internal.dbusConn = nil
}
// Clear state
ls.internal.activeNotifs = make(map[string]uint32)
ls.internal.contexts = make(map[string]*notificationContext)
ls.internal.method = "none"
ls.internal.sendPath = ""
}
return ls.saveCategories()
}
// initNotificationSystem initializes the notification system, choosing the best available method
func (ls *linuxNotifier) initNotificationSystem() error {
var err error
// Cancel any existing listener before starting a new one
if ls.internal.listenerCancel != nil {
ls.internal.listenerCancel()
} }
// Create a new context for the listener // Create a new context for the listener
ls.internal.listenerCtx, ls.internal.listenerCancel = context.WithCancel(context.Background()) ln.listenerCtx, ln.listenerCancel = context.WithCancel(context.Background())
// Reset state // Reset state
ls.internal.activeNotifs = make(map[string]uint32) ln.activeNotifs = make(map[string]uint32)
ls.internal.contexts = make(map[string]*notificationContext) ln.contexts = make(map[string]*notificationContext)
ls.internal.listenerRunning = false ln.listenerRunning = false
checkDbus := func() (*dbus.Conn, error) { // Try dbus first
dbusConn, err := ln.initDBus()
if err == nil {
ln.dbusConn = dbusConn
ln.method = MethodDbus
// Start the dbus signal listener
go ln.startDBusListener(ln.listenerCtx)
ln.listenerRunning = true
return nil
}
// Try notify-send as fallback
sendPath, err := ln.initNotifySend()
if err == nil {
ln.sendPath = sendPath
ln.method = MethodNotifySend
return nil
}
// No method available
ln.method = ""
ln.sendPath = ""
return errors.New("no notification method is available")
}
// initDBus attempts to initialize D-Bus notifications
func (ln *linuxNotifier) initDBus() (*dbus.Conn, error) {
conn, err := dbus.SessionBusPrivate() conn, err := dbus.SessionBusPrivate()
if err != nil { if err != nil {
return conn, err return nil, err
} }
if err = conn.Auth(nil); err != nil { if err = conn.Auth(nil); err != nil {
return conn, err conn.Close()
return nil, err
} }
if err = conn.Hello(); err != nil { if err = conn.Hello(); err != nil {
return conn, err conn.Close()
return nil, err
} }
obj := conn.Object(dbusNotificationsInterface, dbusObjectPath) obj := conn.Object(dbusNotificationsInterface, dbusObjectPath)
call := obj.Call(callGetCapabilities, 0) call := obj.Call(callGetCapabilities, 0)
if call.Err != nil { if call.Err != nil {
return conn, call.Err conn.Close()
return nil, call.Err
} }
var ret []string var ret []string
err = call.Store(&ret) err = call.Store(&ret)
if err != nil { if err != nil {
return conn, err conn.Close()
return nil, err
} }
// Add a listener for notification signals // Add a listener for notification signals
@ -176,59 +180,68 @@ func (ls *linuxNotifier) initNotificationSystem() error {
dbus.WithMatchInterface(dbusNotificationsInterface), dbus.WithMatchInterface(dbusNotificationsInterface),
) )
if err != nil { if err != nil {
conn.Close()
return nil, err return nil, err
} }
return conn, nil return conn, nil
} }
// Try dbus first // initNotifySend attempts to find notify-send binary
ls.internal.dbusConn, err = checkDbus() func (ln *linuxNotifier) initNotifySend() (string, error) {
if err == nil { // Try standard notify-send
ls.internal.method = MethodDbus
// Start the dbus signal listener with context
go ls.startDBusListener(ls.internal.listenerCtx)
ls.internal.listenerRunning = true
return nil
}
if ls.internal.dbusConn != nil {
ls.internal.dbusConn.Close()
ls.internal.dbusConn = nil
}
// Try notify-send
send, err := exec.LookPath("notify-send") send, err := exec.LookPath("notify-send")
if err == nil { if err == nil {
ls.internal.sendPath = send return send, nil
ls.internal.method = MethodNotifySend
return nil
} }
// Try sw-notify-send // Try sw-notify-send (in some distros)
send, err = exec.LookPath("sw-notify-send") send, err = exec.LookPath("sw-notify-send")
if err == nil { if err == nil {
ls.internal.sendPath = send return send, nil
ls.internal.method = MethodNotifySend
return nil
} }
// No method available return "", errors.New("notify-send not found")
ls.internal.method = "none" }
ls.internal.sendPath = ""
return errors.New("no notification method is available") // Shutdown is called when the service is unloaded
func (ln *linuxNotifier) Shutdown() error {
ln.Lock()
// Cancel the listener context if it's running
if ln.listenerCancel != nil {
ln.listenerCancel()
ln.listenerCancel = nil
}
// Close the connection
if ln.dbusConn != nil {
ln.dbusConn.Close()
ln.dbusConn = nil
}
// Clear state
ln.activeNotifs = make(map[string]uint32)
ln.contexts = make(map[string]*notificationContext)
ln.method = ""
ln.sendPath = ""
ln.initialized = false
ln.Unlock()
return ln.saveCategories()
} }
// startDBusListener listens for DBus signals for notification actions and closures // startDBusListener listens for DBus signals for notification actions and closures
func (ls *linuxNotifier) startDBusListener(ctx context.Context) { func (ln *linuxNotifier) startDBusListener(ctx context.Context) {
signal := make(chan *dbus.Signal, notifyChannelBufferSize) signal := make(chan *dbus.Signal, notifyChannelBufferSize)
ls.internal.dbusConn.Signal(signal) ln.dbusConn.Signal(signal)
defer func() { defer func() {
ls.internal.Lock() ln.Lock()
ls.internal.listenerRunning = false ln.listenerRunning = false
ls.internal.Unlock() ln.Unlock()
ls.internal.dbusConn.RemoveSignal(signal) // Remove signal handler ln.dbusConn.RemoveSignal(signal) // Remove signal handler
close(signal) // Clean up channel close(signal) // Clean up channel
}() }()
@ -252,34 +265,34 @@ func (ls *linuxNotifier) startDBusListener(ctx context.Context) {
case signalNotificationClosed: case signalNotificationClosed:
systemID := s.Body[0].(uint32) systemID := s.Body[0].(uint32)
reason := closedReason(s.Body[1].(uint32)).string() reason := closedReason(s.Body[1].(uint32)).string()
ls.handleNotificationClosed(systemID, reason) ln.handleNotificationClosed(systemID, reason)
case signalActionInvoked: case signalActionInvoked:
systemID := s.Body[0].(uint32) systemID := s.Body[0].(uint32)
actionKey := s.Body[1].(string) actionKey := s.Body[1].(string)
ls.handleActionInvoked(systemID, actionKey) ln.handleActionInvoked(systemID, actionKey)
} }
} }
} }
} }
// handleNotificationClosed processes notification closed signals // handleNotificationClosed processes notification closed signals
func (ls *linuxNotifier) handleNotificationClosed(systemID uint32, reason string) { func (ln *linuxNotifier) handleNotificationClosed(systemID uint32, reason string) {
// Find our notification ID for this system ID // Find our notification ID for this system ID
var notifID string var notifID string
var userData map[string]interface{} var userData map[string]interface{}
ls.internal.Lock() ln.Lock()
for id, sysID := range ls.internal.activeNotifs { for id, sysID := range ln.activeNotifs {
if sysID == systemID { if sysID == systemID {
notifID = id notifID = id
// Get the user data from context if available // Get the user data from context if available
if ctx, exists := ls.internal.contexts[id]; exists { if ctx, exists := ln.contexts[id]; exists {
userData = ctx.UserData userData = ctx.UserData
} }
break break
} }
} }
ls.internal.Unlock() ln.Unlock()
if notifID != "" { if notifID != "" {
response := NotificationResponse{ response := NotificationResponse{
@ -304,28 +317,28 @@ func (ls *linuxNotifier) handleNotificationClosed(systemID uint32, reason string
} }
// Clean up the context // Clean up the context
ls.internal.Lock() ln.Lock()
delete(ls.internal.contexts, notifID) delete(ln.contexts, notifID)
delete(ls.internal.activeNotifs, notifID) delete(ln.activeNotifs, notifID)
ls.internal.Unlock() ln.Unlock()
} }
} }
// handleActionInvoked processes action invoked signals // handleActionInvoked processes action invoked signals
func (ls *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string) { func (ln *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string) {
// Find our notification ID and context for this system ID // Find our notification ID and context for this system ID
var notifID string var notifID string
var ctx *notificationContext var ctx *notificationContext
ls.internal.Lock() ln.Lock()
for id, sysID := range ls.internal.activeNotifs { for id, sysID := range ln.activeNotifs {
if sysID == systemID { if sysID == systemID {
notifID = id notifID = id
ctx = ls.internal.contexts[id] ctx = ln.contexts[id]
break break
} }
} }
ls.internal.Unlock() ln.Unlock()
if notifID != "" { if notifID != "" {
if actionKey == "default" { if actionKey == "default" {
@ -372,62 +385,58 @@ func (ls *linuxNotifier) handleActionInvoked(systemID uint32, actionKey string)
} }
// Clean up the context // Clean up the context
ls.internal.Lock() ln.Lock()
delete(ls.internal.contexts, notifID) delete(ln.contexts, notifID)
delete(ls.internal.activeNotifs, notifID) delete(ln.activeNotifs, notifID)
ls.internal.Unlock() ln.Unlock()
} }
} }
// CheckBundleIdentifier is a Linux stub that always returns true. // CheckBundleIdentifier is a Linux stub that always returns true.
// (bundle identifiers are macOS-specific) func (ln *linuxNotifier) CheckBundleIdentifier() bool {
func (ls *linuxNotifier) CheckBundleIdentifier() bool {
return true return true
} }
// RequestNotificationAuthorization is a Linux stub that always returns true. // RequestNotificationAuthorization is a Linux stub that always returns true.
// (user authorization is macOS-specific) func (ln *linuxNotifier) RequestNotificationAuthorization() (bool, error) {
func (ls *linuxNotifier) RequestNotificationAuthorization() (bool, error) {
return true, nil return true, nil
} }
// CheckNotificationAuthorization is a Linux stub that always returns true. // CheckNotificationAuthorization is a Linux stub that always returns true.
// (user authorization is macOS-specific) func (ln *linuxNotifier) CheckNotificationAuthorization() (bool, error) {
func (ls *linuxNotifier) CheckNotificationAuthorization() (bool, error) {
return true, nil return true, nil
} }
// SendNotification sends a basic notification with a unique identifier, title, subtitle, and body. // SendNotification sends a basic notification with a unique identifier, title, subtitle, and body.
func (ls *linuxNotifier) SendNotification(options NotificationOptions) error { func (ln *linuxNotifier) SendNotification(options NotificationOptions) error {
if ls.internal == nil { if !ln.initialized {
return errors.New("notification service not initialized") return errors.New("notification service not initialized")
} }
if ls.internal.method == "" || (ls.internal.method == MethodDbus && ls.internal.dbusConn == nil) || if err := validateNotificationOptions(options); err != nil {
(ls.internal.method == MethodNotifySend && ls.internal.sendPath == "") { return err
return errors.New("notification system not properly initialized")
} }
ls.internal.Lock() ln.Lock()
defer ls.internal.Unlock() defer ln.Unlock()
var ( var (
systemID uint32 systemID uint32
err error err error
) )
switch ls.internal.method { switch ln.method {
case MethodDbus: case MethodDbus:
systemID, err = ls.sendViaDbus(options, nil) systemID, err = ln.sendViaDbus(options, nil)
case MethodNotifySend: case MethodNotifySend:
systemID, err = ls.sendViaNotifySend(options) systemID, err = ln.sendViaNotifySend(options)
default: default:
err = errors.New("no notification method is available") err = errors.New("no notification method is available")
} }
if err == nil && systemID > 0 { if err == nil && systemID > 0 {
// Store the system ID mapping // Store the system ID mapping
ls.internal.activeNotifs[options.ID] = systemID ln.activeNotifs[options.ID] = systemID
// Create and store the notification context // Create and store the notification context
ctx := &notificationContext{ ctx := &notificationContext{
@ -435,52 +444,51 @@ func (ls *linuxNotifier) SendNotification(options NotificationOptions) error {
SystemID: systemID, SystemID: systemID,
UserData: options.Data, UserData: options.Data,
} }
ls.internal.contexts[options.ID] = ctx ln.contexts[options.ID] = ctx
} }
return err return err
} }
// SendNotificationWithActions sends a notification with additional actions. // SendNotificationWithActions sends a notification with additional actions.
func (ls *linuxNotifier) SendNotificationWithActions(options NotificationOptions) error { func (ln *linuxNotifier) SendNotificationWithActions(options NotificationOptions) error {
if ls.internal == nil { if !ln.initialized {
return errors.New("notification service not initialized") return errors.New("notification service not initialized")
} }
if ls.internal.method == "" || (ls.internal.method == MethodDbus && ls.internal.dbusConn == nil) || if err := validateNotificationOptions(options); err != nil {
(ls.internal.method == MethodNotifySend && ls.internal.sendPath == "") { return err
return errors.New("notification system not properly initialized")
} }
ls.categoriesLock.RLock() ln.categoriesLock.RLock()
category, exists := ls.categories[options.CategoryID] category, exists := ln.categories[options.CategoryID]
ls.categoriesLock.RUnlock() ln.categoriesLock.RUnlock()
if !exists { if !exists {
return ls.SendNotification(options) return ln.SendNotification(options)
} }
ls.internal.Lock() ln.Lock()
defer ls.internal.Unlock() defer ln.Unlock()
var ( var (
systemID uint32 systemID uint32
err error err error
) )
switch ls.internal.method { switch ln.method {
case MethodDbus: case MethodDbus:
systemID, err = ls.sendViaDbus(options, &category) systemID, err = ln.sendViaDbus(options, &category)
case MethodNotifySend: case MethodNotifySend:
// notify-send doesn't support actions, fall back to basic notification // notify-send doesn't support actions, fall back to basic notification
systemID, err = ls.sendViaNotifySend(options) systemID, err = ln.sendViaNotifySend(options)
default: default:
err = errors.New("no notification method is available") err = errors.New("no notification method is available")
} }
if err == nil && systemID > 0 { if err == nil && systemID > 0 {
// Store the system ID mapping // Store the system ID mapping
ls.internal.activeNotifs[options.ID] = systemID ln.activeNotifs[options.ID] = systemID
// Create and store the notification context with actions // Create and store the notification context with actions
ctx := &notificationContext{ ctx := &notificationContext{
@ -497,14 +505,14 @@ func (ls *linuxNotifier) SendNotificationWithActions(options NotificationOptions
} }
} }
ls.internal.contexts[options.ID] = ctx ln.contexts[options.ID] = ctx
} }
return err return err
} }
// sendViaDbus sends a notification via dbus // sendViaDbus sends a notification via dbus
func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *NotificationCategory) (result uint32, err error) { func (ln *linuxNotifier) sendViaDbus(options NotificationOptions, category *NotificationCategory) (result uint32, err error) {
// Prepare actions // Prepare actions
var actions []string var actions []string
if category != nil { if category != nil {
@ -530,9 +538,9 @@ func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *Noti
} }
// Send the notification // Send the notification
obj := ls.internal.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath) obj := ln.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
dbusArgs := []interface{}{ dbusArgs := []interface{}{
ls.appName, // App name ln.appName, // App name
uint32(0), // Replaces ID (0 means new notification) uint32(0), // Replaces ID (0 means new notification)
"", // App icon (empty for now) "", // App icon (empty for now)
options.Title, // Title options.Title, // Title
@ -556,20 +564,15 @@ func (ls *linuxNotifier) sendViaDbus(options NotificationOptions, category *Noti
} }
// sendViaNotifySend sends a notification via notify-send command // sendViaNotifySend sends a notification via notify-send command
func (ls *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32, error) { func (ln *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32, error) {
args := []string{ args := []string{
options.Title, options.Title,
options.Body, options.Body,
"--urgency=normal",
} }
// Add icon if eventually supported
// if options.Icon != "" { ... }
// Add urgency (normal by default)
args = append(args, "--urgency=normal")
// Execute the command // Execute the command
cmd := exec.Command(ls.internal.sendPath, args...) cmd := exec.Command(ln.sendPath, args...)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return 0, fmt.Errorf("notify-send error: %v", err) return 0, fmt.Errorf("notify-send error: %v", err)
@ -579,78 +582,74 @@ func (ls *linuxNotifier) sendViaNotifySend(options NotificationOptions) (uint32,
return 0, nil return 0, nil
} }
// RegisterNotificationCategory registers a new NotificationCategory to be used with SendNotificationWithActions. // RegisterNotificationCategory registers a new NotificationCategory
func (ls *linuxNotifier) RegisterNotificationCategory(category NotificationCategory) error { func (ln *linuxNotifier) RegisterNotificationCategory(category NotificationCategory) error {
ls.categoriesLock.Lock() ln.categoriesLock.Lock()
ls.categories[category.ID] = category ln.categories[category.ID] = category
ls.categoriesLock.Unlock() ln.categoriesLock.Unlock()
return ls.saveCategories() return ln.saveCategories()
} }
// RemoveNotificationCategory removes a previously registered NotificationCategory. // RemoveNotificationCategory removes a previously registered NotificationCategory
func (ls *linuxNotifier) RemoveNotificationCategory(categoryId string) error { func (ln *linuxNotifier) RemoveNotificationCategory(categoryId string) error {
ls.categoriesLock.Lock() ln.categoriesLock.Lock()
delete(ls.categories, categoryId) delete(ln.categories, categoryId)
ls.categoriesLock.Unlock() ln.categoriesLock.Unlock()
return ls.saveCategories() return ln.saveCategories()
} }
// RemoveAllPendingNotifications is a Linux stub that always returns nil. // RemoveAllPendingNotifications is a Linux stub that always returns nil
// (macOS-specific) func (ln *linuxNotifier) RemoveAllPendingNotifications() error {
func (ls *linuxNotifier) RemoveAllPendingNotifications() error {
return nil return nil
} }
// RemovePendingNotification is a Linux stub that always returns nil. // RemovePendingNotification is a Linux stub that always returns nil
// (macOS-specific) func (ln *linuxNotifier) RemovePendingNotification(_ string) error {
func (ls *linuxNotifier) RemovePendingNotification(_ string) error {
return nil return nil
} }
// RemoveAllDeliveredNotifications is a Linux stub that always returns nil. // RemoveAllDeliveredNotifications is a Linux stub that always returns nil
// (macOS-specific) func (ln *linuxNotifier) RemoveAllDeliveredNotifications() error {
func (ls *linuxNotifier) RemoveAllDeliveredNotifications() error {
return nil return nil
} }
// RemoveDeliveredNotification is a Linux stub that always returns nil. // RemoveDeliveredNotification is a Linux stub that always returns nil
// (macOS-specific) func (ln *linuxNotifier) RemoveDeliveredNotification(_ string) error {
func (ls *linuxNotifier) RemoveDeliveredNotification(_ string) error {
return nil return nil
} }
// RemoveNotification removes a notification by ID (Linux-specific) // RemoveNotification removes a notification by ID (Linux-specific)
func (ls *linuxNotifier) RemoveNotification(identifier string) error { func (ln *linuxNotifier) RemoveNotification(identifier string) error {
if ls.internal == nil || ls.internal.method != MethodDbus || ls.internal.dbusConn == nil { if !ln.initialized || ln.method != MethodDbus || ln.dbusConn == nil {
return errors.New("dbus not available for closing notifications") return errors.New("dbus not available for closing notifications")
} }
// Get the system ID for this notification // Get the system ID for this notification
ls.internal.Lock() ln.Lock()
systemID, exists := ls.internal.activeNotifs[identifier] systemID, exists := ln.activeNotifs[identifier]
ls.internal.Unlock() ln.Unlock()
if !exists { if !exists {
return nil // Already closed or unknown return nil // Already closed or unknown
} }
// Call CloseNotification on dbus // Call CloseNotification on dbus
obj := ls.internal.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath) obj := ln.dbusConn.Object(dbusNotificationsInterface, dbusObjectPath)
call := obj.Call(callCloseNotification, 0, systemID) call := obj.Call(callCloseNotification, 0, systemID)
return call.Err return call.Err
} }
// getConfigFilePath returns the path to the configuration file for storing notification categories // getConfigFilePath returns the path to the configuration file
func (ls *linuxNotifier) getConfigFilePath() (string, error) { func (ln *linuxNotifier) getConfigFilePath() (string, error) {
configDir, err := os.UserConfigDir() configDir, err := os.UserConfigDir()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to get user config directory: %v", err) return "", fmt.Errorf("failed to get user config directory: %v", err)
} }
appConfigDir := filepath.Join(configDir, ls.appName) appConfigDir := filepath.Join(configDir, ln.appName)
if err := os.MkdirAll(appConfigDir, 0755); err != nil { if err := os.MkdirAll(appConfigDir, 0755); err != nil {
return "", fmt.Errorf("failed to create config directory: %v", err) return "", fmt.Errorf("failed to create config directory: %v", err)
} }
@ -658,16 +657,16 @@ func (ls *linuxNotifier) getConfigFilePath() (string, error) {
return filepath.Join(appConfigDir, "notification-categories.json"), nil return filepath.Join(appConfigDir, "notification-categories.json"), nil
} }
// saveCategories saves the notification categories to a file. // saveCategories saves the notification categories to a file
func (ls *linuxNotifier) saveCategories() error { func (ln *linuxNotifier) saveCategories() error {
filePath, err := ls.getConfigFilePath() filePath, err := ln.getConfigFilePath()
if err != nil { if err != nil {
return err return err
} }
ls.categoriesLock.RLock() ln.categoriesLock.RLock()
data, err := json.Marshal(ls.categories) data, err := json.Marshal(ln.categories)
ls.categoriesLock.RUnlock() ln.categoriesLock.RUnlock()
if err != nil { if err != nil {
return fmt.Errorf("failed to marshal notification categories: %v", err) return fmt.Errorf("failed to marshal notification categories: %v", err)
@ -680,9 +679,9 @@ func (ls *linuxNotifier) saveCategories() error {
return nil return nil
} }
// loadCategories loads notification categories from a file. // loadCategories loads notification categories from a file
func (ls *linuxNotifier) loadCategories() error { func (ln *linuxNotifier) loadCategories() error {
filePath, err := ls.getConfigFilePath() filePath, err := ln.getConfigFilePath()
if err != nil { if err != nil {
return err return err
} }
@ -705,9 +704,9 @@ func (ls *linuxNotifier) loadCategories() error {
return fmt.Errorf("failed to unmarshal notification categories: %v", err) return fmt.Errorf("failed to unmarshal notification categories: %v", err)
} }
ls.categoriesLock.Lock() ln.categoriesLock.Lock()
ls.categories = categories ln.categories = categories
ls.categoriesLock.Unlock() ln.categoriesLock.Unlock()
return nil return nil
} }