fix: workspace icon flash and credits showing 0 while workspace is in…#8323
fix: workspace icon flash and credits showing 0 while workspace is in…#8323
Conversation
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 01/26/2026, 11:28:02 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Tests:
|
📝 WalkthroughWalkthroughAdd initState-driven loading behavior to topbar workspace UI: show a Skeleton while initializing, delay workspace avatar/icon and credits display until initState is "ready", and adjust popover rendering and subscription checks accordingly. Changes
Possibly related PRs
Suggested reviewers
✨ Finishing touches
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 |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 22.8 kB (baseline 22.8 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 959 kB (baseline 958 kB) • 🔴 +431 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 80.7 kB (baseline 80.7 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed Panels & Settings — 466 kB (baseline 466 kB) • 🟢 -8 BConfiguration panels, inspectors, and settings screens
Status: 12 added / 12 removed User & Accounts — 3.94 kB (baseline 3.94 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 3 added / 3 removed Editors & Dialogs — 2.83 kB (baseline 2.83 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 2 added / 2 removed UI Components — 33.7 kB (baseline 33.7 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed Data & Services — 3.19 MB (baseline 3.19 MB) • 🔴 +1 BStores, services, APIs, and repositories
Status: 8 added / 8 removed Utilities & Hooks — 25.2 kB (baseline 25.2 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 7 added / 7 removed Vendor & Third-Party — 10.7 MB (baseline 10.7 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 6.49 MB (baseline 6.49 MB) • 🟢 -58 BBundles that do not match a named category
Status: 34 added / 34 removed |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/components/topbar/CurrentUserButton.vue`:
- Line 15: The class string in CurrentUserButton.vue contains an unintended
trailing space ('size-full '); update the template expression that builds
classes (where compact is used and cn(...) is called) to use 'size-full' without
the trailing space so the class string is clean—locate the occurrence of
'size-full ' in the component (around the compact && 'size-full ' expression)
and remove the extra space.
- Around line 49-58: The popover is empty when teamWorkspacesEnabled is true but
initState !== 'ready'; update CurrentUserButton.vue to prevent opening an empty
popover by guarding the toggle/open logic: in the method that opens the popover
(e.g., openPopover or togglePopover) check if teamWorkspacesEnabled && initState
!== 'ready' and either return early (disable click) or instead set a loading
flag that renders a skeleton variant of CurrentUserPopoverWorkspace;
alternatively add a v-if/v-else to render a new LoadingPopover component (or
pass a loading prop to CurrentUserPopoverWorkspace) when teamWorkspacesEnabled
is true and initState !== 'ready' so users see a loading state instead of an
empty popover.
| cn( | ||
| 'flex items-center gap-1 rounded-full hover:bg-interface-button-hover-surface justify-center', | ||
| compact && 'size-full aspect-square' | ||
| compact && 'size-full ' |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Nit: Trailing space in class string.
'size-full ' has a trailing space. While cn() handles this gracefully, it appears unintentional.
🧹 Remove trailing space
- compact && 'size-full '
+ compact && 'size-full'📝 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.
| compact && 'size-full ' | |
| compact && 'size-full' |
🤖 Prompt for AI Agents
In `@src/components/topbar/CurrentUserButton.vue` at line 15, The class string in
CurrentUserButton.vue contains an unintended trailing space ('size-full ');
update the template expression that builds classes (where compact is used and
cn(...) is called) to use 'size-full' without the trailing space so the class
string is clean—locate the occurrence of 'size-full ' in the component (around
the compact && 'size-full ' expression) and remove the extra space.
| <!-- Workspace mode: workspace-aware popover (only when ready) --> | ||
| <CurrentUserPopoverWorkspace | ||
| v-if="teamWorkspacesEnabled" | ||
| v-if="teamWorkspacesEnabled && initState === 'ready'" | ||
| @close="closePopover" | ||
| /> | ||
| <!-- Legacy mode: original popover --> | ||
| <CurrentUserPopover v-else @close="closePopover" /> | ||
| <CurrentUserPopover | ||
| v-else-if="!teamWorkspacesEnabled" | ||
| @close="closePopover" | ||
| /> |
There was a problem hiding this comment.
Empty popover state during workspace loading.
When teamWorkspacesEnabled is true but initState !== 'ready', neither popover component renders. If a user clicks the button during the loading state, the popover opens but appears empty.
Consider either:
- Disabling the button click during loading
- Showing a skeleton/loading state inside the popover
🔧 Option 1: Disable popover toggle during loading
<Button
v-if="isLoggedIn"
class="p-1 hover:bg-transparent"
variant="muted-textonly"
:aria-label="$t('g.currentUser')"
- `@click`="popover?.toggle($event)"
+ `@click`="!showWorkspaceSkeleton && popover?.toggle($event)"
>🤖 Prompt for AI Agents
In `@src/components/topbar/CurrentUserButton.vue` around lines 49 - 58, The
popover is empty when teamWorkspacesEnabled is true but initState !== 'ready';
update CurrentUserButton.vue to prevent opening an empty popover by guarding the
toggle/open logic: in the method that opens the popover (e.g., openPopover or
togglePopover) check if teamWorkspacesEnabled && initState !== 'ready' and
either return early (disable click) or instead set a loading flag that renders a
skeleton variant of CurrentUserPopoverWorkspace; alternatively add a v-if/v-else
to render a new LoadingPopover component (or pass a loading prop to
CurrentUserPopoverWorkspace) when teamWorkspacesEnabled is true and initState
!== 'ready' so users see a loading state instead of an empty popover.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/topbar/CurrentUserButton.test.ts (2)
118-119: Pre-existing: Remove@ts-expect-errordirectives by typing the component properly.The coding guidelines state to avoid
@ts-expect-errorand fix the underlying type issue instead. Consider usingComponentPublicInstanceor defining a proper type interface for the component's exposed methods.♻️ Suggested approach
import type { ComponentPublicInstance } from 'vue' interface CurrentUserButtonExposed { popover: { toggle: () => void; hide: () => void } | null closePopover: () => void } // In test: const vm = wrapper.vm as ComponentPublicInstance & CurrentUserButtonExposed vm.popover = { toggle: popoverToggleSpy, hide: vi.fn() }Alternatively, if the component uses
defineExpose, the types should be automatically available.
102-102: Pre-existing: Avoid stubbing theButtoncomponent.Based on learnings for this repository, primitive UI components like
Buttonshould not be stubbed. Using the real component ensures tests accurately reflect production behavior and component API usage.♻️ Suggested fix
stubs: { // Use shallow mount for popover to make testing easier Popover: { template: '<div><slot></slot></div>', methods: { toggle: vi.fn(), hide: vi.fn() } - }, - Button: true + } }
🤖 Fix all issues with AI agents
In `@src/components/topbar/CurrentUserButton.test.ts`:
- Around line 28-45: The mocks for storeToRefs, useFeatureFlags, and
useTeamWorkspaceStore are static and must be converted to hoisted factories so
tests can mutate their return values per-test; replace the current vi.mock
bodies with vi.hoisted() factories that return mutable mock objects (e.g., an
exported/mock variable for flags and mockWorkspaceStore with workspaceName and
initState refs) so individual tests can set mockWorkspaceStore.initState.value
and flags.teamWorkspacesEnabled before mounting; update tests to mutate these
shared mock objects in Arrange and add assertions for loading vs ready states
(checking Skeleton/loader vs workspace avatar) using the same symbols:
storeToRefs, useFeatureFlags, and useTeamWorkspaceStore.
| vi.mock('pinia', () => ({ | ||
| storeToRefs: vi.fn((store) => store) | ||
| })) | ||
|
|
||
| // Mock the useFeatureFlags composable | ||
| vi.mock('@/composables/useFeatureFlags', () => ({ | ||
| useFeatureFlags: vi.fn(() => ({ | ||
| flags: { teamWorkspacesEnabled: false } | ||
| })) | ||
| })) | ||
|
|
||
| // Mock the useTeamWorkspaceStore | ||
| vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => ({ | ||
| useTeamWorkspaceStore: vi.fn(() => ({ | ||
| workspaceName: { value: '' }, | ||
| initState: { value: 'idle' } | ||
| })) | ||
| })) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider using vi.hoisted() to enable per-test mock state manipulation.
The current mocks are static, making it impossible to test the component's behavior with different states (e.g., initState: 'ready' vs 'idle', or teamWorkspacesEnabled: true vs false). Per the coding guidelines, use vi.hoisted() to allow per-test Arrange phase manipulation of mock state.
Additionally, the PR objectives mention fixing "flash of wrong workspace icon (replaced with loader)" and "credits showing 0" behavior tied to initState, but no tests verify this new loading/state-driven behavior.
♻️ Suggested refactor to enable per-test state control
+const mockFeatureFlags = vi.hoisted(() => ({
+ teamWorkspacesEnabled: false
+}))
+
+const mockWorkspaceStore = vi.hoisted(() => ({
+ workspaceName: { value: '' },
+ initState: { value: 'idle' }
+}))
+
// Mock pinia
vi.mock('pinia', () => ({
storeToRefs: vi.fn((store) => store)
}))
// Mock the useFeatureFlags composable
vi.mock('@/composables/useFeatureFlags', () => ({
- useFeatureFlags: vi.fn(() => ({
- flags: { teamWorkspacesEnabled: false }
- }))
+ useFeatureFlags: vi.fn(() => ({
+ flags: mockFeatureFlags
+ }))
}))
// Mock the useTeamWorkspaceStore
vi.mock('@/platform/workspace/stores/teamWorkspaceStore', () => ({
- useTeamWorkspaceStore: vi.fn(() => ({
- workspaceName: { value: '' },
- initState: { value: 'idle' }
- }))
+ useTeamWorkspaceStore: vi.fn(() => mockWorkspaceStore)
}))Then add tests for the new behavior:
it('shows loading state when initState is not ready', () => {
mockWorkspaceStore.initState.value = 'loading'
const wrapper = mountComponent()
// Assert Skeleton is rendered or workspace icon is hidden
})
it('shows workspace content when initState is ready', () => {
mockWorkspaceStore.initState.value = 'ready'
const wrapper = mountComponent()
// Assert workspace avatar/icon is rendered
})Based on learnings, vi.hoisted() should be used when necessary to allow per-test Arrange phase manipulation of deeper mock state.
🤖 Prompt for AI Agents
In `@src/components/topbar/CurrentUserButton.test.ts` around lines 28 - 45, The
mocks for storeToRefs, useFeatureFlags, and useTeamWorkspaceStore are static and
must be converted to hoisted factories so tests can mutate their return values
per-test; replace the current vi.mock bodies with vi.hoisted() factories that
return mutable mock objects (e.g., an exported/mock variable for flags and
mockWorkspaceStore with workspaceName and initState refs) so individual tests
can set mockWorkspaceStore.initState.value and flags.teamWorkspacesEnabled
before mounting; update tests to mutate these shared mock objects in Arrange and
add assertions for loading vs ready states (checking Skeleton/loader vs
workspace avatar) using the same symbols: storeToRefs, useFeatureFlags, and
useTeamWorkspaceStore.
#8323) ## Summary - Fix: flash of wrong workspace icon (replaced with loader) - Fix: personal workspace showing 0 credits ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8323-fix-workspace-icon-flash-and-credits-showing-0-while-workspace-is-in-2f46d73d36508159b52fec3fa0c17e35) by [Unito](https://www.unito.io)
… while workspace is in… (#8324) Backport of #8323 to `cloud/1.37` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8324-backport-cloud-1-37-fix-workspace-icon-flash-and-credits-showing-0-while-workspace-is--2f56d73d365081e18764dc79feadbf3a) by [Unito](https://www.unito.io) Co-authored-by: Simula_r <18093452+simula-r@users.noreply.github.com>
Summary
┆Issue is synchronized with this Notion page by Unito