Skip to content

fix: improve tray menu state management to prevent rapid icon flickering during concurrent sync operations#346

Merged
egalvis27 merged 2 commits into
feat/go-fuse-daemonfrom
fix/tray-menu
May 21, 2026
Merged

fix: improve tray menu state management to prevent rapid icon flickering during concurrent sync operations#346
egalvis27 merged 2 commits into
feat/go-fuse-daemonfrom
fix/tray-menu

Conversation

@egalvis27
Copy link
Copy Markdown

What is Changed / Added

Added a two-layer fix in the tray icon module to prevent flickering when many files are synced concurrently:

1. Reference counter in setTrayStatus (tray-setup.ts)

  • Introduced activeSyncCount to track the number of in-flight sync operations.
  • SYNCING increments the counter and updates the tray immediately.
  • IDLE decrements the counter and only updates the tray when the counter reaches zero (all operations finished).
  • ALERT also decrements the counter, since an error terminates its associated operation.
  • Added resetTrayStatus() for session boundaries (login/logout) to force a state and reset the counter, preventing the icon from getting stuck on SYNCING after a logout mid-upload.

2. State deduplication in TrayMenu.setState (tray-menu.ts)

  • Added currentState field to TrayMenu.
  • setState now returns early when called with the state that is already displayed, skipping the native setImage and setToolTip calls entirely.

Session boundary call sites (register-session-event-handlers.ts, register-app-ready-flow.ts) updated to use resetTrayStatus instead of setTrayStatus on login/logout.


Why

On Linux, Electron's tray relies on the AppIndicator/StatusNotifierItem DBus protocol. Every call to tray.setImage() sends a DBus message that causes the system tray to fully re-render, which momentarily dismisses any open popup menu.

Previously, each individual file operation called setTrayStatus('SYNCING') on start and setTrayStatus('IDLE') on completion. When uploading a folder with many files, or pasting multiple files at once onto the virtual drive, this produced rapid SYNCING → IDLE → SYNCING → IDLE cycles — one full cycle per file. Each cycle triggered two setImage calls, causing the tray menu to flicker uncontrollably and become unclickable.

With both fixes combined, the number of real setImage calls is capped at two for any batch of concurrent operations: one when the first operation starts (IDLE → SYNCING) and one when the last one finishes (SYNCING → IDLE).

Comment on lines +105 to +106
trayInstance.setImage.mockClear();
trayInstance.setToolTip.mockClear();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why you do the mockclear after calling?
In rallity without these mocks, on the assert you would only assert that inly has been called once right? Maybe it is easier to read that way, what do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

These need to be cleared here because there are previous calls that interfere with this test's assert; without clearing them, two previous calls that are false are counted, which interferes with the purpose of the test

traySetup.setTrayStatus('SYNCING'); // file A starts
traySetup.setTrayStatus('SYNCING'); // file B starts
traySetup.setTrayStatus('IDLE'); // file A finishes
trayMenuInstance.setState.mockClear();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why is needed here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a simulation of concurrent uploads of multiple files being uploaded to the drive at the same time; each file updates the icon's status separately.

traySetup.setTrayStatus('IDLE');

// Then
call(trayMenuInstance.setState).toBe('IDLE');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shouldnt it prevail the alert icon over the idle? Since we got an operation that got an "alert"?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The problem with this icon priority system is that when there are multiple simultaneous uploads, the tray displays the most recently updated icon.

@sonarqubecloud
Copy link
Copy Markdown

@egalvis27 egalvis27 merged commit d5d439f into feat/go-fuse-daemon May 21, 2026
10 of 11 checks passed
@egalvis27 egalvis27 deleted the fix/tray-menu branch May 21, 2026 21:07
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.

2 participants