amber-backend/pb_migrations/1790500001_users_onboarding.js

56 lines
2.4 KiB
JavaScript
Raw Normal View History

Tell a new viewer what to buy before asking them to fill in a form Someone with their own accounts had to work out on their own that Amber needs a paid subscription somewhere before it can find anything, and which of three services that means. The Zdroje box already turns their usernames into addon URLs; nothing anywhere told them which usernames to go and get, or what it would cost. Six steps as a new #start tab: change the generated password, choose services, create the accounts and pay, take a free TMDB key, enter it all in Zdroje, download. Steps 1, 5 and 6 hand off to the tabs that already do that work. This screen owns no credentials and no crypto on purpose, since a second encryptor for one blob is how a vault gets corrupted. The password is step 1, not step 6. Changing it re-keys the vault with a fresh salt, so every device already signed in holds a stale key until it authenticates again. Done first, while the vault is empty, there is nothing to re-encrypt and no paired television to strand. State is three fields on the user record and nothing more: skipped, chosen and pwChangedAt. Whether the sources exist is CONFIGS.length and whether TMDB is set is a field in the decrypted config, so storing those again would let two answers disagree. pwChangedAt is the one thing that cannot be derived, because PocketBase records no password-changed timestamp. Prices and click-paths live in a providers collection, editable in the admin UI, with priceCheckedAt rendered beside the number so a stale figure looks stale rather than reading as a promise. Owner supplied the three signup guides; TorBox Free is called out as unusable because it has no API access, which is the only way Amber talks to it. byGo also stops demanding a Czech host. Nothing downstream needed one, and someone who only wants anime was being told to buy a service they had no use for. Verified against PocketBase 0.39.6 from a throwaway data dir with this repo's real migrations, hooks and page: both migrations applied clean, the flow opened itself for a user with no sources, a choice persisted as {"chosen":["prehrajto","torbox"]} and nothing else, guides rendered numbered with working links, skipping raised the banner and survived a reload, resuming came back in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 16:36:55 +00:00
/// <reference path="../pb_data/types.d.ts" />
// Where a user got to in the setup flow — **only the parts that cannot be
// derived.**
//
// Everything else about their progress is already knowable from data that
// exists: whether they have sources at all is a lookup in `addon_config`, which
// the page loads anyway. Storing "sourcesDone" beside it would create a second
// answer to a question that already has one, and the two would eventually
// disagree — at which point the flow either nags someone who is finished or
// congratulates someone who is not.
//
// So this holds three things and no more:
//
// { "skipped": true, // chose "nastavím později"
// "chosen": ["prehrajto", "torbox"], // which services they picked
// "pwChangedAt": "2026-08-12T09:00:00Z" } // they replaced the owner's password
//
// `pwChangedAt` is written by the web page when the change succeeds. It cannot be
// derived: PocketBase records no password-changed timestamp, and `updated` moves
// for any edit at all. Absent therefore means "as far as we know, still on the
// password Richard generated" — which is the honest default for a reminder, and
// the cost of being wrong is one dismissible nudge.
//
// **Per account, not localStorage.** Setup spans devices by nature — the flow is
// read on a phone or laptop while the app is installed on a television — so
// progress kept in one browser would ask someone who finished on their laptop to
// start again on their phone.
//
// Json rather than three columns: it is one opaque blob the page reads and writes
// whole, nothing filters or sorts on it, and adding a fourth thing later should
// not need a migration. `users` update rules are unchanged — a user may already
// PATCH their own record (that is how the rating default is set), and this rides
// on that.
//
// Existing accounts are untouched and read as `{}`: no `skipped`, so the family
// would be offered the flow — except they all have `addon_config` rows already,
// which is what actually suppresses it. Nobody who is set up sees anything.
migrate(
(app) => {
const users = app.findCollectionByNameOrId("users")
users.fields.add(new Field({
type: "json",
name: "onboarding",
maxSize: 4000,
}))
app.save(users)
},
(app) => {
const users = app.findCollectionByNameOrId("users")
users.fields.removeByName("onboarding")
app.save(users)
},
)