Skip to content

Commit 97aef24

Browse files
committed
Fix tray icon not appearing on first launch (macOS)
On macOS, the welcome dialogs (osascript) were running in parallel with systray.Run(), causing a race condition with Cocoa's main thread event loop initialization. This resulted in the tray icon not appearing on first launch, but working on subsequent launches. Move first-run welcome prompts to run after tray initialization completes (from within onReady callback) to avoid the race condition.
1 parent 346003b commit 97aef24

File tree

3 files changed

+47
-34
lines changed

3 files changed

+47
-34
lines changed

cmd/nfc-agent/main.go

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -169,39 +169,10 @@ func run(cfg *config.Config, headless bool) {
169169
if useTray {
170170
log.Println("Starting with system tray...")
171171

172-
// Show welcome popup and prompts on first run
173-
if welcome.IsFirstRun() {
174-
go func() {
175-
welcome.ShowWelcome()
176-
177-
// Check if auto-start is not already configured (e.g., by Homebrew)
178-
svc := service.New()
179-
if !svc.IsInstalled() {
180-
// Prompt user to enable auto-start
181-
if welcome.PromptAutostart() {
182-
if err := svc.Install(); err != nil {
183-
log.Printf("Failed to enable auto-start: %v", err)
184-
} else {
185-
log.Println("Auto-start enabled")
186-
}
187-
}
188-
}
189-
190-
// Prompt for crash reporting
191-
if welcome.PromptCrashReporting() {
192-
if err := settings.SetCrashReporting(true); err != nil {
193-
log.Printf("Failed to save crash reporting setting: %v", err)
194-
} else {
195-
log.Println("Crash reporting enabled")
196-
}
197-
}
198-
199-
_ = welcome.MarkAsShown() // Ignore error - non-critical
200-
}()
201-
}
202-
203172
// Create tray app with quit handler
204-
trayApp := tray.New(addr, func() {
173+
// Pass isFirstRun so welcome prompts can be shown after tray initialization
174+
// (avoids race condition with Cocoa event loop on macOS)
175+
trayApp := tray.New(addr, welcome.IsFirstRun(), func() {
205176
log.Println("Shutting down...")
206177
os.Exit(0)
207178
})

internal/tray/tray.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ package tray
44

55
import (
66
"fmt"
7+
"log"
78
"os/exec"
89
"runtime"
910
"sync"
1011

1112
"github.com/SimplyPrint/nfc-agent/internal/api"
1213
"github.com/SimplyPrint/nfc-agent/internal/core"
14+
"github.com/SimplyPrint/nfc-agent/internal/service"
15+
"github.com/SimplyPrint/nfc-agent/internal/settings"
1316
"github.com/SimplyPrint/nfc-agent/internal/welcome"
1417
"github.com/getlantern/systray"
1518
)
@@ -18,6 +21,7 @@ import (
1821
type TrayApp struct {
1922
serverAddr string
2023
onQuit func()
24+
isFirstRun bool
2125
readerCount int
2226
mu sync.Mutex
2327

@@ -27,9 +31,10 @@ type TrayApp struct {
2731
}
2832

2933
// New creates a new TrayApp instance
30-
func New(serverAddr string, onQuit func()) *TrayApp {
34+
func New(serverAddr string, isFirstRun bool, onQuit func()) *TrayApp {
3135
return &TrayApp{
3236
serverAddr: serverAddr,
37+
isFirstRun: isFirstRun,
3338
onQuit: onQuit,
3439
}
3540
}
@@ -104,6 +109,12 @@ func (t *TrayApp) onReady() {
104109
}
105110
}
106111
}()
112+
113+
// Show first-run prompts after tray is fully initialized
114+
// This prevents race condition with Cocoa event loop on macOS
115+
if t.isFirstRun {
116+
go t.showFirstRunPrompts()
117+
}
107118
}
108119

109120
func (t *TrayApp) onExit() {
@@ -137,6 +148,36 @@ func (t *TrayApp) updateStatus() {
137148
}
138149
}
139150

151+
// showFirstRunPrompts displays welcome dialogs and prompts on first run.
152+
// This runs after the tray is initialized to avoid race conditions with Cocoa on macOS.
153+
func (t *TrayApp) showFirstRunPrompts() {
154+
welcome.ShowWelcome()
155+
156+
// Check if auto-start is not already configured (e.g., by Homebrew)
157+
svc := service.New()
158+
if !svc.IsInstalled() {
159+
// Prompt user to enable auto-start
160+
if welcome.PromptAutostart() {
161+
if err := svc.Install(); err != nil {
162+
log.Printf("Failed to enable auto-start: %v", err)
163+
} else {
164+
log.Println("Auto-start enabled")
165+
}
166+
}
167+
}
168+
169+
// Prompt for crash reporting
170+
if welcome.PromptCrashReporting() {
171+
if err := settings.SetCrashReporting(true); err != nil {
172+
log.Printf("Failed to save crash reporting setting: %v", err)
173+
} else {
174+
log.Println("Crash reporting enabled")
175+
}
176+
}
177+
178+
_ = welcome.MarkAsShown() // Ignore error - non-critical
179+
}
180+
140181
// SetReaderCount updates the displayed reader count
141182
func (t *TrayApp) SetReaderCount(count int) {
142183
t.mu.Lock()

internal/tray/tray_linux.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ type TrayApp struct {
99
}
1010

1111
// New creates a new TrayApp instance (no-op on Linux)
12-
func New(serverAddr string, onQuit func()) *TrayApp {
12+
func New(serverAddr string, isFirstRun bool, onQuit func()) *TrayApp {
13+
// isFirstRun is ignored on Linux - no tray, no welcome dialogs
1314
return &TrayApp{
1415
serverAddr: serverAddr,
1516
onQuit: onQuit,

0 commit comments

Comments
 (0)