device-state.jsjavascript
/**
* @file Why saving is blocked, in the user's language — the message table behind
* requireCanSave(). Extracted like support-form.js so the wording (which is product, not
* style) is unit-tested in plain Node (test/device-state.test.js).
*
* Loaded two ways (UMD-lite): the Electron renderer loads it with a script tag, which
* attaches window.DeviceState; Node tests require('../src/device-state.js').
* @module device-state
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) module.exports = factory();
else root.DeviceState = factory();
})(typeof self !== 'undefined' ? self : this, function () {
/**
* The one save-blocked question, answered per state. The expired branch uses the same
* copy as the launch-gate expired notice — one sentence everywhere — and picks "free
* trial" vs "subscription" off trial_ends_at, which is only present for subs that
* started as trials.
* @param {?Object} entitlement Verified entitlement payload, or null when signed out.
* @param {?{can_save: boolean}} [entitlement.features] Feature flags; can_save true
* means saving is allowed.
* @param {?{authorized: boolean, used: number, max: number}} [entitlement.device]
* Device-cap state; authorized false means this machine is over the seat cap.
* @param {?string} [entitlement.trial_ends_at] Trial-end timestamp when the sub began
* as a trial.
* @returns {?string} Null when saving is allowed, otherwise the user-facing message.
*/
function saveBlockedMessage(entitlement) {
if (entitlement?.features?.can_save) return null;
if (!entitlement) return 'Sign in to save laybacks.';
if (entitlement.device && entitlement.device.authorized === false) {
const { used, max } = entitlement.device;
return `You’re on ${used} of ${max} machines — deactivate one on your account page to save from this one. Preview still works.`;
}
const what = entitlement.trial_ends_at ? 'free trial' : 'subscription';
return `Your ${what} has ended — saving laybacks is disabled.`;
}
return { saveBlockedMessage };
});