fix: an unreadable plan is not a free plan (PUT-1843)

The Home tab read /marketplace/subscriptions/current and folded every
failure into the free state: a non-OK response left `subscription` null,
and the catch said so explicitly. So a refusal rendered as a confident
"Free" with an Upgrade badge.

That is reachable: verification gates are default-on for authenticated
routes, and a team seat that still owes its password change is refused
there — it then reads Free while its team pays for a tier. A rate limit
or a blip does the same to anyone.

The plan card moves into its own method so a failed read can return
without touching it, leaving the card as it was rather than naming a
plan the account does not have. A 200 carrying no subscription still
reads Free, which is the one case that actually means it.
This commit is contained in:
Juan Castro committed 2026-09-17 13:52:03 -04:00
1 parent 51ce868d1e
commit e50d210423
1 file changed
+26 -21
+26 -21
View File
@@ -427,27 +427,28 @@ const TabHome = {
}
},
async _loadUsageDataUncached($el_window) {
// Load plan data — fetch live from /marketplace/subscriptions/current
// rather than reading `window.user.subscription` (which is set once
// from whoami at page-load and goes stale after subscribe / portal
// cancel until a hard refresh).
/**
* The plan card, read live rather than from the whoami copy, which goes
* stale. Not knowing the plan is not the same as being on the free one,
* so a read that fails returns without touching the card.
*/
async _renderPlanCard($el_window) {
let subscription = null;
try {
const resp = await fetch(
`${window.api_origin}/marketplace/subscriptions/current`,
{ headers: { Authorization: `Bearer ${puter.authToken}` } },
);
// A refusal is not a free plan: a seat owing a password change 403s.
if (!resp.ok) return;
const data = await resp.json();
subscription = data?.subscription ?? null;
} catch (e) {
console.error('Failed to load plan data:', e);
return;
}
try {
let subscription = null;
try {
const resp = await fetch(
`${window.api_origin}/marketplace/subscriptions/current`,
{
headers: { Authorization: `Bearer ${puter.authToken}` },
},
);
if (resp.ok) {
const data = await resp.json();
subscription = data?.subscription ?? null;
}
} catch {
// fall through to free state
}
const pastDue =
!!subscription && subscription.status === 'past_due';
@@ -535,8 +536,12 @@ const TabHome = {
}
});
} catch (e) {
console.error('Failed to load plan data:', e);
console.error('Failed to render plan card:', e);
}
},
async _loadUsageDataUncached($el_window) {
await this._renderPlanCard($el_window);
// Load storage data
try {