feat: add auto-start and minimize to tray feature#1301
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds autostart and minimize-to-tray behavior to the Tauri desktop app, including Tauri dependency/capability updates, tray UI and autostart commands, a Settings UI toggle for autostart, and a one-line OpenAPI operation description addition. ChangesApplication Behavior Settings
Sequence DiagramsequenceDiagram
participant Frontend as Frontend (React)
participant TauriCmds as Tauri Commands
participant Autolaunch as Autolaunch Plugin
participant Tray as System Tray
participant Window as Main Window
Frontend->>TauriCmds: is_autostart_enabled()
TauriCmds->>Autolaunch: is_enabled()
Autolaunch-->>TauriCmds: bool
TauriCmds-->>Frontend: Result<bool, String>
Frontend->>TauriCmds: enable_autostart() / disable_autostart()
TauriCmds->>Autolaunch: enable() / disable()
Autolaunch-->>TauriCmds: Ok / Err
TauriCmds-->>Frontend: Result<(), String>
Note over Window: User triggers CloseRequested
Window->>Window: hide() (prevent close)
Note over Tray,Window: User selects Show
Tray->>Window: show() + set_focus()
Note over Tray,App: User selects Quit
Tray->>App: kill_process_tree() + exit(0)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/backend/backend_python/openapi.json`:
- Line 896: The OpenAPI description uses American spelling "favorite" but the
endpoint and schema use British spelling; update the description string for the
"/toggle-favourite" operation to use "favourite" (e.g., "Toggle the favourite
status of an image.") and check the related schema name ToggleFavouriteRequest
and any other description fields for the same consistency to ensure all
user-facing docs use "favourite".
In `@frontend/src-tauri/src/main.rs`:
- Line 243: The .icon call uses app.default_window_icon().unwrap().clone() which
can panic; instead convert the Option to a Result and propagate the error from
the setup closure. Replace the unwrap usage in the .icon invocation (around
default_window_icon) with a safe conversion (e.g., call .ok_or / ok_or_else to
produce an error value and use ? to return it) so setup returns Err when no
default icon is present rather than panicking.
- Around line 242-273: The tray left-click handler (inside TrayIconBuilder
.on_tray_icon_event using TrayIconEvent::Click and MouseButton::Left) will
conflict with Tauri v2's default menu_on_left_click behavior; update the
TrayIconBuilder chain to call .menu_on_left_click(false) so left-click only
triggers your restore logic (show/set_focus in the on_tray_icon_event) and the
context menu remains on right-click.
In `@frontend/src/pages/SettingsPage/components/SystemSettingsCard.tsx`:
- Around line 17-25: The handler handleToggle can fire multiple invoke(...)
calls concurrently causing out-of-order resolution and stale UI; add a local
pending boolean state (e.g., isToggling) and short-circuit if isToggling is
true, set isToggling = true before calling invoke and set it false in finally,
disable the switch control while isToggling is true, compute the intended new
value from the current autostart at the moment of click (const next =
!autostart) and only call setAutostart(next) after a successful invoke (and
consider reading the confirmed value from the response if available) to ensure
the toggle reflects the last user action; update both occurrences of
handleToggle (lines shown) accordingly.
- Around line 6-70: Add a React Testing Library test suite for
SystemSettingsCard that mocks the invoke bridge to cover mount-time fetch,
toggle success, and error paths: mock invoke('is_autostart_enabled') to return
true/false and assert initial switch state and loading behavior (verify disabled
while loading and role="switch" aria-checked updates), mock invoke for
'enable_autostart'/'disable_autostart' to resolve and assert the UI flips state
when the button is clicked, and mock invoke to reject to assert the error path
leaves state unchanged and optionally logs; use render, waitFor (or findBy*),
and fireEvent/click to drive the component and restore/reset mocks between tests
so the mount effect and handleToggle flows are fully covered.
- Around line 33-64: The switch control in SystemSettingsCard is missing an
accessible name and description; add IDs for the visible label ("Launch at
startup") and its helper text then reference them from the button via
aria-labelledby and aria-describedby (keeping existing role="switch",
aria-checked={autostart}, disabled={loading}, and onClick={handleToggle});
ensure the IDs are unique within the component and used where the label and
helper text are rendered so assistive tech announces both the name and the
description.
- Around line 11-14: The current promise chain calling
invoke<boolean>('is_autostart_enabled') swallows errors and unconditionally
calls setAutostart(false) on failure; instead preserve an explicit unknown/error
state and keep the control disabled until a successful read. Change the state
used by SystemSettingsCard (the state set by setAutostart) to allow a tri-state
(true | false | null or an explicit error flag), update the promise catch to set
the state to null/error rather than false, ensure setLoading still flips off in
finally, and update any toggle/handler logic to no-op or stay disabled when
autostart is null so the UI does not falsely show disabled and avoid calling
enable_autostart when the real status is unknown; locate this logic around the
invoke call and the setAutostart usage (and the toggle handler) to implement the
change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7e50fbb4-03f3-49a1-8b39-a72d45b0ef81
⛔ Files ignored due to path filters (2)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
docs/backend/backend_python/openapi.jsonfrontend/src-tauri/Cargo.tomlfrontend/src-tauri/capabilities/migrated.jsonfrontend/src-tauri/src/main.rsfrontend/src/pages/SettingsPage/Settings.tsxfrontend/src/pages/SettingsPage/components/SystemSettingsCard.tsx
| "Images" | ||
| ], | ||
| "summary": "Toggle Favourite", | ||
| "description": "Toggle the favorite status of an image.", |
There was a problem hiding this comment.
Spelling inconsistency: use "favourite" to match the endpoint naming.
The description uses American spelling ("favorite") while the endpoint path and request schema use British spelling ("/toggle-favourite", "ToggleFavouriteRequest"). For consistency in user-facing API documentation, the description should match the endpoint's spelling convention.
📝 Suggested fix
- "description": "Toggle the favorite status of an image.",
+ "description": "Toggle the favourite status of an image.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "description": "Toggle the favorite status of an image.", | |
| "description": "Toggle the favourite status of an image.", |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/backend/backend_python/openapi.json` at line 896, The OpenAPI
description uses American spelling "favorite" but the endpoint and schema use
British spelling; update the description string for the "/toggle-favourite"
operation to use "favourite" (e.g., "Toggle the favourite status of an image.")
and check the related schema name ToggleFavouriteRequest and any other
description fields for the same consistency to ensure all user-facing docs use
"favourite".
| const SystemSettingsCard: React.FC = () => { | ||
| const [autostart, setAutostart] = useState(false); | ||
| const [loading, setLoading] = useState(true); | ||
|
|
||
| useEffect(() => { | ||
| invoke<boolean>('is_autostart_enabled') | ||
| .then(setAutostart) | ||
| .catch(() => setAutostart(false)) | ||
| .finally(() => setLoading(false)); | ||
| }, []); | ||
|
|
||
| const handleToggle = async () => { | ||
| const next = !autostart; | ||
| try { | ||
| await invoke(next ? 'enable_autostart' : 'disable_autostart'); | ||
| setAutostart(next); | ||
| } catch (err) { | ||
| console.error('Failed to toggle autostart:', err); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <SettingsCard | ||
| icon={Monitor} | ||
| title="System" | ||
| description="System integration and startup behavior" | ||
| > | ||
| <div className="flex items-center justify-between py-1"> | ||
| <div> | ||
| <div className="font-medium">Launch at startup</div> | ||
| <div className="text-muted-foreground text-sm"> | ||
| Automatically start PictoPy when you log in. The window starts | ||
| minimized to the system tray. | ||
| </div> | ||
| </div> | ||
|
|
||
| <button | ||
| role="switch" | ||
| aria-checked={autostart} | ||
| disabled={loading} | ||
| onClick={handleToggle} | ||
| className={[ | ||
| 'relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full', | ||
| 'transition-colors duration-200 ease-in-out', | ||
| 'focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', | ||
| 'disabled:cursor-not-allowed disabled:opacity-50', | ||
| autostart | ||
| ? 'bg-primary focus-visible:ring-primary' | ||
| : 'bg-gray-200 focus-visible:ring-gray-500 dark:bg-gray-700', | ||
| ].join(' ')} | ||
| > | ||
| <span | ||
| className={[ | ||
| 'inline-block h-4 w-4 rounded-full bg-white shadow-md', | ||
| 'transition-transform duration-200 ease-in-out', | ||
| autostart ? 'translate-x-6' : 'translate-x-1', | ||
| ].join(' ')} | ||
| /> | ||
| </button> | ||
| </div> | ||
| </SettingsCard> | ||
| ); | ||
| }; | ||
|
|
||
| export default SystemSettingsCard; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add automated coverage for the new autostart flow.
This is core PR functionality, but no test coverage is shown for the mount-time status fetch, the toggle command dispatch, or the failure paths. A small React Testing Library suite with invoke mocked would catch the regressions above before they ship. As per coding guidelines, "Ensure that test code is automated, comprehensive, and follows testing best practices" and "Verify that all critical functionality is covered by tests".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/SettingsPage/components/SystemSettingsCard.tsx` around
lines 6 - 70, Add a React Testing Library test suite for SystemSettingsCard that
mocks the invoke bridge to cover mount-time fetch, toggle success, and error
paths: mock invoke('is_autostart_enabled') to return true/false and assert
initial switch state and loading behavior (verify disabled while loading and
role="switch" aria-checked updates), mock invoke for
'enable_autostart'/'disable_autostart' to resolve and assert the UI flips state
when the button is clicked, and mock invoke to reject to assert the error path
leaves state unchanged and optionally logs; use render, waitFor (or findBy*),
and fireEvent/click to drive the component and restore/reset mocks between tests
so the mount effect and handleToggle flows are fully covered.
|
@g-k-s-03 please fix the lint errors and address the frontend failures |
Fixes #817
This PR implements two system integration features:
Auto Start — uses tauri-plugin-autostart to register the app
with the system startup manager. User can enable/disable via
a toggle in Settings → General. When launched at boot, the app
starts minimized to the system tray.
Minimize to Tray — closing the window now hides the app to the
system tray instead of exiting. A tray icon with a context menu
(Show / Quit) allows the user to restore the window or fully
exit the app.
Tested on Windows. The tauri-plugin-autostart plugin handles
macOS and Linux platform support internally.
Also fixed a duplicate core:tray:default entry in
capabilities/migrated.json.
Summary by CodeRabbit
New Features
Documentation
Chores