Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/assets/new-project/assets/js/code-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function _updateProBranding() {
// Get plan info from window.top.Phoenix.pro.plan
const planInfo = window.top.Phoenix && window.top.Phoenix.pro && window.top.Phoenix.pro.plan;

if (planInfo && planInfo.paidSubscriber) {
if (planInfo && planInfo.isSubscriber) {
// Hide free title, show pro title
$freeTitle.addClass('forced-hidden');
$proTitle.removeClass('forced-hidden');
Expand Down
6 changes: 4 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,10 @@
}

async function _startRequireLoop() {
// If running on localhost, fetch proxy config to get dynamic account URL
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
// If running on localhost, `npm run serve`, `npm run serveLocalAccount` targets puts in a fetch proxy.
// tTe proxy helps work around cookie domain set by phcode.dev as the dev urls are localhost. so to use
// either the actual services endpoint or localhost endpoints in dev, this is needed.
if (!Phoenix.isTestWindow && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
try {
const response = await fetch('/proxy/config');
if (response.ok) {
Expand Down
8 changes: 5 additions & 3 deletions src/services/EntitlementsManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ define(function (require, exports, module) {

/**
* Get the plan details from entitlements with fallback to free plan defaults. If the user is
* in pro trial(isInProTrial API), then paidSubscriber will always be true as we need to treat user as paid.
* you should use isInProTrial API to check if user is in pro trial if some trial-related logic needs to be done.
* in pro trial(isInProTrial API), then isSubscriber will always be true as we need to treat
* user as subcriber. you should use isInProTrial API to check if user is in pro trial if some
* trial-related logic needs to be done.
* @returns {Promise<Object>} Plan details object
*/
async function getPlanDetails() {
Expand All @@ -101,14 +102,15 @@ define(function (require, exports, module) {
// Fallback to free plan defaults
const currentDate = Date.now();
return {
isSubscriber: false,
paidSubscriber: false,
name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE,
validTill: currentDate + (FREE_PLAN_VALIDITY_DAYS * MS_IN_DAY)
};
}

/**
* Check if user is in a pro trial. IF the user is in pro trail, then `plan.paidSubscriber` will always be true.
* Check if user is in a pro trial. IF the user is in pro trail, then `plan.isSubscriber` will always be true.
* @returns {Promise<boolean>} True if user is in pro trial, false otherwise
*/
async function isInProTrial() {
Expand Down
30 changes: 20 additions & 10 deletions src/services/login-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,20 @@ define(function (require, exports, module) {
// Debounced trigger for entitlements changed
let entitlementsChangedTimer = null;

const ENTITLEMENT_CHANGED_DEBOUNCE_WINDOW = Phoenix.isTestWindow ? 100 : 1000;

function _debounceEntitlementsChanged() {
if (entitlementsChangedTimer) {
// already scheduled, skip
return;
}

// atmost 1 entitlement changed event will be triggered in this window to prevent too many entitlment changed
// events firing.
entitlementsChangedTimer = setTimeout(() => {
LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED);
entitlementsChangedTimer = null;
}, 1000); // atmost 1 entitlement changed event will be triggered in a second
}, ENTITLEMENT_CHANGED_DEBOUNCE_WINDOW);
}


Expand Down Expand Up @@ -461,6 +465,7 @@ define(function (require, exports, module) {
if(entitlements.plan && (!entitlements.plan.validTill || currentDate > entitlements.plan.validTill)) {
entitlements.plan = {
...entitlements.plan,
isSubscriber: false,
paidSubscriber: false,
name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE,
fullName: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE,
Expand Down Expand Up @@ -498,7 +503,8 @@ define(function (require, exports, module) {
* ```javascript
* {
* plan: {
* paidSubscriber: true, // Always true for trial users
* isSubscriber: true, // Always true for trial users
* paidSubscriber: false, // if the user is a paid for the plan, or is it an unpaid promo
* name: "Phoenix Pro"
* fullName: "Phoenix Pro" // this can be deceptive name like "Phoenix Pro For Education" to use in
* // profile popup, not main branding
Expand All @@ -514,11 +520,12 @@ define(function (require, exports, module) {
* ```
*
* **For logged-in trial users:**
* - If remote response has `plan.paidSubscriber: false`, injects `paidSubscriber: true`
* - If remote response has `plan.isSubscriber: false`, injects `isSubscriber: true`
* - Adds `isInProTrial: true` and `trialDaysRemaining`
* - Injects `entitlements.liveEdit.activated: true`
* - Note: Trial users may not be actual paid subscribers, but `paidSubscriber: true` is set
* so all Phoenix code treats them as paid subscribers
* - Note: Trial users may not be actual paid subscribers, but `isSubscriber: true` is set
* so all Phoenix code treats them as subscribers. to check if they actually paid or not, use
* `paidSubscriber` field.
*
* **For logged-in users (full remote response):**
* ```javascript
Expand All @@ -529,6 +536,7 @@ define(function (require, exports, module) {
* name: "Phoenix Pro",
* fullName: "Phoenix Pro" // this can be deceptive name like "Phoenix Pro For Education" to use in
* // profile popup, not main branding
* isSubscriber: boolean,
* paidSubscriber: boolean,
* validTill: number // Timestamp
* },
Expand Down Expand Up @@ -574,7 +582,7 @@ define(function (require, exports, module) {
*
* // Get current entitlements
* const entitlements = await LoginService.getEffectiveEntitlements();
* if (entitlements?.plan?.paidSubscriber) {
* if (entitlements?.plan?.isSubscriber) {
* // Enable pro features
* }
* if (entitlements?.entitlements?.liveEdit?.activated) {
Expand All @@ -597,8 +605,8 @@ define(function (require, exports, module) {
// now we need to grant trial, as user is entitled to trial if he is here.
// User has active server plan(either with login or device license)
if (serverEntitlements && serverEntitlements.plan) {
if (serverEntitlements.plan.paidSubscriber) {
// Already a paid subscriber(or has device license), return as-is
if (serverEntitlements.plan.isSubscriber) {
// Already a subscriber(or has device license), return as-is
// never inject trail data in this case.
return serverEntitlements;
}
Expand All @@ -608,7 +616,8 @@ define(function (require, exports, module) {
...serverEntitlements,
plan: {
...serverEntitlements.plan,
paidSubscriber: true,
isSubscriber: true,
paidSubscriber: serverEntitlements.plan.paidSubscriber || false,
name: brackets.config.main_pro_plan,
fullName: brackets.config.main_pro_plan,
validTill: dateNowFn() + trialDaysRemaining * MS_IN_DAY
Expand All @@ -632,7 +641,8 @@ define(function (require, exports, module) {
// Non-logged-in, non licensed user with trial - return synthetic entitlements
return {
plan: {
paidSubscriber: true,
isSubscriber: true,
paidSubscriber: false,
name: brackets.config.main_pro_plan,
fullName: brackets.config.main_pro_plan,
validTill: dateNowFn() + trialDaysRemaining * MS_IN_DAY
Expand Down
5 changes: 4 additions & 1 deletion src/services/login-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ define(function (require, exports, module) {
// Check paidSubscriber changes
const currentPaidSub = current.plan && current.plan.paidSubscriber;
const lastPaidSub = last.plan && last.plan.paidSubscriber;
if (currentPaidSub !== lastPaidSub) {
// Check isSubscriber changes
const currentIsSubscriber = current.plan && current.plan.isSubscriber;
const lastIsSubscriber = last.plan && last.plan.isSubscriber;
if (currentIsSubscriber !== lastIsSubscriber || currentPaidSub !== lastPaidSub) {
return true;
}

Expand Down
14 changes: 7 additions & 7 deletions src/services/profile-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ define(function (require, exports, module) {
<i class="fa-solid fa-feather" style="margin-left: 3px;"></i>
</span>
</div>`;
} else if (effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber) {
} else if (effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber) {
// Device-licensed user: show Phoenix Pro branding
const planName = effectiveEntitlements.plan.fullName || brackets.config.main_pro_plan;
proInfoHtml = `<div class="trial-plan-info">
Expand Down Expand Up @@ -234,21 +234,21 @@ define(function (require, exports, module) {
// Phoenix.pro is only for display purposes and should not be used to gate features.
// Use kernal mode apis for trusted check of pro features.
Phoenix.pro.plan = {
paidSubscriber: false,
isSubscriber: false,
name: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE,
fullName: Strings.USER_FREE_PLAN_NAME_DO_NOT_TRANSLATE
};
}

if (entitlements && entitlements.plan){
Phoenix.pro.plan = {
paidSubscriber: entitlements.plan.paidSubscriber,
isSubscriber: entitlements.plan.isSubscriber,
name: entitlements.plan.name,
fullName: entitlements.plan.fullName,
validTill: entitlements.plan.validTill
};
}
if (entitlements && entitlements.plan && entitlements.plan.paidSubscriber) {
if (entitlements && entitlements.plan && entitlements.plan.isSubscriber) {
// Pro user (paid subscriber or trial): show short name branding with `name feather icon`(not full name)
let displayName = entitlements.plan.name || brackets.config.main_pro_plan;
if (entitlements.isInProTrial) {
Expand Down Expand Up @@ -384,7 +384,7 @@ define(function (require, exports, module) {
// Update plan class and content based on paid subscriber status
$planName.removeClass('user-plan-free user-plan-paid');

if (entitlements.plan.paidSubscriber) {
if (entitlements.plan.isSubscriber) {
// Use pro styling with feather icon for pro users (paid or trial)
if (entitlements.isInProTrial) {
// For trial users: separate "Phoenix Pro" with icon from "(X days left)" text
Expand Down Expand Up @@ -596,7 +596,7 @@ define(function (require, exports, module) {
const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements();
return effectiveEntitlements &&
(effectiveEntitlements.isInProTrial ||
(effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber));
(effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber));
} catch (error) {
console.error('Failed to check Pro access status:', error);
return false;
Expand All @@ -611,7 +611,7 @@ define(function (require, exports, module) {
const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements();
if (effectiveEntitlements &&
(effectiveEntitlements.isInProTrial ||
(effectiveEntitlements.plan && effectiveEntitlements.plan.paidSubscriber))) {
(effectiveEntitlements.plan && effectiveEntitlements.plan.isSubscriber))) {
console.log('Profile Menu: Found Pro entitlements (trial or device license), updating branding...');
_updateBranding(effectiveEntitlements);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/services/promotions.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ define(function (require, exports, module) {

// getEntitlements() returns null if not logged in
const entitlements = await LoginService.getEntitlements();
return entitlements && entitlements.plan && entitlements.plan.paidSubscriber === true;
return entitlements && entitlements.plan && entitlements.plan.isSubscriber === true;
} catch (error) {
console.error("Error checking pro subscription:", error);
return false;
Expand Down
2 changes: 2 additions & 0 deletions test/spec/login-browser-integ-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ define(function (require, exports, module) {
Date.now() + 30 * 24 * 60 * 60 * 1000; // valid for 30 days

entitlementsResponse.plan = {
isSubscriber: true,
paidSubscriber: true,
name: "Phoenix Pro",
fullName: "Phoenix Pro",
Expand All @@ -196,6 +197,7 @@ define(function (require, exports, module) {
};
} else {
entitlementsResponse.plan = {
isSubscriber: false,
paidSubscriber: false,
name: "Free Plan",
fullName: "Free Plan"
Expand Down
4 changes: 3 additions & 1 deletion test/spec/login-desktop-integ-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ define(function (require, exports, module) {
Date.now() + 30 * 24 * 60 * 60 * 1000; // valid for 30 days

entitlementsResponse.plan = {
paidSubscriber: true,
isSubscriber: true,
paidSubscriber: !isDeviceIDRequest, // Educational device licenses are unpaid
name: "Phoenix Pro",
fullName: isDeviceIDRequest ? "Phoenix Pro Test Edu" : "Phoenix Pro",
validTill: validTill
Expand All @@ -222,6 +223,7 @@ define(function (require, exports, module) {
};
} else {
entitlementsResponse.plan = {
isSubscriber: false,
paidSubscriber: false,
name: "Free Plan",
fullName: "Free Plan"
Expand Down
Loading
Loading