Skip to content

Commit a76a3a5

Browse files
committed
feat: entitlements api call for login and user based entitlements
1 parent b07db9f commit a76a3a5

File tree

5 files changed

+159
-11
lines changed

5 files changed

+159
-11
lines changed

src/nls/root/strings.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,6 @@ define({
16061606
"CONTACT_SUPPORT": "Contact support",
16071607
"SIGN_OUT": "Sign out",
16081608
"ACCOUNT_DETAILS": "Account Details",
1609-
"AI_QUOTA_USED": "AI quota used",
16101609
"LOGIN_REFRESH": "Check Login Status",
16111610
"SIGN_IN_WAITING_TITLE": "Waiting for Sign In",
16121611
"SIGN_IN_WAITING_MESSAGE": "Please complete sign-in in the new tab, then return here.",

src/services/html/profile-popup.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
</div>
1414
</div>
1515
<div class="popup-body">
16-
<div class="profile-section">
16+
<div class="profile-section html-message forced-hidden">
17+
</div>
18+
<div class="profile-section quota-section forced-hidden">
1719
<div class="quota-header">
18-
<span>{{Strings.AI_QUOTA_USED}}</span>
19-
<span>{{quotaUsed}} / {{quotaTotal}} {{quotaUnit}}</span>
20+
<span class="titleText">{{titleText}}</span>
21+
<span class="usageText">{{usageText}}</span>
2022
</div>
2123
<div class="progress-bar">
22-
<div class="progress-fill" style="width: {{quotaPercent}}%;"></div>
24+
<div class="progress-fill" style="width: {{usedPercent}}%;"></div>
2325
</div>
2426
</div>
2527

src/services/login-browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ define(function (require, exports, module) {
418418
secureExports.getProfile = getProfile;
419419
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
420420
secureExports.getAccountBaseURL = _getAccountBaseURL;
421+
secureExports.getEntitlements = ProfileMenu.getEntitlements;
421422
}
422423

423424
// public exports

src/services/login-desktop.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ define(function (require, exports, module) {
417417
secureExports.getProfile = getProfile;
418418
secureExports.verifyLoginStatus = () => _verifyLogin(false);
419419
secureExports.getAccountBaseURL = getAccountBaseURL;
420+
secureExports.getEntitlements = ProfileMenu.getEntitlements;
420421
}
421422

422423
// public exports

src/services/profile-menu.js

Lines changed: 151 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,66 @@ define(function (require, exports, module) {
183183
_setupDocumentClickHandler();
184184
}
185185

186+
// Cached entitlements data
187+
let cachedEntitlements = null;
188+
189+
/**
190+
* Get entitlements from API or cache
191+
* Returns null if user is not logged in
192+
*/
193+
async function getEntitlements(forceRefresh = false) {
194+
// Return null if not logged in
195+
if (!KernalModeTrust.loginService.isLoggedIn()) {
196+
return null;
197+
}
198+
199+
// Return cached data if available and not forcing refresh
200+
if (cachedEntitlements && !forceRefresh) {
201+
return cachedEntitlements;
202+
}
203+
204+
try {
205+
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
206+
const language = Phoenix.app && Phoenix.app.language ? Phoenix.app.language : 'en';
207+
let url = `${accountBaseURL}/getAppEntitlements?lang=${language}`;
208+
let fetchOptions = {
209+
method: 'GET',
210+
headers: {
211+
'Accept': 'application/json'
212+
}
213+
};
214+
215+
// Handle different authentication methods for browser vs desktop
216+
if (Phoenix.isNativeApp) {
217+
// Desktop app: use appSessionID and validationCode
218+
const profile = KernalModeTrust.loginService.getProfile();
219+
if (profile && profile.apiKey && profile.validationCode) {
220+
url += `&appSessionID=${encodeURIComponent(profile.apiKey)}&validationCode=${encodeURIComponent(profile.validationCode)}`;
221+
} else {
222+
console.error('Missing appSessionID or validationCode for desktop app entitlements');
223+
return null;
224+
}
225+
} else {
226+
// Browser app: use session cookies
227+
fetchOptions.credentials = 'include';
228+
}
229+
230+
const response = await fetch(url, fetchOptions);
231+
232+
if (response.ok) {
233+
const result = await response.json();
234+
if (result.isSuccess) {
235+
cachedEntitlements = result;
236+
return cachedEntitlements;
237+
}
238+
}
239+
} catch (error) {
240+
console.error('Failed to fetch entitlements:', error);
241+
}
242+
243+
return null;
244+
}
245+
186246
let userEmail="";
187247
class SecureEmail extends HTMLElement {
188248
constructor() {
@@ -284,6 +344,50 @@ define(function (require, exports, module) {
284344
}
285345
}
286346

347+
/**
348+
* Update popup content with entitlements data
349+
*/
350+
function _updatePopupWithEntitlements(entitlements) {
351+
if (!$popup || !entitlements) {
352+
return;
353+
}
354+
355+
// Update plan information
356+
if (entitlements.plan) {
357+
const $planName = $popup.find('.user-plan-name');
358+
$planName.text(entitlements.plan.name);
359+
360+
// Update plan class based on paid subscriber status
361+
$planName.removeClass('user-plan-free user-plan-paid');
362+
const planClass = entitlements.plan.paidSubscriber ? 'user-plan-paid' : 'user-plan-free';
363+
$planName.addClass(planClass);
364+
}
365+
366+
// Update quota section if available
367+
if (entitlements.profileview && entitlements.profileview.quota) {
368+
const $quotaSection = $popup.find('.quota-section');
369+
const quota = entitlements.profileview.quota;
370+
371+
// Remove forced-hidden and show quota section
372+
$quotaSection.removeClass('forced-hidden');
373+
374+
// Update quota content
375+
$quotaSection.find('.titleText').text(quota.titleText);
376+
$quotaSection.find('.usageText').text(quota.usageText);
377+
$quotaSection.find('.progress-fill').css('width', quota.usedPercent + '%');
378+
}
379+
380+
// Update HTML message if available
381+
if (entitlements.profileview && entitlements.profileview.htmlMessage) {
382+
const $htmlMessageSection = $popup.find('.html-message');
383+
$htmlMessageSection.removeClass('forced-hidden');
384+
$htmlMessageSection.html(entitlements.profileview.htmlMessage);
385+
}
386+
387+
// Reposition popup after content changes
388+
positionPopup();
389+
}
390+
287391
/**
288392
* Shows the user profile popup when the user is logged in
289393
*/
@@ -296,24 +400,36 @@ define(function (require, exports, module) {
296400
const profileData = KernalModeTrust.loginService.getProfile();
297401
userEmail = profileData.email;
298402
userName = profileData.firstName + " " + profileData.lastName;
403+
404+
// Default template data (fallback) - start with cached plan info if available
299405
const templateData = {
300406
initials: profileData.profileIcon.initials,
301407
avatarColor: profileData.profileIcon.color,
302-
planClass: "user-plan-free", // "user-plan-paid" for paid plan
408+
planClass: "user-plan-free",
303409
planName: "Free Plan",
304-
quotaUsed: "7,000",
305-
quotaTotal: "10,000",
306-
quotaUnit: "tokens",
307-
quotaPercent: 70,
410+
titleText: "Ai Quota Used",
411+
usageText: "100 / 200 credits",
412+
usedPercent: 0,
308413
Strings: Strings
309414
};
310415

311-
// Render template with data
416+
// Check for cached entitlements synchronously for immediate basic plan info
417+
if (cachedEntitlements && cachedEntitlements.plan) {
418+
templateData.planClass = cachedEntitlements.plan.paidSubscriber ? "user-plan-paid" : "user-plan-free";
419+
templateData.planName = cachedEntitlements.plan.name;
420+
}
421+
422+
// Render template with data immediately
312423
const renderedTemplate = Mustache.render(profileTemplate, templateData);
313424
$popup = $(renderedTemplate);
314425

315426
$("body").append($popup);
316427
isPopupVisible = true;
428+
429+
// Apply cached entitlements immediately if available (including quota/messages)
430+
if (cachedEntitlements) {
431+
_updatePopupWithEntitlements(cachedEntitlements);
432+
}
317433
positionPopup();
318434

319435
PopUpManager.addPopUp($popup, function() {
@@ -347,6 +463,26 @@ define(function (require, exports, module) {
347463

348464
// Load user details iframe for browser apps (after popup is created)
349465
_loadUserDetailsIframe();
466+
467+
// Refresh entitlements in background and update popup if still visible
468+
_refreshEntitlementsInBackground();
469+
}
470+
471+
/**
472+
* Refresh entitlements in background and update popup if still visible
473+
*/
474+
async function _refreshEntitlementsInBackground() {
475+
try {
476+
// Fetch fresh entitlements from API
477+
const freshEntitlements = await getEntitlements(true); // Force refresh to get latest data
478+
479+
// Only update popup if it's still visible
480+
if (isPopupVisible && $popup && freshEntitlements) {
481+
_updatePopupWithEntitlements(freshEntitlements);
482+
}
483+
} catch (error) {
484+
console.error('Failed to refresh entitlements in background:', error);
485+
}
350486
}
351487

352488
/**
@@ -421,6 +557,9 @@ define(function (require, exports, module) {
421557
closePopup();
422558
}
423559
_removeProfileIcon();
560+
561+
// Clear cached entitlements when user logs out
562+
cachedEntitlements = null;
424563
}
425564

426565
function setLoggedIn(initial, color) {
@@ -429,9 +568,15 @@ define(function (require, exports, module) {
429568
closePopup();
430569
}
431570
_updateProfileIcon(initial, color);
571+
572+
// Preload entitlements when user logs in
573+
getEntitlements().catch(error => {
574+
console.error('Failed to preload entitlements on login:', error);
575+
});
432576
}
433577

434578
exports.init = init;
435579
exports.setNotLoggedIn = setNotLoggedIn;
436580
exports.setLoggedIn = setLoggedIn;
581+
exports.getEntitlements = getEntitlements;
437582
});

0 commit comments

Comments
 (0)