fix: print label uses wrong tenant on mobile#1480
Conversation
LabelMaker built the label URL from useCollections().selectedId, which is only populated when Collection/Selector.vue mounts. On mobile the sidebar (and the Selector inside it) lives in a Sheet that is conditionally rendered, so until the user opens the hamburger menu, selectedId stays null and the ?tenant= query param is omitted from the label URL. The backend then falls back to the user's default group, returning the wrong tenant's item — and because the URL is byte-identical between visits, the browser serves a cached image without firing a request. Switch to useViewPreferences().value.collectionId, matching the same pattern that 24e7995 applied to base-api.ts for attachments.
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThe ChangesLabel URL Tenant Source Refactoring
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Possibly related PRs
Suggested reviewers
Security RecommendationsWhen reviewing this change, consider the following security considerations:
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
What type of PR is this?
What this PR does / why we need it:
On mobile, opening Print Label on an item from a non-default collection rendered the label for a different item - the item that happened to share the same
assetIdin the default collection. Switching the mobile browser to "desktop mode" worked around it; on desktop it always worked.Root cause
LabelMaker.vue#getLabelUrlread the active tenant fromuseCollections().selectedIdand appended it as a?tenant=query param.selectedIdis a module-level ref that is only populated whenCollection/Selector.vuerunsuseCollections().load()in itsonMountedhook.The Selector lives inside the layout's
<Sidebar>. On desktop the sidebar is a regular always-mounted<div>. On mobile (components/ui/sidebar/Sidebar.vuelines 29–43) it's rendered inside a<Sheet>(Reka UIDialogPortal/DialogContent), which is conditionally rendered - its slot is not mounted until the user opens the hamburger menu. Until that happens,selectedId.valueisnull, so:?tenant=….mwTenantmiddleware falls back touser.DefaultGroupID.HandleGetAssetLabelcallsQueryByAssetID(auth, defaultGroupID, assetID). Because asset ids are tenant-scoped sequential numbers (e.g.000-001), the lookup happily returns a different item with the same id from the default collection.This also explains the "no network request on mobile" symptom: with the tenant param missing, the URL on mobile is byte-identical to the cached URL from a previous visit to the default collection's matching asset id, so the browser serves it from cache without firing a request. On desktop the URL includes a unique tenant uuid, so it never collides with the cache.
Fix
frontend/components/global/LabelMaker.vue- read the active tenant fromuseViewPreferences().value.collectionIdinstead ofuseCollections().selectedId. Preferences are persisted viauseLocalStorageand updated byuseCollections.set()/load()whenever the user switches collection, so they don't depend on the Selector ever being mounted.This is the same fix @tonyaellie applied to attachment URLs in
lib/api/base/base-api.tsin24e79952("fix: use prefs instead of use collection for attachments").LabelMaker.vuewas missed by that earlier pass.Which issue(s) this PR fixes:
none
Special notes:
I audited every consumer of
useCollections(). Two other files have the same root cause but a different user-visible symptom - both early-return whenselectedCollection.valueis null:frontend/pages/collection/index.vue(members page) -loadMembers()(line 90),isActionDisabled(line 87),handleLeaveCollection(line 123).frontend/pages/collection/index/settings.vue-loadSettings()(line 34),save()(line 94).If a user deep-links / bookmarks
/collection/membersor/collection/settingson mobile and lands on it without ever opening the sidebar, they'll see an empty members list or a "Select a collection" empty state. Opening and closing the sidebar mounts the Selector once, populatesselectedId, and the page works. The underlying API calls would actually succeed becausebase-api.tsalready reads from prefs.Two ways to fix these, both reasonable - happy to do either as a follow-up:
selectedCollection.valuechecks/reads in those two files to useprefs.value.collectionId(and re-derive the collection fromcollections.valuewhen the display name is needed).void useCollections().load()once inlayouts/default.vue'sonMounted.load()already self-guards against duplicate / concurrent calls (seeuse-collections.ts:22-25), so this is safe and would populateselectedIdfor every authenticated page, fixing the latent collection-page issue and any future component that readsselectedIddirectly.A couple of other call sites build
/qrcode?data=…URLs without a tenant param (components/global/PageQRCode.vue,pages/reports/label-generator.vue), but the/qrcodeendpoint just renders the supplieddatastring into a QR image, so the rendered output doesn't depend on the active tenant - no visible bug there.Testing
000-001present in two collections, open the non-default collection's000-001item on a mobile viewport (or hard-reload mobile after clearing cache so the previous bad cache entry is gone)./api/v1/labelmaker/asset/000-001?print=false&tenant=<non-default-uuid>and the rendered image shows the correct item's name and parent location.assetId(LabelMaker is invoked withtype="item"/id=item.id) and a location detail page (type="location") to confirm the other branches ofgetLabelUrl()still work.Disclaimer: I have written this MR with the help of Claude Code
Summary by CodeRabbit