Skip to content

fix: single-instance lock, tray open-on-double-click, and activity commit counts - #21

Merged
Dustella merged 4 commits into
mainfrom
fix/single-instance-and-activity-commit-count
Jul 9, 2026
Merged

Dustella merged 4 commits into
mainfrom
fix/single-instance-and-activity-commit-count

Conversation

@Dustella

@Dustella Dustella commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Three related desktop-shell fixes, all verified locally.

fix(client): single-instance lock (#18)

The app keeps running in the tray (window-all-closed is a no-op, the close button hides to tray), so relaunching the binary spawned a second process with its own window and its own tray icon. Acquire the Electron single-instance lock before whenReady: a duplicate launch quits, and the primary instance focuses its existing window via the second-instance event.

Closes #18.

fix: recover activity push commit counts via the compare API (#14)

GitHub reduced the PushEvent payload — it no longer includes size/distinct_size/commits, so the Activity feed always showed "0 commits" for every push (verified against real events, including public accounts). Carry the before/head SHAs through normalization and resolve each push group's real count from GET /repos/{owner}/{repo}/compare/{before}...{head} as concurrency-limited progressive enhancement (mirroring the existing repo-card enrichment). When the count is unknown (new branch, force-push, lost access, or still resolving) render a count-less "pushed to {repo} · {branch}" sentence instead of a fake 0.

Closes #14.

fix(client): open the app on tray icon double-click

Double-clicking the tray / status-bar icon now restores and focuses the window (creating it if the app was closed to the tray). Right-click still opens the context menu.

Test plan

  • pnpm -r typecheck ✓
  • pnpm --filter @oh-my-github/api test — incl. new reduced-payload + getPushCommitCounts cases ✓
  • pnpm --filter @oh-my-github/client test — incl. new resolved-count / unknown-sentence cases ✓
  • pnpm --filter @oh-my-github/client build ✓
  • Manually verified: no duplicate instances/tray icons on relaunch; Activity shows real commit counts; tray double-click opens the window.

🤖 Generated with Claude Code

Dustella and others added 3 commits July 8, 2026 22:52
The app keeps running in the tray (window-all-closed is a no-op and the
close button hides the window), so relaunching the binary spawned a second
process with its own window and its own tray icon. Acquire the Electron
single-instance lock before whenReady: a duplicate launch quits, and the
primary instance focuses its existing window via the second-instance event.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GitHub reduced the PushEvent payload: it no longer includes size,
distinct_size, or commits, so the activity feed always rendered "0
commits" for every push. Carry the before/head SHAs through normalization
and resolve each push group's real commit count from
GET /repos/{owner}/{repo}/compare/{before}...{head}, as concurrency-limited
progressive enhancement mirroring the existing repo-card enrichment. When
the count is unknown (new branch, force-push, lost access, or still
resolving) render a count-less "pushed to {repo} · {branch}" sentence
instead of a fake 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Double-clicking the tray / status-bar icon now restores and focuses the
window (creating it if the app was closed to the tray), matching the
standard desktop expectation. Right-click still opens the context menu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
const ref = pushCountRefForEvent(props.event)
// For a push, hand the resolved compare count down (null while pending/unavailable so
// the sentence stays count-less instead of showing a stale 0); non-push events ignore it.
const resolved = ref ? (props.pushCounts?.get(ref.key) ?? null) : undefined

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里把 Map#get() miss 也折叠成 null,会跳过 presentFeedEvent 里的 payload fallback。API normalization 仍保留了 payload.size / commits.length 的真实 count;在 compare 请求还没回来或失败时,这里会把一个原本可用的 count 展示成 pushedUnknown。建议用 props.pushCounts?.has(ref.key) ? props.pushCounts.get(ref.key)! : undefined,只有确实 resolved 为 null 且 payload 也没有 count 时再显示无计数文案。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修复 (cf0dfbd):改成 has() 判定——Map miss(pending / 非 push)保持 undefined,让 presentFeedEvent 回退到 payload.commitCount;只有缓存里确实是 null(compare 查过但拿不到)才走 pushedUnknown。

const presentation = computed(() => presentFeedGroup(props.group))
const presentation = computed(() => {
const ref = pushCountRefForGroup(props.group)
const resolved = ref ? (props.pushCounts?.get(ref.key) ?? null) : undefined

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上,group 的 aggregate count 也会被 Map miss -> null 覆盖掉。这里会让 presentFeedGroup 无法使用 payloadTotal,尤其是旧 payload 中仍有 size / commits 的事件,或者 compare 还在 pending 的阶段。建议保持 miss = undefined,让 helper 自己回退到 payloadTotal。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同样已修 (cf0dfbd):group 也用 has(),miss 时回退到 payloadTotal,只有缓存 null 才无计数。

const gotSingleInstanceLock = app.requestSingleInstanceLock()

if (!gotSingleInstanceLock) {
app.quit()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议把后续 app.whenReady().then(...) 初始化整个放进 else,或在 duplicate instance 分支直接 early-return/exit。当前调用 app.quit() 后,模块下面仍会注册 whenReady 初始化回调;大多时候进程会退出,但这个结构让副进程的 IPC/window/tray 初始化路径仍可被排队,读起来也偏离 Electron single-instance 官方示例。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已重构 (cf0dfbd):把 whenReady 及 before-quit / updater / window-all-closed 全部放进单实例锁的 else,副实例 app.quit() 后不再排队任何 window/tray/IPC 初始化,贴合 Electron 官方示例结构。

…nce init

Address review feedback on the fix branch:

- Activity rows collapsed a pushCounts Map *miss* into `null`, which forced
  the count-less sentence even when the payload still carried a real count
  (legacy size/commits) or the compare request was still pending. Distinguish
  a miss (has() === false → undefined → payload fallback) from a resolved
  `null` (looked up, unavailable → count-less), in both the event and group rows.
- Wrap the whenReady bootstrap and the before-quit / updater / window-all-closed
  handlers inside the single-instance-lock `else`, so a duplicate instance quits
  without queuing any window/tray/IPC init — matching Electron's example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Dustella
Dustella merged commit 6a390d6 into main Jul 9, 2026
4 checks passed
@Dustella
Dustella deleted the fix/single-instance-and-activity-commit-count branch July 9, 2026 05:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug反馈 [BUG]: 在 Activity 中用户 pushed 的 commits 的次数为0

2 participants