Skip to content

Commit 5096499

Browse files
committed
refactor: getEntitlements into login-service.js
1 parent fb85aee commit 5096499

File tree

4 files changed

+147
-99
lines changed

4 files changed

+147
-99
lines changed

src/services/login-browser.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ define(function (require, exports, module) {
5151
Strings = require("strings"),
5252
StringUtils = require("utils/StringUtils"),
5353
ProfileMenu = require("./profile-menu"),
54+
LoginService = require("./login-service"),
5455
Mustache = require("thirdparty/mustache/mustache"),
5556
browserLoginWaitingTemplate = require("text!./html/browser-login-waiting-dialog.html");
5657

@@ -77,10 +78,6 @@ define(function (require, exports, module) {
7778
EventDispatcher.makeEventDispatcher(exports);
7879
EventDispatcher.makeEventDispatcher(secureExports);
7980

80-
// Event constants
81-
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";
82-
secureExports.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
83-
8481
const _EVT_PAGE_FOCUSED = "page_focused";
8582
$(window).focus(function () {
8683
exports.trigger(_EVT_PAGE_FOCUSED);
@@ -422,7 +419,8 @@ define(function (require, exports, module) {
422419
secureExports.getProfile = getProfile;
423420
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
424421
secureExports.getAccountBaseURL = _getAccountBaseURL;
425-
secureExports.getEntitlements = ProfileMenu.getEntitlements;
422+
secureExports.getEntitlements = LoginService.getEntitlements;
423+
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
426424
}
427425

428426
// public exports

src/services/login-desktop.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ define(function (require, exports, module) {
2727
Strings = require("strings"),
2828
NativeApp = require("utils/NativeApp"),
2929
ProfileMenu = require("./profile-menu"),
30+
LoginService = require("./login-service"),
3031
Mustache = require("thirdparty/mustache/mustache"),
3132
NodeConnector = require("NodeConnector"),
3233
otpDialogTemplate = require("text!./html/otp-dialog.html");
@@ -53,10 +54,6 @@ define(function (require, exports, module) {
5354
EventDispatcher.makeEventDispatcher(exports);
5455
EventDispatcher.makeEventDispatcher(secureExports);
5556

56-
// Event constants
57-
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";
58-
secureExports.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
59-
6057
const _EVT_PAGE_FOCUSED = "page_focused";
6158
$(window).focus(function () {
6259
exports.trigger(_EVT_PAGE_FOCUSED);
@@ -421,7 +418,8 @@ define(function (require, exports, module) {
421418
secureExports.getProfile = getProfile;
422419
secureExports.verifyLoginStatus = () => _verifyLogin(false);
423420
secureExports.getAccountBaseURL = getAccountBaseURL;
424-
secureExports.getEntitlements = ProfileMenu.getEntitlements;
421+
secureExports.getEntitlements = LoginService.getEntitlements;
422+
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
425423
}
426424

427425
// public exports

src/services/login-service.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it under
7+
* the terms of the GNU Affero General Public License as published by the Free
8+
* Software Foundation, either version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Affero General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
16+
*
17+
*/
18+
19+
/**
20+
* Shared Login Service
21+
*
22+
* This module contains shared login service functionality used by both
23+
* browser and desktop login implementations, including entitlements management.
24+
*/
25+
26+
define(function (require, exports, module) {
27+
28+
const KernalModeTrust = window.KernalModeTrust;
29+
if(!KernalModeTrust){
30+
// integrated extensions will have access to kernal mode, but not external extensions
31+
throw new Error("Login service should have access to KernalModeTrust. Cannot boot without trust ring");
32+
}
33+
34+
// Event constants
35+
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";
36+
37+
// Cached entitlements data
38+
let cachedEntitlements = null;
39+
40+
/**
41+
* Get entitlements from API or cache
42+
* Returns null if user is not logged in
43+
*/
44+
async function getEntitlements(forceRefresh = false) {
45+
// Return null if not logged in
46+
if (!KernalModeTrust.loginService.isLoggedIn()) {
47+
return null;
48+
}
49+
50+
// Return cached data if available and not forcing refresh
51+
if (cachedEntitlements && !forceRefresh) {
52+
return cachedEntitlements;
53+
}
54+
55+
try {
56+
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
57+
const language = Phoenix.app && Phoenix.app.language ? Phoenix.app.language : 'en';
58+
let url = `${accountBaseURL}/getAppEntitlements?lang=${language}`;
59+
let fetchOptions = {
60+
method: 'GET',
61+
headers: {
62+
'Accept': 'application/json'
63+
}
64+
};
65+
66+
// Handle different authentication methods for browser vs desktop
67+
if (Phoenix.isNativeApp) {
68+
// Desktop app: use appSessionID and validationCode
69+
const profile = KernalModeTrust.loginService.getProfile();
70+
if (profile && profile.apiKey && profile.validationCode) {
71+
url += `&appSessionID=${encodeURIComponent(profile.apiKey)}&validationCode=${encodeURIComponent(profile.validationCode)}`;
72+
} else {
73+
console.error('Missing appSessionID or validationCode for desktop app entitlements');
74+
return null;
75+
}
76+
} else {
77+
// Browser app: use session cookies
78+
fetchOptions.credentials = 'include';
79+
}
80+
81+
const response = await fetch(url, fetchOptions);
82+
83+
if (response.ok) {
84+
const result = await response.json();
85+
if (result.isSuccess) {
86+
// Check if entitlements actually changed
87+
const entitlementsChanged = JSON.stringify(cachedEntitlements) !== JSON.stringify(result);
88+
89+
cachedEntitlements = result;
90+
91+
// Trigger event if entitlements changed
92+
if (entitlementsChanged) {
93+
KernalModeTrust.loginService.trigger(EVENT_ENTITLEMENTS_CHANGED, result);
94+
}
95+
96+
return cachedEntitlements;
97+
}
98+
}
99+
} catch (error) {
100+
console.error('Failed to fetch entitlements:', error);
101+
}
102+
103+
return null;
104+
}
105+
106+
/**
107+
* Clear cached entitlements and trigger change event
108+
* Called when user logs out
109+
*/
110+
function clearEntitlements() {
111+
if (cachedEntitlements) {
112+
cachedEntitlements = null;
113+
114+
// Trigger event when entitlements are cleared
115+
if (KernalModeTrust.loginService.trigger) {
116+
KernalModeTrust.loginService.trigger(EVENT_ENTITLEMENTS_CHANGED, null);
117+
}
118+
}
119+
}
120+
121+
// Exports
122+
exports.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
123+
exports.getEntitlements = getEntitlements;
124+
exports.clearEntitlements = clearEntitlements;
125+
});

src/services/profile-menu.js

Lines changed: 16 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ define(function (require, exports, module) {
22
const Mustache = require("thirdparty/mustache/mustache"),
33
PopUpManager = require("widgets/PopUpManager"),
44
ThemeManager = require("view/ThemeManager"),
5-
Strings = require("strings");
5+
Strings = require("strings"),
6+
LoginService = require("./login-service");
67

78
const KernalModeTrust = window.KernalModeTrust;
89
if(!KernalModeTrust){
@@ -183,74 +184,6 @@ define(function (require, exports, module) {
183184
_setupDocumentClickHandler();
184185
}
185186

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-
// Check if entitlements actually changed
236-
const entitlementsChanged = JSON.stringify(cachedEntitlements) !== JSON.stringify(result);
237-
238-
cachedEntitlements = result;
239-
240-
// Trigger event if entitlements changed
241-
if (entitlementsChanged && KernalModeTrust.loginService.trigger) {
242-
KernalModeTrust.loginService.trigger(KernalModeTrust.loginService.EVENT_ENTITLEMENTS_CHANGED, result);
243-
}
244-
245-
return cachedEntitlements;
246-
}
247-
}
248-
} catch (error) {
249-
console.error('Failed to fetch entitlements:', error);
250-
}
251-
252-
return null;
253-
}
254187

255188
let userEmail="";
256189
class SecureEmail extends HTMLElement {
@@ -422,11 +355,8 @@ define(function (require, exports, module) {
422355
Strings: Strings
423356
};
424357

425-
// Check for cached entitlements synchronously for immediate basic plan info
426-
if (cachedEntitlements && cachedEntitlements.plan) {
427-
templateData.planClass = cachedEntitlements.plan.paidSubscriber ? "user-plan-paid" : "user-plan-free";
428-
templateData.planName = cachedEntitlements.plan.name;
429-
}
358+
// Note: We don't await here to keep popup display instant
359+
// Cached entitlements will be applied asynchronously after popup is shown
430360

431361
// Render template with data immediately
432362
const renderedTemplate = Mustache.render(profileTemplate, templateData);
@@ -435,11 +365,16 @@ define(function (require, exports, module) {
435365
$("body").append($popup);
436366
isPopupVisible = true;
437367

438-
// Apply cached entitlements immediately if available (including quota/messages)
439-
if (cachedEntitlements) {
440-
_updatePopupWithEntitlements(cachedEntitlements);
441-
}
442368
positionPopup();
369+
370+
// Apply cached entitlements immediately if available (including quota/messages)
371+
KernalModeTrust.loginService.getEntitlements(false).then(cachedEntitlements => {
372+
if (cachedEntitlements && isPopupVisible) {
373+
_updatePopupWithEntitlements(cachedEntitlements);
374+
}
375+
}).catch(error => {
376+
console.error('Failed to apply cached entitlements to popup:', error);
377+
});
443378

444379
PopUpManager.addPopUp($popup, function() {
445380
$popup.remove();
@@ -483,7 +418,7 @@ define(function (require, exports, module) {
483418
async function _refreshEntitlementsInBackground() {
484419
try {
485420
// Fetch fresh entitlements from API
486-
const freshEntitlements = await getEntitlements(true); // Force refresh to get latest data
421+
const freshEntitlements = await KernalModeTrust.loginService.getEntitlements(true); // Force refresh to get latest data
487422

488423
// Only update popup if it's still visible
489424
if (isPopupVisible && $popup && freshEntitlements) {
@@ -568,14 +503,7 @@ define(function (require, exports, module) {
568503
_removeProfileIcon();
569504

570505
// Clear cached entitlements when user logs out
571-
if (cachedEntitlements) {
572-
cachedEntitlements = null;
573-
574-
// Trigger event when entitlements are cleared
575-
if (KernalModeTrust.loginService.trigger) {
576-
KernalModeTrust.loginService.trigger(KernalModeTrust.loginService.EVENT_ENTITLEMENTS_CHANGED, null);
577-
}
578-
}
506+
LoginService.clearEntitlements();
579507
}
580508

581509
function setLoggedIn(initial, color) {
@@ -586,13 +514,12 @@ define(function (require, exports, module) {
586514
_updateProfileIcon(initial, color);
587515

588516
// Preload entitlements when user logs in
589-
getEntitlements().catch(error => {
517+
KernalModeTrust.loginService.getEntitlements().catch(error => {
590518
console.error('Failed to preload entitlements on login:', error);
591519
});
592520
}
593521

594522
exports.init = init;
595523
exports.setNotLoggedIn = setNotLoggedIn;
596524
exports.setLoggedIn = setLoggedIn;
597-
exports.getEntitlements = getEntitlements;
598525
});

0 commit comments

Comments
 (0)