Skip to content

Commit d21017f

Browse files
committed
improve UX
1 parent 40e3afa commit d21017f

File tree

8 files changed

+89
-32
lines changed

8 files changed

+89
-32
lines changed

.DS_Store

0 Bytes
Binary file not shown.

loginitem_darwin.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,13 @@ func isRunningFromAppBundle() bool {
7575
if err != nil {
7676
return false
7777
}
78-
78+
7979
// Resolve any symlinks
8080
execPath, err = filepath.EvalSymlinks(execPath)
8181
if err != nil {
8282
return false
8383
}
84-
84+
8585
// Check if we're running from an app bundle
8686
// App bundles have the structure: /path/to/App.app/Contents/MacOS/executable
8787
return strings.Contains(execPath, ".app/Contents/MacOS/")
@@ -122,19 +122,19 @@ func addLoginItemUI(app *App) {
122122
log.Println("Hiding 'Start at Login' menu item - not running from app bundle")
123123
return
124124
}
125-
125+
126126
loginItem := systray.AddMenuItem("Start at Login", "Automatically start when you log in")
127127
app.menuItems = append(app.menuItems, loginItem)
128-
128+
129129
// Set initial state
130130
if isLoginItem() {
131131
loginItem.Check()
132132
}
133-
133+
134134
loginItem.Click(func() {
135135
isEnabled := isLoginItem()
136136
newState := !isEnabled
137-
137+
138138
if err := setLoginItem(newState); err != nil {
139139
log.Printf("Failed to set login item: %v", err)
140140
// Revert the UI state on error
@@ -145,12 +145,12 @@ func addLoginItemUI(app *App) {
145145
}
146146
return
147147
}
148-
148+
149149
// Update UI state
150150
if newState {
151151
loginItem.Check()
152152
} else {
153153
loginItem.Uncheck()
154154
}
155155
})
156-
}
156+
}

loginitem_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ package main
77
// addLoginItemUI is a no-op on non-macOS platforms
88
func addLoginItemUI(app *App) {
99
// Login items are only supported on macOS
10-
}
10+
}

main.go

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ package main
66

77
import (
88
"context"
9+
"crypto/sha256"
910
_ "embed"
1011
"fmt"
1112
"log"
1213
"os"
1314
"path/filepath"
15+
"strings"
1416
"sync"
1517
"time"
1618

@@ -60,8 +62,9 @@ type App struct {
6062
outgoing []PR
6163
menuItems []*systray.MenuItem
6264
cacheDir string
63-
showStaleIncoming bool
65+
hideStaleIncoming bool
6466
previousBlockedPRs map[string]bool // Track previously blocked PRs by URL
67+
lastMenuHash string // Hash of last menu state to detect changes
6568
mu sync.RWMutex // Protects concurrent access to PR data
6669
}
6770

@@ -81,7 +84,7 @@ func main() {
8184
app := &App{
8285
ctx: context.Background(),
8386
cacheDir: cacheDir,
84-
showStaleIncoming: false,
87+
hideStaleIncoming: true,
8588
previousBlockedPRs: make(map[string]bool),
8689
}
8790

@@ -212,8 +215,8 @@ func (app *App) updatePRs() {
212215

213216
app.mu.RLock()
214217
for _, pr := range app.incoming {
215-
// Skip stale PRs if not showing them
216-
if !app.showStaleIncoming && isStale(pr.UpdatedAt) {
218+
// Skip stale PRs if hiding them
219+
if app.hideStaleIncoming && isStale(pr.UpdatedAt) {
217220
continue
218221
}
219222
if pr.NeedsReview {
@@ -222,6 +225,10 @@ func (app *App) updatePRs() {
222225
}
223226

224227
for _, pr := range app.outgoing {
228+
// Skip stale PRs if hiding them
229+
if app.hideStaleIncoming && isStale(pr.UpdatedAt) {
230+
continue
231+
}
225232
if pr.IsBlocked {
226233
outgoingBlocked++
227234
}
@@ -238,10 +245,54 @@ func (app *App) updatePRs() {
238245
systray.SetTitle(fmt.Sprintf("0/%d 🚀", outgoingBlocked))
239246
}
240247

241-
app.updateMenu()
248+
app.updateMenuIfChanged()
242249
}
243250

244251
// isStale returns true if the PR hasn't been updated in over 90 days
245252
func isStale(updatedAt time.Time) bool {
246253
return time.Since(updatedAt) > stalePRThreshold
247254
}
255+
256+
// generateMenuHash creates a hash of the current menu state to detect changes
257+
func (app *App) generateMenuHash() string {
258+
var builder strings.Builder
259+
260+
app.mu.RLock()
261+
defer app.mu.RUnlock()
262+
263+
// Include hideStaleIncoming setting
264+
builder.WriteString(fmt.Sprintf("hide:%v|", app.hideStaleIncoming))
265+
266+
// Hash incoming PRs (filtered by stale setting)
267+
builder.WriteString("incoming:")
268+
for _, pr := range app.incoming {
269+
if !app.hideStaleIncoming || !isStale(pr.UpdatedAt) {
270+
builder.WriteString(fmt.Sprintf("%d:%s:%v:%s|", pr.ID, pr.Repository, pr.NeedsReview, pr.Size))
271+
}
272+
}
273+
274+
// Hash outgoing PRs (filtered by stale setting)
275+
builder.WriteString("outgoing:")
276+
for _, pr := range app.outgoing {
277+
if !app.hideStaleIncoming || !isStale(pr.UpdatedAt) {
278+
builder.WriteString(fmt.Sprintf("%d:%s:%v|", pr.ID, pr.Repository, pr.IsBlocked))
279+
}
280+
}
281+
282+
// Generate SHA256 hash
283+
hash := sha256.Sum256([]byte(builder.String()))
284+
return fmt.Sprintf("%x", hash)
285+
}
286+
287+
// updateMenuIfChanged only rebuilds the menu if the PR data has actually changed
288+
func (app *App) updateMenuIfChanged() {
289+
currentHash := app.generateMenuHash()
290+
if currentHash == app.lastMenuHash {
291+
log.Println("Menu data unchanged, skipping menu rebuild")
292+
return
293+
}
294+
295+
log.Println("Menu data changed, rebuilding menu")
296+
app.lastMenuHash = currentHash
297+
app.updateMenu()
298+
}

media/logo.png

-1.14 MB
Loading

media/logo.xcf

3.45 MB
Binary file not shown.

menubar-icon.png

758 KB
Loading

ui.go

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (app *App) updateMenu() {
121121

122122
app.mu.RLock()
123123
for _, pr := range app.incoming {
124-
if app.showStaleIncoming || !isStale(pr.UpdatedAt) {
124+
if !app.hideStaleIncoming || !isStale(pr.UpdatedAt) {
125125
incomingCount++
126126
if pr.NeedsReview {
127127
incomingBlocked++
@@ -132,11 +132,12 @@ func (app *App) updateMenu() {
132132
outgoingBlocked := 0
133133
outgoingCount := 0
134134
for _, pr := range app.outgoing {
135-
if pr.IsBlocked {
136-
outgoingBlocked++
135+
if !app.hideStaleIncoming || !isStale(pr.UpdatedAt) {
136+
outgoingCount++
137+
if pr.IsBlocked {
138+
outgoingBlocked++
139+
}
137140
}
138-
// Count all outgoing PRs
139-
outgoingCount++
140141
}
141142
app.mu.RUnlock()
142143

@@ -163,7 +164,7 @@ func (app *App) updateMenu() {
163164

164165
for _, pr := range prs {
165166
// Apply filters
166-
if !app.showStaleIncoming && isStale(pr.UpdatedAt) {
167+
if app.hideStaleIncoming && isStale(pr.UpdatedAt) {
167168
continue
168169
}
169170
title := fmt.Sprintf("%s #%d", pr.Repository, pr.Number)
@@ -204,7 +205,10 @@ func (app *App) updateMenu() {
204205
app.mu.RUnlock()
205206

206207
for _, pr := range prs {
207-
// No filters for outgoing PRs
208+
// Apply stale filter to outgoing PRs
209+
if app.hideStaleIncoming && isStale(pr.UpdatedAt) {
210+
continue
211+
}
208212
title := fmt.Sprintf("%s #%d", pr.Repository, pr.Number)
209213
if pr.IsBlocked {
210214
title = fmt.Sprintf("%s 🚀", title)
@@ -226,20 +230,22 @@ func (app *App) updateMenu() {
226230

227231
systray.AddSeparator()
228232

229-
// Show stale incoming
230-
showStaleIncomingItem := systray.AddMenuItem("Show stale PRs (>90 days)", "")
231-
app.menuItems = append(app.menuItems, showStaleIncomingItem)
232-
if !app.showStaleIncoming {
233-
showStaleIncomingItem.Check()
233+
// Hide stale PRs
234+
hideStaleItem := systray.AddMenuItem("Hide stale PRs (>90 days)", "")
235+
app.menuItems = append(app.menuItems, hideStaleItem)
236+
if app.hideStaleIncoming {
237+
hideStaleItem.Check()
234238
}
235-
showStaleIncomingItem.Click(func() {
236-
app.showStaleIncoming = !app.showStaleIncoming
237-
if app.showStaleIncoming {
238-
showStaleIncomingItem.Check()
239+
hideStaleItem.Click(func() {
240+
app.hideStaleIncoming = !app.hideStaleIncoming
241+
if app.hideStaleIncoming {
242+
hideStaleItem.Check()
239243
} else {
240-
showStaleIncomingItem.Uncheck()
244+
hideStaleItem.Uncheck()
241245
}
242-
log.Printf("Show stale incoming: %v", app.showStaleIncoming)
246+
log.Printf("Hide stale PRs: %v", app.hideStaleIncoming)
247+
// Force menu rebuild since hideStaleIncoming changed
248+
app.lastMenuHash = ""
243249
app.updateMenu()
244250
})
245251

0 commit comments

Comments
 (0)