✨ Add update notifications
This commit is contained in:
@@ -6,7 +6,7 @@ package services
|
||||
#cgo CFLAGS: -I../lib
|
||||
#cgo LDFLAGS: -luser32
|
||||
#include "../lib/hotkey_windows.c"
|
||||
#include "hotkey_windows.h"
|
||||
#include "../lib/hotkey_windows.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/creativeprojects/go-selfupdate"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
@@ -26,16 +28,18 @@ type SelfUpdateResult struct {
|
||||
|
||||
// SelfUpdateService 自我更新服务
|
||||
type SelfUpdateService struct {
|
||||
logger *log.LogService
|
||||
configService *ConfigService
|
||||
config *models.AppConfig
|
||||
logger *log.LogService
|
||||
configService *ConfigService
|
||||
badgeService *badge.BadgeService // 直接使用Wails原生badge服务
|
||||
notificationService *notifications.NotificationService // 通知服务
|
||||
config *models.AppConfig
|
||||
|
||||
// 状态管理
|
||||
isUpdating bool
|
||||
}
|
||||
|
||||
// NewSelfUpdateService 创建自我更新服务实例
|
||||
func NewSelfUpdateService(configService *ConfigService, logger *log.LogService) (*SelfUpdateService, error) {
|
||||
func NewSelfUpdateService(configService *ConfigService, badgeService *badge.BadgeService, notificationService *notifications.NotificationService, logger *log.LogService) (*SelfUpdateService, error) {
|
||||
// 获取配置
|
||||
appConfig, err := configService.GetConfig()
|
||||
if err != nil {
|
||||
@@ -43,10 +47,12 @@ func NewSelfUpdateService(configService *ConfigService, logger *log.LogService)
|
||||
}
|
||||
|
||||
service := &SelfUpdateService{
|
||||
logger: logger,
|
||||
configService: configService,
|
||||
config: appConfig,
|
||||
isUpdating: false,
|
||||
logger: logger,
|
||||
configService: configService,
|
||||
badgeService: badgeService,
|
||||
notificationService: notificationService,
|
||||
config: appConfig,
|
||||
isUpdating: false,
|
||||
}
|
||||
|
||||
return service, nil
|
||||
@@ -63,6 +69,7 @@ func (s *SelfUpdateService) CheckForUpdates(ctx context.Context) (*SelfUpdateRes
|
||||
// 首先尝试主要更新源
|
||||
primaryResult, err := s.checkSourceForUpdates(ctx, s.config.Updates.PrimarySource)
|
||||
if err == nil && primaryResult != nil {
|
||||
s.handleUpdateBadge(primaryResult)
|
||||
return primaryResult, nil
|
||||
}
|
||||
|
||||
@@ -71,9 +78,12 @@ func (s *SelfUpdateService) CheckForUpdates(ctx context.Context) (*SelfUpdateRes
|
||||
if backupErr != nil {
|
||||
// 如果备用源也失败,返回主要源的错误信息
|
||||
result.Error = fmt.Sprintf("Primary source error: %v; Backup source error: %v", err, backupErr)
|
||||
// 确保在检查失败时也调用handleUpdateBadge来清除可能存在的badge
|
||||
s.handleUpdateBadge(result)
|
||||
return result, errors.New(result.Error)
|
||||
}
|
||||
|
||||
s.handleUpdateBadge(backupResult)
|
||||
return backupResult, nil
|
||||
}
|
||||
|
||||
@@ -314,6 +324,13 @@ func (s *SelfUpdateService) ApplyUpdate(ctx context.Context) (*SelfUpdateResult,
|
||||
s.logger.Error("Failed to migrate config after update", "error", err)
|
||||
}
|
||||
|
||||
// 更新成功,移除badge
|
||||
if s.badgeService != nil {
|
||||
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||
s.logger.Error("failed to remove update badge after successful update", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -395,6 +412,13 @@ func (s *SelfUpdateService) updateFromSource(ctx context.Context, sourceType mod
|
||||
s.logger.Error("Failed to update config version", "error", err)
|
||||
}
|
||||
|
||||
// 更新成功,移除badge
|
||||
if s.badgeService != nil {
|
||||
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||
s.logger.Error("failed to remove update badge after successful update", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -465,3 +489,63 @@ func (s *SelfUpdateService) cleanupBackup(backupPath string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleUpdateBadge 处理更新通知badge和通知
|
||||
func (s *SelfUpdateService) handleUpdateBadge(result *SelfUpdateResult) {
|
||||
if result != nil && result.HasUpdate {
|
||||
// 有更新时显示更新badge
|
||||
if s.badgeService != nil {
|
||||
if err := s.badgeService.SetBadge("●"); err != nil {
|
||||
s.logger.Error("failed to set update badge", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 发送简单通知
|
||||
s.sendUpdateNotification(result)
|
||||
} else {
|
||||
// 没有更新或出错时移除badge
|
||||
if s.badgeService != nil {
|
||||
if err := s.badgeService.RemoveBadge(); err != nil {
|
||||
s.logger.Error("failed to remove update badge", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sendUpdateNotification 发送更新通知
|
||||
func (s *SelfUpdateService) sendUpdateNotification(result *SelfUpdateResult) {
|
||||
if s.notificationService == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查通知授权(macOS需要)
|
||||
authorized, err := s.notificationService.CheckNotificationAuthorization()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to check notification authorization", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !authorized {
|
||||
authorized, err = s.notificationService.RequestNotificationAuthorization()
|
||||
if err != nil || !authorized {
|
||||
s.logger.Error("Failed to get notification authorization", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 构建简单通知内容
|
||||
title := "Voidraft Update Available"
|
||||
body := fmt.Sprintf("New version %s available (current: %s)", result.LatestVersion, result.CurrentVersion)
|
||||
|
||||
// 发送简单通知
|
||||
err = s.notificationService.SendNotification(notifications.NotificationOptions{
|
||||
ID: "update_available",
|
||||
Title: title,
|
||||
Subtitle: "New version available",
|
||||
Body: body,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to send notification", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,29 +4,34 @@ import (
|
||||
"voidraft/internal/models"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||
)
|
||||
|
||||
// ServiceManager 服务管理器,负责协调各个服务
|
||||
type ServiceManager struct {
|
||||
configService *ConfigService
|
||||
databaseService *DatabaseService
|
||||
documentService *DocumentService
|
||||
windowService *WindowService
|
||||
windowSnapService *WindowSnapService
|
||||
migrationService *MigrationService
|
||||
systemService *SystemService
|
||||
hotkeyService *HotkeyService
|
||||
dialogService *DialogService
|
||||
trayService *TrayService
|
||||
keyBindingService *KeyBindingService
|
||||
extensionService *ExtensionService
|
||||
startupService *StartupService
|
||||
selfUpdateService *SelfUpdateService
|
||||
translationService *TranslationService
|
||||
themeService *ThemeService
|
||||
BackupService *BackupService
|
||||
logger *log.LogService
|
||||
configService *ConfigService
|
||||
databaseService *DatabaseService
|
||||
documentService *DocumentService
|
||||
windowService *WindowService
|
||||
windowSnapService *WindowSnapService
|
||||
migrationService *MigrationService
|
||||
systemService *SystemService
|
||||
hotkeyService *HotkeyService
|
||||
dialogService *DialogService
|
||||
trayService *TrayService
|
||||
keyBindingService *KeyBindingService
|
||||
extensionService *ExtensionService
|
||||
startupService *StartupService
|
||||
selfUpdateService *SelfUpdateService
|
||||
translationService *TranslationService
|
||||
themeService *ThemeService
|
||||
badgeService *badge.BadgeService
|
||||
notificationService *notifications.NotificationService
|
||||
testService *TestService // 测试服务(仅开发环境)
|
||||
BackupService *BackupService
|
||||
logger *log.LogService
|
||||
}
|
||||
|
||||
// NewServiceManager 创建新的服务管理器实例
|
||||
@@ -34,6 +39,12 @@ func NewServiceManager() *ServiceManager {
|
||||
// 初始化日志服务
|
||||
logger := log.New()
|
||||
|
||||
// 初始化badge服务
|
||||
badgeService := badge.New()
|
||||
|
||||
// 初始化通知服务
|
||||
notificationService := notifications.New()
|
||||
|
||||
// 初始化配置服务
|
||||
configService := NewConfigService(logger)
|
||||
|
||||
@@ -77,7 +88,7 @@ func NewServiceManager() *ServiceManager {
|
||||
startupService := NewStartupService(configService, logger)
|
||||
|
||||
// 初始化自我更新服务
|
||||
selfUpdateService, err := NewSelfUpdateService(configService, logger)
|
||||
selfUpdateService, err := NewSelfUpdateService(configService, badgeService, notificationService, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -91,6 +102,9 @@ func NewServiceManager() *ServiceManager {
|
||||
// 初始化备份服务
|
||||
backupService := NewBackupService(configService, databaseService, logger)
|
||||
|
||||
// 初始化测试服务(开发环境使用)
|
||||
testService := NewTestService(badgeService, notificationService, logger)
|
||||
|
||||
// 使用新的配置通知系统设置热键配置变更监听
|
||||
err = configService.SetHotkeyChangeCallback(func(enable bool, hotkey *models.HotkeyCombo) error {
|
||||
return hotkeyService.UpdateHotkey(enable, hotkey)
|
||||
@@ -124,24 +138,27 @@ func NewServiceManager() *ServiceManager {
|
||||
}
|
||||
|
||||
return &ServiceManager{
|
||||
configService: configService,
|
||||
databaseService: databaseService,
|
||||
documentService: documentService,
|
||||
windowService: windowService,
|
||||
windowSnapService: windowSnapService,
|
||||
migrationService: migrationService,
|
||||
systemService: systemService,
|
||||
hotkeyService: hotkeyService,
|
||||
dialogService: dialogService,
|
||||
trayService: trayService,
|
||||
keyBindingService: keyBindingService,
|
||||
extensionService: extensionService,
|
||||
startupService: startupService,
|
||||
selfUpdateService: selfUpdateService,
|
||||
translationService: translationService,
|
||||
themeService: themeService,
|
||||
BackupService: backupService,
|
||||
logger: logger,
|
||||
configService: configService,
|
||||
databaseService: databaseService,
|
||||
documentService: documentService,
|
||||
windowService: windowService,
|
||||
windowSnapService: windowSnapService,
|
||||
migrationService: migrationService,
|
||||
systemService: systemService,
|
||||
hotkeyService: hotkeyService,
|
||||
dialogService: dialogService,
|
||||
trayService: trayService,
|
||||
keyBindingService: keyBindingService,
|
||||
extensionService: extensionService,
|
||||
startupService: startupService,
|
||||
selfUpdateService: selfUpdateService,
|
||||
translationService: translationService,
|
||||
themeService: themeService,
|
||||
badgeService: badgeService,
|
||||
notificationService: notificationService,
|
||||
testService: testService,
|
||||
BackupService: backupService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +181,9 @@ func (sm *ServiceManager) GetServices() []application.Service {
|
||||
application.NewService(sm.selfUpdateService),
|
||||
application.NewService(sm.translationService),
|
||||
application.NewService(sm.themeService),
|
||||
application.NewService(sm.badgeService),
|
||||
application.NewService(sm.notificationService),
|
||||
application.NewService(sm.testService), // 注册测试服务
|
||||
application.NewService(sm.BackupService),
|
||||
}
|
||||
return services
|
||||
@@ -243,3 +263,13 @@ func (sm *ServiceManager) GetThemeService() *ThemeService {
|
||||
func (sm *ServiceManager) GetWindowSnapService() *WindowSnapService {
|
||||
return sm.windowSnapService
|
||||
}
|
||||
|
||||
// GetBadgeService 获取badge服务实例
|
||||
func (sm *ServiceManager) GetBadgeService() *badge.BadgeService {
|
||||
return sm.badgeService
|
||||
}
|
||||
|
||||
// GetNotificationService 获取通知服务实例
|
||||
func (sm *ServiceManager) GetNotificationService() *notifications.NotificationService {
|
||||
return sm.notificationService
|
||||
}
|
||||
|
||||
139
internal/services/test_service.go
Normal file
139
internal/services/test_service.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/badge"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||
)
|
||||
|
||||
// TestService 测试服务 - 仅在开发环境使用
|
||||
type TestService struct {
|
||||
logger *log.LogService
|
||||
badgeService *badge.BadgeService
|
||||
notificationService *notifications.NotificationService
|
||||
}
|
||||
|
||||
// NewTestService 创建测试服务实例
|
||||
func NewTestService(badgeService *badge.BadgeService, notificationService *notifications.NotificationService, logger *log.LogService) *TestService {
|
||||
if logger == nil {
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
return &TestService{
|
||||
logger: logger,
|
||||
badgeService: badgeService,
|
||||
notificationService: notificationService,
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceStartup 服务启动时调用
|
||||
func (ts *TestService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestBadge 测试Badge功能
|
||||
func (ts *TestService) TestBadge(text string) error {
|
||||
if ts.badgeService == nil {
|
||||
return fmt.Errorf("badge service not available")
|
||||
}
|
||||
|
||||
if text == "" {
|
||||
// 如果文本为空,则移除badge
|
||||
err := ts.badgeService.RemoveBadge()
|
||||
if err != nil {
|
||||
ts.logger.Error("Failed to remove badge", "error", err)
|
||||
return err
|
||||
}
|
||||
ts.logger.Info("Badge removed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置badge
|
||||
err := ts.badgeService.SetBadge(text)
|
||||
if err != nil {
|
||||
ts.logger.Error("Failed to set badge", "text", text, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
ts.logger.Info("Badge set successfully", "text", text)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestNotification 测试通知功能
|
||||
func (ts *TestService) TestNotification(title, subtitle, body string) error {
|
||||
if ts.notificationService == nil {
|
||||
return fmt.Errorf("notification service not available")
|
||||
}
|
||||
|
||||
// 检查通知授权(macOS需要)
|
||||
authorized, err := ts.notificationService.CheckNotificationAuthorization()
|
||||
if err != nil {
|
||||
ts.logger.Error("Failed to check notification authorization", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !authorized {
|
||||
authorized, err = ts.notificationService.RequestNotificationAuthorization()
|
||||
if err != nil || !authorized {
|
||||
ts.logger.Error("Failed to get notification authorization", "error", err)
|
||||
return fmt.Errorf("notification authorization denied")
|
||||
}
|
||||
}
|
||||
|
||||
// 使用默认值如果参数为空
|
||||
if title == "" {
|
||||
title = "Test Notification"
|
||||
}
|
||||
if subtitle == "" {
|
||||
subtitle = "Testing notification system"
|
||||
}
|
||||
if body == "" {
|
||||
body = "This is a test notification to verify the system is working correctly."
|
||||
}
|
||||
|
||||
// 发送测试通知
|
||||
err = ts.notificationService.SendNotification(notifications.NotificationOptions{
|
||||
ID: "test_notification",
|
||||
Title: title,
|
||||
Subtitle: subtitle,
|
||||
Body: body,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ts.logger.Error("Failed to send test notification", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
ts.logger.Info("Test notification sent successfully", "title", title)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestUpdateNotification 测试更新通知
|
||||
func (ts *TestService) TestUpdateNotification() error {
|
||||
// 设置badge
|
||||
if err := ts.TestBadge("●"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送更新通知
|
||||
return ts.TestNotification(
|
||||
"Voidraft Update Available",
|
||||
"New version available",
|
||||
"New version 1.2.3 available (current: 1.2.0)",
|
||||
)
|
||||
}
|
||||
|
||||
// ClearAll 清除所有测试状态
|
||||
func (ts *TestService) ClearAll() error {
|
||||
// 移除badge
|
||||
if err := ts.TestBadge(""); err != nil {
|
||||
ts.logger.Error("Failed to clear badge during cleanup", "error", err)
|
||||
}
|
||||
|
||||
ts.logger.Info("Test states cleared successfully")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user