[backport cloud/1.51] fix(workspace): hide subscription CTA on Local - #15717
[backport cloud/1.51] fix(workspace): hide subscription CTA on Local#15717comfy-pr-bot wants to merge 1 commit into
Conversation
## Why QA findings 3 and 6 expose the same invalid Local-only state: - **Finding 3:** a Local personal workspace without a subscription shows `Subscribe`. - **Finding 6:** switching to another Local workspace recomputes billing state and shows the same `Subscribe` action. Local workspace switching from [FE-1584](https://linear.app/comfyorg/issue/FE-1584/add-workspace-switcher-to-localdesktop-credits-only-no-settings) is limited to authentication and credits. Local does not support Cloud subscription management, so neither workspace state should offer a subscription CTA. ### Root cause `CurrentUserPopoverWorkspace` derived `showSubscribeAction` only from subscription access, delinquency, lifecycle, and permission state. It did not include the app distribution. In Local, an unsubscribed workspace can validly have no subscription access while retaining workspace-management permissions; that combination incorrectly satisfied the Cloud `Subscribe` condition. A workspace switch recalculated the same condition, which is why findings 3 and 6 had the same visible outcome. ## How - Gate `showSubscribeAction` on the existing deterministic `isCloud` distribution value. - Preserve the existing Subscribe/Resubscribe behavior for Cloud distributions. - Add unit coverage for a Local workspace changing from personal to an unsubscribed team workspace. - Extend the Local workspace-switch browser test to assert the CTA is absent both before and after the switch. ## AS IS  Local after switching to Team Workspace incorrectly shows `Subscribe`. ## TO BE  Local after switching to Team Workspace keeps subscription actions hidden. ## Testing - `pnpm test:unit src/platform/workspace/components/CurrentUserPopoverWorkspace.test.ts` - `pnpm test:browser:local browser_tests/tests/workspace/localWorkspaceSwitcher.spec.ts --project=chromium` - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm knip` - lint-staged: oxfmt, stylelint, oxlint, ESLint ## References - [QA findings](https://app.notion.com/p/3c36d73d365080b4a1b7ea008d323c69) - [FE-1584](https://linear.app/comfyorg/issue/FE-1584/add-workspace-switcher-to-localdesktop-credits-only-no-settings) --------- Co-authored-by: Amp <amp@ampcode.com>
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1827 passed, 0 failed · 1 flaky📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-23T08:23:15.203Z",
"gitSha": "262f7d11a8b33b962c61a21f49394afedd79d603",
"branch": "backport-15601-to-cloud-1.51",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2033.5599999999658,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.981999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 529.065,
"heapDeltaBytes": 4895196,
"heapUsedBytes": 70279072,
"domNodes": 16,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 9.151999999999997,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2018.5270000000628,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.446000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 505.29100000000005,
"heapDeltaBytes": 5050964,
"heapUsedBytes": 70509560,
"domNodes": 16,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 9.044,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2033.8519999999676,
"styleRecalcs": 80,
"styleRecalcDurationMs": 51.513000000000005,
"layouts": 12,
"layoutDurationMs": 3.7449999999999997,
"taskDurationMs": 1029.265,
"heapDeltaBytes": -17844348,
"heapUsedBytes": 47475616,
"domNodes": -279,
"jsHeapTotalBytes": 23433216,
"scriptDurationMs": 129.28300000000002,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1893.63400000002,
"styleRecalcs": 77,
"styleRecalcDurationMs": 43.988,
"layouts": 12,
"layoutDurationMs": 4.034000000000001,
"taskDurationMs": 885.6360000000001,
"heapDeltaBytes": 191768,
"heapUsedBytes": 65581216,
"domNodes": 60,
"jsHeapTotalBytes": 25169920,
"scriptDurationMs": 118.64,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1738.4829999999738,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.907,
"layouts": 6,
"layoutDurationMs": 0.6880000000000001,
"taskDurationMs": 413.57099999999997,
"heapDeltaBytes": 7926048,
"heapUsedBytes": 73173248,
"domNodes": 78,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 11.565000000000001,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1741.54400000009,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.318999999999996,
"layouts": 6,
"layoutDurationMs": 0.5419999999999999,
"taskDurationMs": 413.562,
"heapDeltaBytes": 8180592,
"heapUsedBytes": 73501156,
"domNodes": 79,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 11.902000000000001,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 623.0160000000069,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.344,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 410.749,
"heapDeltaBytes": -11882256,
"heapUsedBytes": 53752948,
"domNodes": 18,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 59.808,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 630.6600000000344,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.408000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 396.8740000000001,
"heapDeltaBytes": -11585040,
"heapUsedBytes": 53699440,
"domNodes": 18,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 59.351,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2016.4819999999963,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.212999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 658.059,
"heapDeltaBytes": 12795576,
"heapUsedBytes": 72976124,
"domNodes": -283,
"jsHeapTotalBytes": 2981888,
"scriptDurationMs": 15.502000000000002,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2029.7630000000026,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.642999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 677.014,
"heapDeltaBytes": 12760916,
"heapUsedBytes": 73125448,
"domNodes": -281,
"jsHeapTotalBytes": 4030464,
"scriptDurationMs": 16.496000000000002,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2233.793999999989,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.328999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1270.6029999999998,
"heapDeltaBytes": 7660776,
"heapUsedBytes": 68765648,
"domNodes": -284,
"jsHeapTotalBytes": 4485120,
"scriptDurationMs": 349.734,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2234.220999999934,
"styleRecalcs": 68,
"styleRecalcDurationMs": 14.129999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1250.252,
"heapDeltaBytes": 7774544,
"heapUsedBytes": 69040032,
"domNodes": -285,
"jsHeapTotalBytes": 3698688,
"scriptDurationMs": 344.652,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3241.6939999999954,
"styleRecalcs": 65,
"styleRecalcDurationMs": 16.17,
"layouts": 60,
"layoutDurationMs": 7.6690000000000005,
"taskDurationMs": 1416.7990000000002,
"heapDeltaBytes": 16515132,
"heapUsedBytes": 79398012,
"domNodes": 12,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 402.874,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3329.3559999999616,
"styleRecalcs": 64,
"styleRecalcDurationMs": 14.018999999999997,
"layouts": 60,
"layoutDurationMs": 7.191999999999999,
"taskDurationMs": 1428.744,
"heapDeltaBytes": 2167900,
"heapUsedBytes": 64439508,
"domNodes": -287,
"jsHeapTotalBytes": 2981888,
"scriptDurationMs": 390.597,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2017.2940000000494,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.867000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 653.017,
"heapDeltaBytes": 12753704,
"heapUsedBytes": 73673924,
"domNodes": -284,
"jsHeapTotalBytes": 3506176,
"scriptDurationMs": 15.816999999999998,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2016.4689999999155,
"styleRecalcs": 6,
"styleRecalcDurationMs": 6.252000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 640.3820000000001,
"heapDeltaBytes": 12670320,
"heapUsedBytes": 74422968,
"domNodes": -288,
"jsHeapTotalBytes": 2719744,
"scriptDurationMs": 17.846999999999994,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 609.1679999999542,
"styleRecalcs": 46,
"styleRecalcDurationMs": 11.569,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 418.669,
"heapDeltaBytes": -9980328,
"heapUsedBytes": 55563932,
"domNodes": 18,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 121.633,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 611.7130000000088,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.945999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 416.407,
"heapDeltaBytes": -10004736,
"heapUsedBytes": 55581784,
"domNodes": 20,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 121.838,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1991.8420000000197,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.431,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 504.14300000000003,
"heapDeltaBytes": 5213924,
"heapUsedBytes": 70872128,
"domNodes": 16,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 9.047,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2004.7140000000354,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.699,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 504.04400000000004,
"heapDeltaBytes": 5472140,
"heapUsedBytes": 70863604,
"domNodes": 18,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 8.732999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1717.0229999999833,
"styleRecalcs": 76,
"styleRecalcDurationMs": 42.882000000000005,
"layouts": 16,
"layoutDurationMs": 4.804,
"taskDurationMs": 816.4879999999999,
"heapDeltaBytes": -3719276,
"heapUsedBytes": 61804380,
"domNodes": 61,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 92.831,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1696.2220000000343,
"styleRecalcs": 76,
"styleRecalcDurationMs": 39.114000000000004,
"layouts": 16,
"layoutDurationMs": 4.777,
"taskDurationMs": 802.952,
"heapDeltaBytes": -3696172,
"heapUsedBytes": 61986096,
"domNodes": 61,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 93.321,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 1434.6499999999196,
"styleRecalcs": 19,
"styleRecalcDurationMs": 31.595,
"layouts": 14,
"layoutDurationMs": 12.710000000000003,
"taskDurationMs": 1019.4859999999999,
"heapDeltaBytes": -4210252,
"heapUsedBytes": 83547792,
"domNodes": 13673,
"jsHeapTotalBytes": 10485760,
"scriptDurationMs": 18.496,
"eventListeners": 2375,
"totalBlockingTimeMs": 144,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8711.680999999999,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.973,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4534.590999999999,
"heapDeltaBytes": -3538612,
"heapUsedBytes": 56874256,
"domNodes": -282,
"jsHeapTotalBytes": 4816896,
"scriptDurationMs": 1137.587,
"eventListeners": -163,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8468.422999999915,
"styleRecalcs": 249,
"styleRecalcDurationMs": 36.451,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4365.307,
"heapDeltaBytes": 13816268,
"heapUsedBytes": 73576032,
"domNodes": -282,
"jsHeapTotalBytes": 3698688,
"scriptDurationMs": 1044.503,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333338,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17865.746000000003,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16951.048,
"heapDeltaBytes": -26238600,
"heapUsedBytes": 171156756,
"domNodes": -8312,
"jsHeapTotalBytes": -6496256,
"scriptDurationMs": 132.484,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17870.62100000003,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16924.342,
"heapDeltaBytes": -44386404,
"heapUsedBytes": 152911080,
"domNodes": -8312,
"jsHeapTotalBytes": -13836288,
"scriptDurationMs": 136.405,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 21189.099,
"styleRecalcs": 176,
"styleRecalcDurationMs": 20.891999999999967,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20578.01,
"heapDeltaBytes": -21874120,
"heapUsedBytes": 176705788,
"domNodes": -8312,
"jsHeapTotalBytes": -11284480,
"scriptDurationMs": 429.624,
"eventListeners": -16385,
"totalBlockingTimeMs": 50,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 21317.318,
"styleRecalcs": 177,
"styleRecalcDurationMs": 20.726999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20764.143,
"heapDeltaBytes": -24147116,
"heapUsedBytes": 173225464,
"domNodes": -8312,
"jsHeapTotalBytes": -14692352,
"scriptDurationMs": 438.26199999999994,
"eventListeners": -16383,
"totalBlockingTimeMs": 165,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 471.0929999999962,
"styleRecalcs": 15,
"styleRecalcDurationMs": 22.766999999999996,
"layouts": 3,
"layoutDurationMs": 0.5720000000000001,
"taskDurationMs": 122.24,
"heapDeltaBytes": 5037836,
"heapUsedBytes": 69450876,
"domNodes": 130,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 10.352,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 461.2769999999955,
"styleRecalcs": 13,
"styleRecalcDurationMs": 18.175,
"layouts": 3,
"layoutDurationMs": 0.581,
"taskDurationMs": 109.299,
"heapDeltaBytes": 4928408,
"heapUsedBytes": 69679172,
"domNodes": 123,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 7.453999999999999,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## cloud/1.51 #15717 +/- ##
=============================================
Coverage ? 81.55%
=============================================
Files ? 1884
Lines ? 117703
Branches ? 37243
=============================================
Hits ? 95987
Misses ? 21244
Partials ? 472
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Backport of #15601 to
cloud/1.51Automatically created by backport workflow.