Skip to content

Commit 030b638

Browse files
committed
chore: pro trial ui branding
1 parent eaeaf00 commit 030b638

File tree

8 files changed

+150
-29
lines changed

8 files changed

+150
-29
lines changed

gulpfile.js/translateStrings.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,16 @@ function aggregateUtilizationMetrics(obj) {
8080
return globalUtilizationMetrics;
8181
}
8282

83+
const translationContext =
84+
`This is a bunch of strings extracted from a JavaScript file used to develop our product with is a text editor.
85+
Some strings may have HTML or templates(mustache library used).
86+
The brand name “Phoenix Pro” must remain in English and should never be translated.
87+
Please translate these strings accurately.
88+
`;
89+
8390
function getTranslationrequest(stringsToTranslate, lang) {
8491
return {
85-
translationContext: "This is a bunch of strings extracted from a JavaScript file used to develop our product with is a text editor. Some strings may have HTML or templates(mustache library used). Please translate these strings accurately.",
92+
translationContext: translationContext,
8693
"source": stringsToTranslate,
8794
"provider": "vertex",
8895
"sourceContext": {

src/nls/root/strings.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,5 +1678,6 @@ define({
16781678
"PROMO_CARD_4_MESSAGE": "Edit headings, buttons, and copy directly in the preview.",
16791679
"PROMO_LEARN_MORE": "Learn More\u2026",
16801680
"PROMO_GET_APP_UPSELL_BUTTON": "Get {0}",
1681-
"PROMO_PRO_ENDED_TITLE": "Your {0} upgrade has ended"
1681+
"PROMO_PRO_ENDED_TITLE": "Your {0} upgrade has ended",
1682+
"PROMO_PRO_TRIAL_DAYS_LEFT": "Phoenix Pro Trial ({0} days left)"
16821683
});

src/services/html/login-popup.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
<div class="profile-popup">
22
<div class="popup-header">
33
<h1 class="popup-title">{{Strings.PROFILE_POP_TITLE}}</h1>
4+
{{#trialInfo}}
5+
<div class="trial-plan-info">
6+
<span class="phoenix-pro-title-plain">
7+
<span class="pro-plan-name">{{planName}}</span>
8+
<i class="fa-solid fa-feather" style="margin-left: 3px;"></i>
9+
</span>
10+
</div>
11+
{{/trialInfo}}
412
</div>
513
<div class="popup-body">
614
<button id="phoenix-signin-btn" class="btn dialog-button primary">

src/services/login-service.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,57 @@ define(function (require, exports, module) {
122122
}
123123
}
124124

125+
/**
126+
* Get effective entitlements including trial enhancement for UI display
127+
* Returns enhanced entitlements that treat trial users as pro users
128+
*/
129+
async function getEffectiveEntitlements(forceRefresh = false) {
130+
// Get raw server entitlements
131+
const serverEntitlements = await getEntitlements(forceRefresh);
132+
133+
// Get trial days remaining
134+
const trialDaysRemaining = await LoginService.getProTrialDaysRemaining();
135+
136+
// If no trial is active, return server entitlements as-is
137+
if (trialDaysRemaining <= 0) {
138+
return serverEntitlements;
139+
}
140+
141+
// User has active trial
142+
if (serverEntitlements && serverEntitlements.plan) {
143+
// Logged-in user with trial
144+
if (serverEntitlements.plan.paidSubscriber) {
145+
// Already a paid subscriber, return as-is
146+
return serverEntitlements;
147+
} else {
148+
// Enhance entitlements for trial user
149+
return {
150+
...serverEntitlements,
151+
plan: {
152+
...serverEntitlements.plan,
153+
paidSubscriber: true,
154+
name: "Phoenix Pro"
155+
},
156+
isInProTrial: true,
157+
trialDaysRemaining: trialDaysRemaining
158+
};
159+
}
160+
} else {
161+
// Non-logged-in user with trial - return synthetic entitlements
162+
return {
163+
plan: {
164+
paidSubscriber: true,
165+
name: "Phoenix Pro"
166+
},
167+
isInProTrial: true,
168+
trialDaysRemaining: trialDaysRemaining
169+
};
170+
}
171+
}
172+
125173
// Add functions to secure exports
126174
LoginService.getEntitlements = getEntitlements;
175+
LoginService.getEffectiveEntitlements = getEffectiveEntitlements;
127176
LoginService.clearEntitlements = clearEntitlements;
128177
LoginService.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
129178
});

src/services/profile-menu.js

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ define(function (require, exports, module) {
33
PopUpManager = require("widgets/PopUpManager"),
44
ThemeManager = require("view/ThemeManager"),
55
Strings = require("strings"),
6+
StringUtils = require("utils/StringUtils"),
67
LoginService = require("./login-service");
78

89
const KernalModeTrust = window.KernalModeTrust;
@@ -139,7 +140,7 @@ define(function (require, exports, module) {
139140
/**
140141
* Shows the sign-in popup when the user is not logged in
141142
*/
142-
function showLoginPopup() {
143+
async function showLoginPopup() {
143144
// If popup is already visible, just close it
144145
if (isPopupVisible) {
145146
closePopup();
@@ -149,8 +150,19 @@ define(function (require, exports, module) {
149150
// create the popup element
150151
closePopup(); // close any existing popup first
151152

153+
// Check if non-logged-in user has trial entitlements
154+
const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements();
155+
let templateData = {Strings};
156+
157+
if (effectiveEntitlements && effectiveEntitlements.isInProTrial) {
158+
// Add trial information to template data
159+
const planName = StringUtils.format(Strings.PROMO_PRO_TRIAL_DAYS_LEFT,
160+
effectiveEntitlements.trialDaysRemaining);
161+
templateData.trialInfo = {planName};
162+
}
163+
152164
// Render template with data
153-
const renderedTemplate = Mustache.render(loginTemplate, {Strings});
165+
const renderedTemplate = Mustache.render(loginTemplate, templateData);
154166
$popup = $(renderedTemplate);
155167

156168
$("body").append($popup);
@@ -206,12 +218,15 @@ define(function (require, exports, module) {
206218
};
207219
}
208220
if (entitlements && entitlements.plan && entitlements.plan.paidSubscriber) {
209-
// Paid subscriber: show plan name with feather icon
210-
const planName = entitlements.plan.name || "Phoenix Pro";
221+
// Pro user (paid subscriber or trial): show plan name with feather icon
222+
let displayName = entitlements.plan.name || "Phoenix Pro";
223+
if (entitlements.isInProTrial) {
224+
displayName = `Phoenix Pro`; // Just "Phoenix Pro" for branding, not "Phoenix Pro Trial"
225+
}
211226
$brandingLink
212227
.attr("href", "https://account.phcode.dev")
213228
.addClass("phoenix-pro")
214-
.html(`${planName}<i class="fa-solid fa-feather orange-gold" style="margin-left: 3px;"></i>`);
229+
.html(`${displayName}<i class="fa-solid fa-feather orange-gold" style="margin-left: 3px;"></i>`);
215230
} else {
216231
// Free user: show phcode.io branding
217232
$brandingLink
@@ -333,17 +348,29 @@ define(function (require, exports, module) {
333348
// Update plan information
334349
if (entitlements.plan) {
335350
const $planName = $popup.find('.user-plan-name');
336-
351+
337352
// Update plan class and content based on paid subscriber status
338353
$planName.removeClass('user-plan-free user-plan-paid');
339-
354+
340355
if (entitlements.plan.paidSubscriber) {
341-
// Use pro styling with feather icon for paid subscribers
342-
const proTitle = `<span class="phoenix-pro-title">
343-
<span class="pro-plan-name">${entitlements.plan.name}</span>
344-
<i class="fa-solid fa-feather orange-gold" style="margin-left: 3px;"></i>
345-
</span>`;
346-
$planName.addClass('user-plan-paid').html(proTitle);
356+
// Use pro styling with feather icon for pro users (paid or trial)
357+
if (entitlements.isInProTrial) {
358+
// For trial users: separate "Phoenix Pro" with icon from "(X days left)" text
359+
const planName = StringUtils.format(Strings.PROMO_PRO_TRIAL_DAYS_LEFT,
360+
entitlements.trialDaysRemaining);
361+
const proTitle = `<span class="phoenix-pro-title-plain">
362+
<span class="pro-plan-name">${planName}</span>
363+
<i class="fa-solid fa-feather" style="margin-left: 3px;"></i>
364+
</span>`;
365+
$planName.addClass('user-plan-paid').html(proTitle);
366+
} else {
367+
// For paid users: regular plan name with icon
368+
const proTitle = `<span class="phoenix-pro-title-plain">
369+
<span class="pro-plan-name">${entitlements.plan.name}</span>
370+
<i class="fa-solid fa-feather" style="margin-left: 3px;"></i>
371+
</span>`;
372+
$planName.addClass('user-plan-paid').html(proTitle);
373+
}
347374
} else {
348375
// Use simple text for free users
349376
$planName.addClass('user-plan-free').text(entitlements.plan.name);
@@ -412,8 +439,8 @@ define(function (require, exports, module) {
412439

413440
positionPopup();
414441

415-
// Apply cached entitlements immediately if available (including quota/messages)
416-
KernalModeTrust.loginService.getEntitlements(false).then(cachedEntitlements => {
442+
// Apply cached effective entitlements immediately if available (including quota/messages)
443+
KernalModeTrust.loginService.getEffectiveEntitlements(false).then(cachedEntitlements => {
417444
if (cachedEntitlements && isPopupVisible) {
418445
_updatePopupWithEntitlements(cachedEntitlements);
419446
}
@@ -462,8 +489,7 @@ define(function (require, exports, module) {
462489
*/
463490
async function _refreshEntitlementsInBackground() {
464491
try {
465-
// Fetch fresh entitlements from API
466-
const freshEntitlements = await KernalModeTrust.loginService.getEntitlements(true); // Force refresh to get latest data
492+
const freshEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements(true);
467493

468494
// Only update popup if it's still visible
469495
if (isPopupVisible && $popup && freshEntitlements) {
@@ -524,6 +550,23 @@ define(function (require, exports, module) {
524550
});
525551
}
526552

553+
/**
554+
* Initialize branding for non-logged-in trial users on startup
555+
*/
556+
async function _initializeBrandingForTrialUsers() {
557+
// Only check if user is not logged in
558+
if (!KernalModeTrust.loginService.isLoggedIn()) {
559+
try {
560+
const effectiveEntitlements = await KernalModeTrust.loginService.getEffectiveEntitlements();
561+
if (effectiveEntitlements && effectiveEntitlements.isInProTrial) {
562+
_updateBranding(effectiveEntitlements);
563+
}
564+
} catch (error) {
565+
console.error('Failed to initialize branding for trial users:', error);
566+
}
567+
}
568+
}
569+
527570
function init() {
528571
const helpButtonID = "user-profile-button";
529572
$icon = $("<a>")
@@ -537,6 +580,9 @@ define(function (require, exports, module) {
537580
$icon.on('click', ()=>{
538581
togglePopup();
539582
});
583+
584+
// Initialize branding for non-logged-in trial users
585+
_initializeBrandingForTrialUsers();
540586
}
541587

542588
function setNotLoggedIn() {
@@ -560,11 +606,11 @@ define(function (require, exports, module) {
560606
}
561607
_updateProfileIcon(initial, color);
562608

563-
// Preload entitlements when user logs in
564-
KernalModeTrust.loginService.getEntitlements()
609+
// Preload effective entitlements when user logs in
610+
KernalModeTrust.loginService.getEffectiveEntitlements()
565611
.then(_updateBranding)
566612
.catch(error => {
567-
console.error('Failed to preload entitlements on login:', error);
613+
console.error('Failed to preload effective entitlements on login:', error);
568614
});
569615
}
570616

src/services/promotions.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,16 @@ define(function (require, exports, module) {
191191
}
192192

193193
/**
194-
* Check if pro trial is currently activated
194+
* Get remaining pro trial days
195+
* Returns 0 if no trial or trial expired
195196
*/
196-
async function isProTrialActivated() {
197+
async function getProTrialDaysRemaining() {
197198
const trialData = await _getTrialData();
198199
if (!trialData) {
199-
return false;
200+
return 0;
200201
}
201202

202-
const remainingDays = _calculateRemainingTrialDays(trialData);
203-
204-
return remainingDays > 0;
203+
return _calculateRemainingTrialDays(trialData);
205204
}
206205

207206
async function activateProTrial() {
@@ -321,7 +320,7 @@ define(function (require, exports, module) {
321320
}, TRIAL_POLL_MS);
322321

323322
// Add to secure exports
324-
LoginService.isProTrialActivated = isProTrialActivated;
323+
LoginService.getProTrialDaysRemaining = getProTrialDaysRemaining;
325324
LoginService.EVENT_PRO_UPGRADE_ON_INSTALL = EVENT_PRO_UPGRADE_ON_INSTALL;
326325

327326
// no public exports to prevent extension tampering

src/styles/brackets_core_ui_variables.less

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,6 @@
298298
#e6a600 75%, /* darker amber */
299299
#c99700 100% /* rich golden brown */
300300
);
301+
302+
@phoenixPro-brand-light: #cc5500;
303+
@phoenixPro-brand-dark: #ff8c42;

src/styles/phoenix-pro.less

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
}
2222
}
2323

24+
.phoenix-pro-title-plain {
25+
color: @phoenixPro-brand-light;
26+
27+
.dark & {
28+
color: @phoenixPro-brand-dark;
29+
}
30+
}
31+
2432
/* Dialog styles with light default + .dark overrides */
2533
.browser-login-waiting-dialog, .pro-upgrade-dialog{
2634
/* ---- Layout ---- */

0 commit comments

Comments
 (0)