From 50cdc47ab13c9059b2e4b90663e982c497d65d48 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 13:32:49 +0530 Subject: [PATCH 1/6] feat: replace phoenix code support button with user profile button --- src/extensionsIntegrated/Phoenix/main.js | 8 ++++---- src/nls/root/strings.js | 1 + src/styles/brackets.less | 4 ++++ src/styles/images/circle-user-solid.svg | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 src/styles/images/circle-user-solid.svg diff --git a/src/extensionsIntegrated/Phoenix/main.js b/src/extensionsIntegrated/Phoenix/main.js index 94f0015324..ff6ad241d6 100644 --- a/src/extensionsIntegrated/Phoenix/main.js +++ b/src/extensionsIntegrated/Phoenix/main.js @@ -38,17 +38,17 @@ define(function (require, exports, module) { let $icon; function _addToolbarIcon() { - const helpButtonID = "help-button"; + const helpButtonID = "user-profile-button"; $icon = $("") .attr({ id: helpButtonID, href: "#", - class: "help", - title: Strings.CMD_SUPPORT + class: "user", + title: Strings.CMD_USER_PROFILE }) .appendTo($("#main-toolbar .bottom-buttons")); $icon.on('click', ()=>{ - Phoenix.app.openURLInDefaultBrowser(brackets.config.support_url); + console.log("User profile button was clicked"); }); } function _showUnSupportedBrowserDialogue() { diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 7b996f7da4..4f4fe37d5d 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -626,6 +626,7 @@ define({ "CMD_AUTO_UPDATE": "Auto Update", "CMD_HOW_TO_USE_BRACKETS": "How to Use {APP_NAME}", "CMD_SUPPORT": "{APP_NAME} Support", + "CMD_USER_PROFILE": "User Profile", "CMD_DOCS": "Help, Getting Started", "CMD_SUGGEST": "Suggest a Feature", "CMD_REPORT_ISSUE": "Report Issue", diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 6b4b43a611..5b1e38e825 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -672,6 +672,10 @@ a, img { background-image: url("images/discuss-icon.svg"); } +.user { + background-image: url("images/circle-user-solid.svg"); +} + #editor-holder { position: relative; diff --git a/src/styles/images/circle-user-solid.svg b/src/styles/images/circle-user-solid.svg new file mode 100644 index 0000000000..836691525d --- /dev/null +++ b/src/styles/images/circle-user-solid.svg @@ -0,0 +1 @@ + From 977d1bf6b2869b1d3787502b15f1852f1a98ea6e Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 18:01:03 +0530 Subject: [PATCH 2/6] feat: user profile implementation --- .../Phoenix/html/login-dialog.html | 17 ++ .../Phoenix/html/profile-panel.html | 39 ++++ src/extensionsIntegrated/Phoenix/main.js | 5 +- .../Phoenix/profile-menu.js | 190 ++++++++++++++++++ src/styles/brackets.less | 1 + src/styles/user-profile.less | 29 +++ 6 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/extensionsIntegrated/Phoenix/html/login-dialog.html create mode 100644 src/extensionsIntegrated/Phoenix/html/profile-panel.html create mode 100644 src/extensionsIntegrated/Phoenix/profile-menu.js create mode 100644 src/styles/user-profile.less diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html new file mode 100644 index 0000000000..a06d7dfdce --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -0,0 +1,17 @@ +
+ + +
diff --git a/src/extensionsIntegrated/Phoenix/html/profile-panel.html b/src/extensionsIntegrated/Phoenix/html/profile-panel.html new file mode 100644 index 0000000000..9eb2cda00d --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/html/profile-panel.html @@ -0,0 +1,39 @@ +
+ + +
diff --git a/src/extensionsIntegrated/Phoenix/main.js b/src/extensionsIntegrated/Phoenix/main.js index ff6ad241d6..5bb8dcacbc 100644 --- a/src/extensionsIntegrated/Phoenix/main.js +++ b/src/extensionsIntegrated/Phoenix/main.js @@ -32,7 +32,8 @@ define(function (require, exports, module) { Strings = require("strings"), Dialogs = require("widgets/Dialogs"), NotificationUI = require("widgets/NotificationUI"), - DefaultDialogs = require("widgets/DefaultDialogs"); + DefaultDialogs = require("widgets/DefaultDialogs"), + ProfileMenu = require("./profile-menu"); const PERSIST_STORAGE_DIALOG_DELAY_SECS = 60000; let $icon; @@ -48,7 +49,7 @@ define(function (require, exports, module) { }) .appendTo($("#main-toolbar .bottom-buttons")); $icon.on('click', ()=>{ - console.log("User profile button was clicked"); + ProfileMenu.init(); }); } function _showUnSupportedBrowserDialogue() { diff --git a/src/extensionsIntegrated/Phoenix/profile-menu.js b/src/extensionsIntegrated/Phoenix/profile-menu.js new file mode 100644 index 0000000000..5b8a83d62b --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/profile-menu.js @@ -0,0 +1,190 @@ +define(function (require, exports, module) { + const loginTemplate = require("text!./html/login-dialog.html"); + const profileTemplate = require("text!./html/profile-panel.html"); + + // for the popup DOM element + let $popup = null; + + // this is to track whether the popup is visible or not + let isPopupVisible = false; + + // if user is logged in we show the profile menu, otherwise we show the login menu + const isLoggedIn = false; + + function _handleSignInBtnClick() { + console.log("User clicked sign in button"); + } + + function _handleSignOutBtnClick() { + console.log("User clicked sign out"); + } + + function _handleContactSupportBtnClick() { + Phoenix.app.openURLInDefaultBrowser(brackets.config.support_url); + } + + function _handleAccountDetailsBtnClick() { + console.log("User clicked account details"); + } + + /** + * Close the popup if it's open + * this is called at various instances like when the user click on the profile icon even if the popup is open + * or when user clicks somewhere else on the document + */ + function closePopup() { + if ($popup) { + $popup.remove(); + $popup = null; + isPopupVisible = false; + + // remove global click handler + $(document).off("click.profilePopup"); + } + } + + /** + * handle clicks outside the popup to close it + */ + function handleDocumentClick(e) { + // If popup exists and click is outside the popup + if ($popup && $popup.length && !$popup[0].contains(e.target)) { + // If the click is not on the user-profile-button (which would toggle the popup) + if (e.target.id !== "user-profile-button" && !$(e.target).closest("#user-profile-button").length) { + closePopup(); + } + } + } + + /** + * this function is to position the popup near the profile button + */ + function positionPopup() { + const $profileButton = $("#user-profile-button"); + + if ($profileButton.length && $popup) { + const buttonPos = $profileButton.offset(); + const popupWidth = $popup.outerWidth(); + const windowWidth = $(window).width(); + + // pos above the profile button + let top = buttonPos.top - $popup.outerHeight() - 10; + + // If popup would go off the right edge of the window, align right edge of popup with right edge of button + let left = Math.min( + buttonPos.left - popupWidth + $profileButton.outerWidth(), + windowWidth - popupWidth - 10 + ); + + // never go off left edge + left = Math.max(10, left); + + $popup.css({ + top: top + "px", + left: left + "px" + }); + } + } + + /** + * Shows the sign-in popup when the user is not logged in + */ + function showLoginPopup() { + // If popup is already visible, just close it + if (isPopupVisible) { + closePopup(); + return; + } + + // create the popup element + closePopup(); // close any existing popup first + $popup = $(loginTemplate); + $("body").append($popup); + isPopupVisible = true; + + // Position the popup + positionPopup(); + + // event handlers for buttons + $popup.find("#phoenix-signin-btn").on("click", function () { + _handleSignInBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-support-btn").on("click", function () { + _handleContactSupportBtnClick(); + closePopup(); + }); + + // Set up global click handler to close popup when clicking outside + // Delay attaching to avoid immediate closing + setTimeout(function () { + $(document).on("click.profilePopup", handleDocumentClick); + }, 0); + } + + /** + * Shows the user profile popup when the user is logged in + */ + function showProfilePopup() { + // If popup is already visible, just close it + if (isPopupVisible) { + closePopup(); + return; + } + + closePopup(); + $popup = $(profileTemplate); + $("body").append($popup); + isPopupVisible = true; + positionPopup(); + + $popup.find("#phoenix-account-btn").on("click", function () { + _handleAccountDetailsBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-support-btn").on("click", function () { + _handleContactSupportBtnClick(); + closePopup(); + }); + + $popup.find("#phoenix-signout-btn").on("click", function () { + _handleSignOutBtnClick(); + closePopup(); + }); + + // Set up global click handler to close popup when clicking outside + // Delay attaching to avoid immediate closing + setTimeout(function () { + $(document).on("click.profilePopup", handleDocumentClick); + }, 0); + } + + /** + * Toggle the profile popup based on the user's login status + * this function is called inside the src/extensionsIntegrated/Phoenix/main.js when user clicks on the profile icon + */ + function init() { + // check if the popup is already visible or not. if visible close it + if (isPopupVisible) { + closePopup(); + return; + } + + if (isLoggedIn) { + showProfilePopup(); + } else { + showLoginPopup(); + } + + // handle window resize to reposition popup + $(window).on("resize.profilePopup", function () { + if (isPopupVisible) { + positionPopup(); + } + }); + } + + exports.init = init; +}); diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 5b1e38e825..7deb9ff685 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -44,6 +44,7 @@ @import "Extn-TabBar.less"; @import "Extn-DisplayShortcuts.less"; @import "Extn-CSSColorPreview.less"; +@import "user-profile.less"; /* Overall layout */ diff --git a/src/styles/user-profile.less b/src/styles/user-profile.less new file mode 100644 index 0000000000..b6ce9f2f46 --- /dev/null +++ b/src/styles/user-profile.less @@ -0,0 +1,29 @@ +.profile-popup { + background-color: #2c2c2c; + color: #ffffff; + border-radius: 6px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + width: 300px; + overflow: hidden; + position: absolute; + z-index: 9999; + + .popup-header { + padding: 15px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + } + + .popup-body { + padding: 15px; + } + + .popup-title { + margin: 0; + font-size: 18px; + font-weight: normal; + } +} + +.dark .profile-popup { + background-color: #1d1f21; +} From 9a1e2106d653e95613737f004bb7b0bebb7b7517 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 19:02:12 +0530 Subject: [PATCH 3/6] fix: remove hard-coded values for easier integration --- .../Phoenix/html/login-dialog.html | 6 +- .../Phoenix/html/profile-panel.html | 18 +++--- .../Phoenix/profile-menu.js | 57 ++++++++++++++++--- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html index a06d7dfdce..3bbb928a6c 100644 --- a/src/extensionsIntegrated/Phoenix/html/login-dialog.html +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -1,16 +1,16 @@
diff --git a/src/extensionsIntegrated/Phoenix/html/profile-panel.html b/src/extensionsIntegrated/Phoenix/html/profile-panel.html index 9eb2cda00d..d4eaf8a093 100644 --- a/src/extensionsIntegrated/Phoenix/html/profile-panel.html +++ b/src/extensionsIntegrated/Phoenix/html/profile-panel.html @@ -2,38 +2,38 @@
diff --git a/src/extensionsIntegrated/Phoenix/profile-menu.js b/src/extensionsIntegrated/Phoenix/profile-menu.js index 5b8a83d62b..3df2c9848a 100644 --- a/src/extensionsIntegrated/Phoenix/profile-menu.js +++ b/src/extensionsIntegrated/Phoenix/profile-menu.js @@ -1,4 +1,7 @@ define(function (require, exports, module) { + const Mustache = require("thirdparty/mustache/mustache"); + + // HTML templates const loginTemplate = require("text!./html/login-dialog.html"); const profileTemplate = require("text!./html/profile-panel.html"); @@ -9,7 +12,27 @@ define(function (require, exports, module) { let isPopupVisible = false; // if user is logged in we show the profile menu, otherwise we show the login menu - const isLoggedIn = false; + const isLoggedIn = true; + + const defaultLoginData = { + welcomeTitle: "Welcome to Phoenix Code", + signInBtnText: "Sign in to your account", + supportBtnText: "Contact support" + }; + + const defaultProfileData = { + initials: "CA", + userName: "Charly A.", + planName: "Paid Plan", + quotaLabel: "AI quota used", + quotaUsed: "7,000", + quotaTotal: "10,000", + quotaUnit: "tokens", + quotaPercent: 70, + accountBtnText: "Account details", + supportBtnText: "Contact support", + signOutBtnText: "Sign out" + }; function _handleSignInBtnClick() { console.log("User clicked sign in button"); @@ -88,21 +111,28 @@ define(function (require, exports, module) { /** * Shows the sign-in popup when the user is not logged in + * @param {Object} loginData - Data to populate the template (optional) */ - function showLoginPopup() { + function showLoginPopup(loginData) { // If popup is already visible, just close it if (isPopupVisible) { closePopup(); return; } + // Merge provided data with defaults + const templateData = $.extend({}, defaultLoginData, loginData || {}); + // create the popup element closePopup(); // close any existing popup first - $popup = $(loginTemplate); + + // Render template with data + const renderedTemplate = Mustache.render(loginTemplate, templateData); + $popup = $(renderedTemplate); + $("body").append($popup); isPopupVisible = true; - // Position the popup positionPopup(); // event handlers for buttons @@ -125,16 +155,24 @@ define(function (require, exports, module) { /** * Shows the user profile popup when the user is logged in + * @param {Object} profileData - Data to populate the template (optional) */ - function showProfilePopup() { + function showProfilePopup(profileData) { // If popup is already visible, just close it if (isPopupVisible) { closePopup(); return; } + // Merge provided data with defaults + const templateData = $.extend({}, defaultProfileData, profileData || {}); + closePopup(); - $popup = $(profileTemplate); + + // Render template with data + const renderedTemplate = Mustache.render(profileTemplate, templateData); + $popup = $(renderedTemplate); + $("body").append($popup); isPopupVisible = true; positionPopup(); @@ -164,8 +202,9 @@ define(function (require, exports, module) { /** * Toggle the profile popup based on the user's login status * this function is called inside the src/extensionsIntegrated/Phoenix/main.js when user clicks on the profile icon + * @param {Object} data - Data to populate the templates (optional) */ - function init() { + function init(data) { // check if the popup is already visible or not. if visible close it if (isPopupVisible) { closePopup(); @@ -173,9 +212,9 @@ define(function (require, exports, module) { } if (isLoggedIn) { - showProfilePopup(); + showProfilePopup(data); } else { - showLoginPopup(); + showLoginPopup(data); } // handle window resize to reposition popup From bd8cd0f11d16fe349a779cdf486654140f33d252 Mon Sep 17 00:00:00 2001 From: Pluto Date: Fri, 16 May 2025 19:13:21 +0530 Subject: [PATCH 4/6] refactor: improve styles to align the UI with rest of Phoenix --- .../Phoenix/html/login-dialog.html | 10 +- .../Phoenix/html/profile-panel.html | 30 +-- .../Phoenix/profile-menu.js | 2 +- src/styles/user-profile.less | 239 +++++++++++++++++- 4 files changed, 249 insertions(+), 32 deletions(-) diff --git a/src/extensionsIntegrated/Phoenix/html/login-dialog.html b/src/extensionsIntegrated/Phoenix/html/login-dialog.html index 3bbb928a6c..dbe692d262 100644 --- a/src/extensionsIntegrated/Phoenix/html/login-dialog.html +++ b/src/extensionsIntegrated/Phoenix/html/login-dialog.html @@ -3,13 +3,13 @@

{{welcomeTitle}}