From 6186e343f804946c299f01ea6f80ce5a9994905e Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 7 Sep 2025 14:49:45 +0530 Subject: [PATCH 1/5] chore: pro upsell dialog on promotion complete --- src/brackets.config.dist.json | 3 +- src/brackets.config.staging.json | 3 +- src/config.json | 2 + src/nls/root/strings.js | 4 +- src/services/html/pro-upgrade.html | 2 +- src/services/html/promo-ended.html | 22 ++++++ src/services/login-service.js | 2 +- src/services/pro-dialogs.js | 29 +++++++- src/services/profile-menu.js | 1 - src/services/promotions.js | 15 +++- src/styles/brackets_core_ui_variables.less | 1 + src/styles/brackets_patterns_override.less | 3 +- src/styles/phoenix-pro.less | 79 ++++++++++++++++------ 13 files changed, 133 insertions(+), 33 deletions(-) create mode 100644 src/services/html/promo-ended.html diff --git a/src/brackets.config.dist.json b/src/brackets.config.dist.json index dab5e83374..0e8dfcc466 100644 --- a/src/brackets.config.dist.json +++ b/src/brackets.config.dist.json @@ -9,5 +9,6 @@ "buildtype" : "production", "bugsnagEnv" : "production", "app_notification_url" : "https://updates.phcode.io/appNotifications/prod/", - "app_update_url" : "https://updates.phcode.io/tauri/update-latest-stable-prod.json" + "app_update_url" : "https://updates.phcode.io/tauri/update-latest-stable-prod.json", + "promotions_url" : "https://promotions.phcode.dev/prod/" } diff --git a/src/brackets.config.staging.json b/src/brackets.config.staging.json index 94095484e8..6ba54f9810 100644 --- a/src/brackets.config.staging.json +++ b/src/brackets.config.staging.json @@ -9,5 +9,6 @@ "buildtype" : "staging", "bugsnagEnv" : "staging", "app_notification_url" : "https://updates.phcode.io/appNotifications/staging/", - "app_update_url" : "https://updates.phcode.io/tauri/update-latest-pre-release.json" + "app_update_url" : "https://updates.phcode.io/tauri/update-latest-pre-release.json", + "promotions_url" : "https://promotions.phcode.dev/dev/" } diff --git a/src/config.json b/src/config.json index c5b3efc417..3bba88b870 100644 --- a/src/config.json +++ b/src/config.json @@ -4,6 +4,8 @@ "app_name_about": "Phoenix Code", "about_icon": "styles/images/phoenix-icon.svg", "account_url": "https://account.phcode.dev/", + "promotions_url": "https://promotions.phcode.dev/dev/", + "purchase_url": "https://phcode.io/pricing", "how_to_use_url": "https://github.com/adobe/brackets/wiki/How-to-Use-Brackets", "docs_url": "https://docs.phcode.dev/", "support_url": "https://account.phcode.dev/?returnUrl=https%3A%2F%2Faccount.phcode.dev%2F%23support", diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 6ba78b8cc6..8f4027eb50 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -1675,5 +1675,7 @@ define({ "PROMO_CARD_3_MESSAGE": "Duplicate and delete elements with a single click.", "PROMO_CARD_4": "Editing Text In Preview", "PROMO_CARD_4_MESSAGE": "Edit headings, buttons, and copy directly in the preview.", - "PROMO_LEARN_MORE": "Learn More\u2026" + "PROMO_LEARN_MORE": "Learn More\u2026", + "PROMO_GET_APP_UPSELL_BUTTON": "Get {0}", + "PROMO_PRO_ENDED_TITLE": "Your {0} upgrade has ended" }); diff --git a/src/services/html/pro-upgrade.html b/src/services/html/pro-upgrade.html index 323aa91509..ce7b82ca2b 100644 --- a/src/services/html/pro-upgrade.html +++ b/src/services/html/pro-upgrade.html @@ -1,4 +1,4 @@ - diff --git a/src/services/pro-dialogs.js b/src/services/pro-dialogs.js index b263fefa98..95ab8fa001 100644 --- a/src/services/pro-dialogs.js +++ b/src/services/pro-dialogs.js @@ -44,33 +44,81 @@ define(function (require, exports, module) { function showProUpgradeDialog(trialDays) { const title = StringUtils.format(Strings.PROMO_UPGRADE_TITLE, proTitle); const message = StringUtils.format(Strings.PROMO_UPGRADE_MESSAGE, trialDays); - const $template = $(Mustache.render(proUpgradeHTML, {title, message, Strings})); + const $template = $(Mustache.render(proUpgradeHTML, { + title, message, Strings, + secondaryButton: Strings.PROMO_LEARN_MORE, + primaryButton: Strings.OK + })); Dialogs.showModalDialogUsingTemplate($template).done(function (id) { console.log("Dialog closed with id: " + id); - if(id === 'learn_more') { + if(id === 'secondaryButton') { // todo add metrics Phoenix.app.openURLInDefaultBrowser(brackets.config.purchase_url); } }); } - function showProEndedDialog(currentVersion) { - currentVersion = currentVersion || window.AppConfig.apiVersion; + function _showLocalProEndedDialog() { + const title = StringUtils.format(Strings.PROMO_PRO_ENDED_TITLE, proTitle); + const buttonGetPro = StringUtils.format(Strings.PROMO_GET_APP_UPSELL_BUTTON, proTitlePlain); + const $template = $(Mustache.render(proUpgradeHTML, { + title, Strings, + message: Strings.PROMO_ENDED_MESSAGE, + secondaryButton: Strings.CANCEL, + primaryButton: buttonGetPro + })); + Dialogs.showModalDialogUsingTemplate($template).done(function (id) { + console.log("Dialog closed with id: " + id); + if(id === 'ok') { + // todo add metrics + Phoenix.app.openURLInDefaultBrowser(brackets.config.purchase_url); + } + }); + } + + function _showRemoteProEndedDialog(currentVersion, promoHtmlURL, upsellPurchaseURL) { const buttonGetPro = StringUtils.format(Strings.PROMO_GET_APP_UPSELL_BUTTON, proTitlePlain); const title = StringUtils.format(Strings.PROMO_PRO_ENDED_TITLE, proTitle); const currentTheme = ThemeManager.getCurrentTheme(); const theme = currentTheme && currentTheme.dark ? "dark" : "light"; - const promoURL = `${brackets.config.promotions_url}app/upsell_after_trial.html?lang=${brackets.getLocale()}&theme=${theme}&version=${currentVersion}`; + const promoURL = `${promoHtmlURL}?lang=${ + brackets.getLocale()}&theme=${theme}&version=${currentVersion}`; const $template = $(Mustache.render(proEndedHTML, {Strings, title, buttonGetPro, promoURL})); Dialogs.showModalDialogUsingTemplate($template).done(function (id) { console.log("Dialog closed with id: " + id); if(id === 'get_pro') { - // todo add metrics - Phoenix.app.openURLInDefaultBrowser(brackets.config.purchase_url); + Phoenix.app.openURLInDefaultBrowser(upsellPurchaseURL || brackets.config.purchase_url); } }); } + async function showProEndedDialog() { + const currentVersion = window.AppConfig.apiVersion; + + if (!navigator.onLine) { + _showLocalProEndedDialog(); + return; + } + + try { + const configURL = `${brackets.config.promotions_url}app/config.json`; + const response = await fetch(configURL); + if (!response.ok) { + _showLocalProEndedDialog(); + return; + } + + const config = await response.json(); + if (config.upsell_after_trial_url) { + _showRemoteProEndedDialog(currentVersion, config.upsell_after_trial_url, config.upsell_purchase_url); + } else { + _showLocalProEndedDialog(); + } + } catch (error) { + _showLocalProEndedDialog(); + } + } + exports.showProUpgradeDialog = showProUpgradeDialog; exports.showProEndedDialog = showProEndedDialog; }); diff --git a/src/services/promotions.js b/src/services/promotions.js index 02a4f3cc6a..edd4be6733 100644 --- a/src/services/promotions.js +++ b/src/services/promotions.js @@ -208,7 +208,7 @@ define(function (require, exports, module) { // todo we should not show this to logged in pro subscribers, but at startup time, // we do not know if login is done yet. console.log("Existing trial expired, showing promo ended dialog"); - ProDialogs.showProEndedDialog(currentVersion); + ProDialogs.showProEndedDialog(); // Store that dialog was shown for this version await _setTrialData({ ...existingTrialData, From 9a9cd4b7181e4d8f1002d3950e887983a1ea9b7a Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 7 Sep 2025 16:55:37 +0530 Subject: [PATCH 3/5] chore: pro upsell dialog metrics --- src/services/pro-dialogs.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/services/pro-dialogs.js b/src/services/pro-dialogs.js index 95ab8fa001..6dbbf6cb38 100644 --- a/src/services/pro-dialogs.js +++ b/src/services/pro-dialogs.js @@ -38,6 +38,7 @@ define(function (require, exports, module) { Strings = require("strings"), StringUtils = require("utils/StringUtils"), ThemeManager = require("view/ThemeManager"), + Metrics = require("utils/Metrics"), proUpgradeHTML = require("text!./html/pro-upgrade.html"), proEndedHTML = require("text!./html/promo-ended.html"); @@ -51,9 +52,12 @@ define(function (require, exports, module) { })); Dialogs.showModalDialogUsingTemplate($template).done(function (id) { console.log("Dialog closed with id: " + id); + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgShow", "promo"); if(id === 'secondaryButton') { - // todo add metrics + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "promoLearn"); Phoenix.app.openURLInDefaultBrowser(brackets.config.purchase_url); + } else { + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "promoCancel"); } }); } @@ -69,9 +73,12 @@ define(function (require, exports, module) { })); Dialogs.showModalDialogUsingTemplate($template).done(function (id) { console.log("Dialog closed with id: " + id); + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgShow", "localUpgrade"); if(id === 'ok') { - // todo add metrics + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "localGetPro"); Phoenix.app.openURLInDefaultBrowser(brackets.config.purchase_url); + } else { + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "localCancel"); } }); } @@ -86,8 +93,12 @@ define(function (require, exports, module) { const $template = $(Mustache.render(proEndedHTML, {Strings, title, buttonGetPro, promoURL})); Dialogs.showModalDialogUsingTemplate($template).done(function (id) { console.log("Dialog closed with id: " + id); + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgShow", "remoteUpgrade"); if(id === 'get_pro') { + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "remoteGetPro"); Phoenix.app.openURLInDefaultBrowser(upsellPurchaseURL || brackets.config.purchase_url); + } else { + Metrics.countEvent(Metrics.EVENT_TYPE.PRO, "dlgAct", "remoteCancel"); } }); } From 6ec8f65cd1acea09c77a9a9c7ab356a59cd8f667 Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 7 Sep 2025 17:19:05 +0530 Subject: [PATCH 4/5] chore: dont show pro upgrade dialog if notifications visible on top --- src/services/promotions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/promotions.js b/src/services/promotions.js index edd4be6733..8f3d60de05 100644 --- a/src/services/promotions.js +++ b/src/services/promotions.js @@ -266,7 +266,9 @@ define(function (require, exports, module) { function _isAnyDialogsVisible() { const $modal = $(`.modal.instance`); - return $modal.length > 0 && $modal.is(':visible'); + const $notifications = $(`.notification-ui-tooltip`); + return ($modal.length > 0 && $modal.is(':visible')) || + ($notifications.length > 0 && $notifications.is(':visible')); } /** From 3fdf6a07d5cfe6e59753a6e16aa4365ba9ff21f9 Mon Sep 17 00:00:00 2001 From: abose Date: Sun, 7 Sep 2025 21:43:53 +0530 Subject: [PATCH 5/5] fix: full vertical empty space below health popup is mouse inactive --- src/styles/brackets.less | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 6ff3d92116..1f1ef12c8b 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -3222,6 +3222,7 @@ label input { flex-direction: column; z-index: 100; height: 100%; + pointer-events: none; } .notification-popup-container { @@ -3237,6 +3238,7 @@ label input { /* animated properties */ opacity: 0; transform: translateY(-50px); + pointer-events: all; } .notification-dialog-content strong {