Skip to content

feat(cloud): move account control to objects toolbar#700

Merged
fernandotonon merged 7 commits into
masterfrom
codex/cloud-account-toolbar
Jun 2, 2026
Merged

feat(cloud): move account control to objects toolbar#700
fernandotonon merged 7 commits into
masterfrom
codex/cloud-account-toolbar

Conversation

@fernandotonon
Copy link
Copy Markdown
Owner

@fernandotonon fernandotonon commented Jun 1, 2026

Summary

  • Removes the top-level Cloud menu bar entry and adds a VS Code-style QtMesh Cloud account control pinned to the bottom of the left objects toolbar.
  • Introduces reusable CloudAccountMenuButton: outline user/cloud icon when signed out, initials avatar + connected badge when signed in.
  • Popup menu: non-clickable header (display name + “QtMesh Cloud account”), Open My Projects / Upload Files…, then Sign in or Sign out depending on auth state.
  • Fixes signed-out segfault (nested QPainter in custom button paint) and ghost header text after sign-out (remove header actions from menu instead of only hiding).

Test plan

  • ./build_local/bin/UnitTests --gtest_filter="MainWindowTest.CloudAccount*"
  • ./build_local/bin/UnitTests --gtest_filter="MainWindowTest.CloudMenuIsNotOnMenuBar"
  • ./build_local/bin/UnitTests --gtest_filter="CloudAccountMenuButtonTest.*"
  • Manual: sign in → open menu → sign out → open menu (no name ghosting, no crash)
  • CI green on Linux/macOS/Windows builds + unit-tests-linux

Summary by CodeRabbit

  • New Features

    • Cloud account moved to a toolbar account button with avatar and status badge.
  • UI Changes

    • Button shows sign-in state, display name/initials, tooltip and a styled popup with actions (Sign in/out, Upload Files, Open My Projects). Cloud entry removed from the main menu.
    • Added cloud user icon asset.
  • Bug Fixes

    • Showing a warning if opening the cloud web URL fails.
  • Tests

    • New unit and UI tests for the account button, menu behavior, initials logic, and session teardown.

Replace the Cloud menu bar entry with a VS Code-style account button
pinned to the bottom of the left objects toolbar, including a disabled
signed-in user row in the popup menu and MainWindow unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Warning

Review limit reached

@fernandotonon, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 59 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ea973580-9cd4-4dba-a51d-1277a93cea80

📥 Commits

Reviewing files that changed from the base of the PR and between 19ae608 and 2bd7a0b.

📒 Files selected for processing (3)
  • src/CloudAccountMenuButton_test.cpp
  • src/CloudCredentialStore_test.cpp
  • src/mainwindow.cpp
📝 Walkthrough

Walkthrough

Replaces the top-level "Cloud" menu with a CloudAccountMenuButton avatar in the objects toolbar, adds the CloudAccountMenuButton widget and tests, integrates it via MainWindow::setupCloudAccountStatusControl(), refactors updateCloudAuthActions() to refresh the new control, and isolates credential persistence for tests.

Changes

Cloud Account Toolbar UI Migration

Layer / File(s) Summary
CloudAccountMenuButton API & build/resource entries
src/CloudAccountMenuButton.h, src/CMakeLists.txt, resources/resource.qrc, tests/CMakeLists.txt
Add CloudAccountMenuButton public API, register CloudAccountMenuButton.cpp in src/tests CMakeLists, and add cloud_account_user.svg resource.
CloudAccountMenuButton implementation
src/CloudAccountMenuButton.cpp
Implement display-name loader, AvatarButton painting, initialsFromDisplayName, constructor (layout/menu/connections), applyMenuStyle(), buildMenu(), updateHeader(), and refresh() that reads CloudSession/QSettings and toggles UI/menu state.
MainWindow integration & refactor
src/mainwindow.h, src/mainwindow.cpp
Add forward declarations and CloudAccountMenuButton* m_cloudAccountControl, add setupCloudAccountStatusControl(); include CloudAccountMenuButton/QStyle, call setup from constructor, remove legacy Cloud menu construction, connect avatar menu signals to existing handlers (with breadcrumbs), and make updateCloudAuthActions() delegate to m_cloudAccountControl->refresh().
Tests and test build
src/CloudAccountMenuButton_test.cpp, src/mainwindow_test.cpp, tests/CMakeLists.txt
Add unit/UI tests for initials parsing and repaint/refresh safety, extend mainwindow tests for toolbar placement and signed-in/out menu header/actions, update teardown to clear cloud-session settings, and include widget source in test build CMakeLists.
Credential store test isolation & tests
src/CloudCredentialStore.cpp, src/CloudCredentialStore_test.cpp
Add a test-organization check to force fallback-file storage during isolated tests, tighten fallback file writes, and convert CloudCredentialStore tests to a fixture that sets/restores QCoreApplication names and clears session/settings in setup/teardown.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CloudButton as CloudAccountMenuButton
  participant MainWindow
  participant CredentialStore as CloudCredentialStore

  User->>CloudButton: click opens dropdown
  CloudButton->>MainWindow: QMenu aboutToShow -> request refresh
  MainWindow->>CredentialStore: query CloudSession and QSettings
  CredentialStore-->>MainWindow: CloudSession and display-name
  MainWindow-->>CloudButton: update tooltip, avatar, and menu items
  User->>CloudButton: select Upload / Sign in/out / Open Projects
  CloudButton-->>MainWindow: emit uploadFilesRequested / signInRequested / signOutRequested / openProjectsRequested
  MainWindow->>MainWindow: handle requests and add breadcrumbs
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • fernandotonon/QtMeshEditor#696: Shares the QtMesh Cloud device-code/session infrastructure and CloudCredentialStore usage that CloudAccountMenuButton reads for session/display-name.
  • fernandotonon/QtMeshEditor#698: Overlaps with legacy cloud upload/Open Projects wiring that this PR migrates into the new toolbar control.

Poem

🐇 I painted a circle, initials bright,
A toolbar bobber in the app's daylight,
Menubar retired, dropdowns now sing,
I hop, I test, I wire each tiny thing,
☁️✨ a rabbit's small UI delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(cloud): move account control to objects toolbar' is clear, specific, and accurately summarizes the main change.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the summary, technical details, test plan, and known issues.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/cloud-account-toolbar

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@src/mainwindow_test.cpp`:
- Around line 378-408: The test currently calls
CloudCredentialStore::clearSession() only inside
CloudAccountMenuShowsConnectedUserAsDisabledRow, which can leave persisted
credentials if an assertion fails; move the cleanup to
MainWindowTest::TearDown() so every test always clears stored credentials after
running. Modify MainWindowTest::TearDown() to call
CloudCredentialStore::clearSession() (and any related QSettings cleanup for
AppSettingsKeys::cloudUserName() if needed) and remove or keep redundant clears
inside individual tests like CloudAccountMenuShowsConnectedUserAsDisabledRow to
ensure deterministic teardown.

In `@src/mainwindow.cpp`:
- Around line 2305-2325: The new cloud menu actions are missing Sentry
breadcrumbs and the "Open My Projects" handler ignores QDesktopServices::openUrl
failure; add SentryReporter::addBreadcrumb("ui.action", "<action name>") at the
start of each action handler (signInToQtMeshCloud, signOutOfQtMeshCloud,
uploadFilesToQtMeshCloud and the lambda used for m_cloudOpenDashboardAction) so
clicks are tracked, and change the m_cloudOpenDashboardAction lambda to capture
the QDesktopServices::openUrl(...) return value and handle false by
logging/reporting the failure (e.g. SentryReporter::addBreadcrumb or error
report and user-visible fallback) instead of failing silently.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2cdf80e-7e5c-4ac5-9a22-712e92b8c877

📥 Commits

Reviewing files that changed from the base of the PR and between 4ef7760 and 12784e2.

📒 Files selected for processing (3)
  • src/mainwindow.cpp
  • src/mainwindow.h
  • src/mainwindow_test.cpp

Comment thread src/mainwindow_test.cpp Outdated
Comment thread src/mainwindow.cpp Outdated
fernandotonon and others added 3 commits June 1, 2026 19:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Clear CloudCredentialStore in MainWindowTest teardown, add ui.action
Sentry breadcrumbs for cloud toolbar menu actions, and warn when the
browser fails to open QtMesh Cloud.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the generic network icon with a dedicated CloudAccountMenuButton
component: outline user icon when signed out, initials avatar when signed in,
and a status badge for connected/offline. Restructure the popup with a
non-clickable name header, main cloud actions, and account sign-in/out.

Fix signed-out paint crash (nested QPainter) and remove the header from the
menu when logged out so stale name text no longer bleeds through.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@src/CloudAccountMenuButton_test.cpp`:
- Around line 19-40: The test mutates the real CloudCredentialStore and any
QSettings-backed state; instead ensure the test uses a test-only credential
backend or a fake store and isolated QSettings namespace. Modify the test (and
code if needed) so CloudAccountMenuButton uses an injectable store: add or use
an API such as passing a FakeCloudCredentialStore into CloudAccountMenuButton
(or a CloudCredentialStore::setTestBackend / useTestNamespace) and configure
QSettings to a test-only organization/application name or prefix before calling
CloudCredentialStore::clearSession/saveSession; then call the same sequence
(show/refresh/repaint/processEvents) against the fake store so the production
credential store and real QSettings are not touched.

In `@src/CloudAccountMenuButton.cpp`:
- Around line 224-227: The subtitle text is hard-coded so the "Signed in as …"
row never appears; change initialization and updates to derive the subtitle
(m_headerSubtitleLabel) or the name label from the signed-in state and
displayName: set m_headerSubtitleLabel to either "Signed in as" (when signedIn
is true) or "QtMesh Cloud account" (when not signed in) in the constructor and
ensure updateHeader() also updates m_headerSubtitleLabel based on the
signedIn/displayName values; also apply the same change to the second header
block referenced around the other label pair (lines ~257-263) so both header
subtitle/name labels reflect the connected state.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 23ec3e04-38e9-48be-8df7-957e2046d6ee

📥 Commits

Reviewing files that changed from the base of the PR and between 34adcac and 1204dcb.

⛔ Files ignored due to path filters (1)
  • resources/cloud_account_user.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • resources/resource.qrc
  • src/CMakeLists.txt
  • src/CloudAccountMenuButton.cpp
  • src/CloudAccountMenuButton.h
  • src/CloudAccountMenuButton_test.cpp
  • src/mainwindow.cpp
  • src/mainwindow.h
  • src/mainwindow_test.cpp
  • tests/CMakeLists.txt
✅ Files skipped from review due to trivial changes (1)
  • resources/resource.qrc
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mainwindow_test.cpp

Comment thread src/CloudAccountMenuButton_test.cpp Outdated
Comment on lines +224 to +227
m_headerSubtitleLabel = new QLabel(tr("QtMesh Cloud account"), m_headerWidget);
m_headerSubtitleLabel->setObjectName(QStringLiteral("cloudAccountMenuHeaderSubtitle"));
m_headerSubtitleLabel->setStyleSheet(QStringLiteral(
"color: #9a9a9a; font-size: 11px; background: transparent;"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use signed-in header copy here.

The menu never renders the promised disabled Signed in as … row because the subtitle stays hard-coded to QtMesh Cloud account and updateHeader() only swaps the name label. Please derive one of these labels from signedIn/displayName so the connected state is explicit.

Also applies to: 257-263

🤖 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 `@src/CloudAccountMenuButton.cpp` around lines 224 - 227, The subtitle text is
hard-coded so the "Signed in as …" row never appears; change initialization and
updates to derive the subtitle (m_headerSubtitleLabel) or the name label from
the signed-in state and displayName: set m_headerSubtitleLabel to either "Signed
in as" (when signedIn is true) or "QtMesh Cloud account" (when not signed in) in
the constructor and ensure updateHeader() also updates m_headerSubtitleLabel
based on the signedIn/displayName values; also apply the same change to the
second header block referenced around the other label pair (lines ~257-263) so
both header subtitle/name labels reflect the connected state.

fernandotonon and others added 2 commits June 2, 2026 08:36
Route credential storage to an isolated fallback file when tests set
organization to QtMeshEditorTests so unit tests do not touch the OS
keychain. Use gtest fixtures with isolated QSettings in cloud tests,
show "Signed in to QtMesh Cloud" in the menu header subtitle, and
centralize MainWindow cloud-session cleanup in TearDown.

Co-authored-by: Cursor <cursoragent@cursor.com>
Consolidate duplicate fallback branches in CloudCredentialStore, apply
C++17 init-statements, const-correctness, and auto per SonarCloud rules
for the account menu changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/CloudCredentialStore_test.cpp (1)

51-67: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert that cloudTokenExpiresAt() is removed too.

CloudCredentialStore::migrateLegacySettingsIfNeeded() clears the expiry key along with token and email, but this test only checks two of the three legacy fields. A partial migration would still pass here.

🤖 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 `@src/CloudCredentialStore_test.cpp` around lines 51 - 67, Update the test
MigratesLegacyPlaintextSettings to also verify the expiry key is removed: after
calling CloudCredentialStore::migrateLegacySettingsIfNeeded() add an assertion
that the legacy expiry entry (AppSettingsKeys::cloudTokenExpiresAt()) is gone
(e.g. settings.value(AppSettingsKeys::cloudTokenExpiresAt()).isNull() or
equivalent) so the test fails for partial migrations; reference the test name
and CloudCredentialStore::migrateLegacySettingsIfNeeded() to locate where to add
the check.
🧹 Nitpick comments (1)
src/CloudAccountMenuButton_test.cpp (1)

47-72: ⚡ Quick win

Assert the signed-out menu cleanup too.

After Line 68 this only exercises refresh()/repaint(). It never verifies that the account header is actually gone once the session is cleared, so the "ghost header text after sign-out" regression could come back without failing this test.

🤖 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 `@src/CloudAccountMenuButton_test.cpp` around lines 47 - 72, The test currently
clears the session with CloudCredentialStore::clearSession() and calls
button.refresh()/button.repaint() but does not verify the UI cleaned up; after
calling clearSession() and refreshing/repainting the CloudAccountMenuButton,
assert that the header QLabel with objectName "cloudAccountMenuHeaderSubtitle"
no longer exists or is not visible—e.g. call
button.findChild<QLabel*>("cloudAccountMenuHeaderSubtitle") and EXPECT_EQ(it,
nullptr) or EXPECT_FALSE(subtitle->isVisible())—so the
CloudAccountMenuButtonTest ensures the signed-out header is removed after
CloudCredentialStore::clearSession().
🤖 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.

Outside diff comments:
In `@src/CloudCredentialStore_test.cpp`:
- Around line 51-67: Update the test MigratesLegacyPlaintextSettings to also
verify the expiry key is removed: after calling
CloudCredentialStore::migrateLegacySettingsIfNeeded() add an assertion that the
legacy expiry entry (AppSettingsKeys::cloudTokenExpiresAt()) is gone (e.g.
settings.value(AppSettingsKeys::cloudTokenExpiresAt()).isNull() or equivalent)
so the test fails for partial migrations; reference the test name and
CloudCredentialStore::migrateLegacySettingsIfNeeded() to locate where to add the
check.

---

Nitpick comments:
In `@src/CloudAccountMenuButton_test.cpp`:
- Around line 47-72: The test currently clears the session with
CloudCredentialStore::clearSession() and calls button.refresh()/button.repaint()
but does not verify the UI cleaned up; after calling clearSession() and
refreshing/repainting the CloudAccountMenuButton, assert that the header QLabel
with objectName "cloudAccountMenuHeaderSubtitle" no longer exists or is not
visible—e.g. call button.findChild<QLabel*>("cloudAccountMenuHeaderSubtitle")
and EXPECT_EQ(it, nullptr) or EXPECT_FALSE(subtitle->isVisible())—so the
CloudAccountMenuButtonTest ensures the signed-out header is removed after
CloudCredentialStore::clearSession().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3f67ded-8fef-48b2-9eea-cb01231647fd

📥 Commits

Reviewing files that changed from the base of the PR and between 1204dcb and 19ae608.

📒 Files selected for processing (6)
  • src/CloudAccountMenuButton.cpp
  • src/CloudAccountMenuButton_test.cpp
  • src/CloudCredentialStore.cpp
  • src/CloudCredentialStore_test.cpp
  • src/mainwindow.cpp
  • src/mainwindow_test.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/mainwindow_test.cpp
  • src/CloudAccountMenuButton.cpp
  • src/mainwindow.cpp

Poll for browser approval while the sign-in dialog stays open so copy/paste
approval works without clicking Open Browser. Show status text without
repeating the user code.

Tests: assert legacy cloudTokenExpiresAt is cleared on migration, and that
the signed-out menu no longer lists the account header widget action.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 2, 2026

@fernandotonon fernandotonon merged commit 0e73b5d into master Jun 2, 2026
20 checks passed
@fernandotonon fernandotonon deleted the codex/cloud-account-toolbar branch June 2, 2026 16:29
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.

1 participant