Skip to content

Commit

Permalink
chore: chnage survey to notification instead of popup to not take use…
Browse files Browse the repository at this point in the history
…r focus
  • Loading branch information
abose committed Jan 22, 2025
1 parent 43270aa commit 229f5d2
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 52 deletions.
128 changes: 79 additions & 49 deletions src/extensionsIntegrated/Phoenix/guided-tour.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ define(function (require, exports, module) {
// All popup notifications will show immediately on boot, we don't want to interrupt user amidst his work
// by showing it at a later point in time.
const GENERAL_SURVEY_TIME = 1200000, // 20 min
POWER_USER_SURVEY_TIME = 10000, // 10 seconds to allow the survey to preload, but not
SURVEY_PRELOAD_DELAY = 10000, // 10 seconds to allow the survey to preload, but not
// enough time to break user workflow
ONE_MONTH_IN_DAYS = 30,
POWER_USER_SURVEY_INTERVAL_DAYS = 35;
Expand Down Expand Up @@ -212,27 +212,23 @@ define(function (require, exports, module) {
}
}

function _showGeneralSurvey(surveyURL, delayOverride) {
function _showFirstUseSurvey(surveyURL, delayOverride, title, useDialog) {
let surveyVersion = 6; // increment this if you want to show this again
if(userAlreadyDidAction.generalSurveyShownVersion === surveyVersion) {
return;
}
const $surveyFrame = addSurveyIframe(surveyURL);
let $surveyFrame;
if(useDialog){
$surveyFrame = addSurveyIframe(surveyURL);
}
setTimeout(()=>{
const templateVars = {
Strings: Strings
};
let positionObserver;
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "generalShown", 1);
Dialogs.showModalDialogUsingTemplate(Mustache.render(SurveyTemplate, templateVars)).done(()=>{
positionObserver && positionObserver.disconnect();
$surveyFrame.remove();
});
setTimeout(()=>{
const $surveyFrameContainer = $('#surveyFrameContainer');
repositionIframe($surveyFrame, $surveyFrameContainer);
positionObserver = observerPositionChanges($surveyFrame, $surveyFrameContainer);
}, 200);
if(useDialog){
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "firstDialog", 1);
_showDialogSurvey($surveyFrame);
} else {
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "firstNotification", 1);
_showSurveyNotification(surveyURL, title);
}
userAlreadyDidAction.generalSurveyShownVersion = surveyVersion;
PhStore.setItem(GUIDED_TOUR_LOCAL_STORAGE_KEY, JSON.stringify(userAlreadyDidAction));
}, delayOverride || GENERAL_SURVEY_TIME);
Expand Down Expand Up @@ -275,38 +271,66 @@ define(function (require, exports, module) {
return resizeObserver;
}

function _showPowerUserSurvey(surveyURL, intervalOverride) {
if(Metrics.isPowerUser()) {
const intervalDays = intervalOverride || POWER_USER_SURVEY_INTERVAL_DAYS;
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "power", "user", 1);
let lastShownDate = userAlreadyDidAction.lastShownPowerSurveyDate;
let nextShowDate = new Date(lastShownDate);
nextShowDate.setUTCDate(nextShowDate.getUTCDate() + intervalDays);
let currentDate = new Date();
if(currentDate < nextShowDate){
return;
}
function _showDialogSurvey($surveyFrame) {
const templateVars = {
Strings: Strings
};
let positionObserver;
Dialogs.showModalDialogUsingTemplate(Mustache.render(SurveyTemplate, templateVars))
.done(()=>{
positionObserver && positionObserver.disconnect();
$surveyFrame.remove();
});
const $surveyFrameContainer = $('#surveyFrameContainer');
setTimeout(()=>{
repositionIframe($surveyFrame, $surveyFrameContainer);
positionObserver = observerPositionChanges($surveyFrame, $surveyFrameContainer);
}, 200);
}

function _showSurveyNotification(surveyUrl, title) {
NotificationUI.createToastFromTemplate(
title || Strings.SURVEY_TITLE_VOTE_FOR_FEATURES_YOU_WANT,
`<div class="survey-notification-popup">
<iframe src="${surveyUrl}" style="width: 500px; height: 645px;" frameborder="0"></iframe></div>`, {
toastStyle: `${NotificationUI.NOTIFICATION_STYLES_CSS_CLASS.INFO} survey-notification-big forced-hidden`,
dismissOnClick: false
});
setTimeout(()=>{
$('.survey-notification-big').removeClass('forced-hidden');
}, SURVEY_PRELOAD_DELAY);
}

function _showRepeatUserSurvey(surveyURL, intervalOverride, title, useDialog) {
let nextPowerSurveyShowDate = userAlreadyDidAction.nextPowerSurveyShowDate;
if(!nextPowerSurveyShowDate){
// first boot, we schedule the power user survey to happen in two weeks
let nextShowDate = new Date();
nextShowDate.setUTCDate(nextShowDate.getUTCDate() + 14); // the first time repeat survey always shows up
// always after 2 weeks.
userAlreadyDidAction.nextPowerSurveyShowDate = nextShowDate.getTime();
PhStore.setItem(GUIDED_TOUR_LOCAL_STORAGE_KEY, JSON.stringify(userAlreadyDidAction));
return;
}
const intervalDays = intervalOverride || POWER_USER_SURVEY_INTERVAL_DAYS;
let nextShowDate = new Date(nextPowerSurveyShowDate);
let currentDate = new Date();
if(currentDate < nextShowDate){
return;
}
if(useDialog){
const $surveyFrame = addSurveyIframe(surveyURL);
setTimeout(()=>{
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "powerShown", 1);
const templateVars = {
Strings: Strings
};
let positionObserver;
Dialogs.showModalDialogUsingTemplate(Mustache.render(SurveyTemplate, templateVars))
.done(()=>{
positionObserver && positionObserver.disconnect();
$surveyFrame.remove();
});
const $surveyFrameContainer = $('#surveyFrameContainer');
setTimeout(()=>{
repositionIframe($surveyFrame, $surveyFrameContainer);
positionObserver = observerPositionChanges($surveyFrame, $surveyFrameContainer);
}, 200);
userAlreadyDidAction.lastShownPowerSurveyDate = Date.now();
PhStore.setItem(GUIDED_TOUR_LOCAL_STORAGE_KEY, JSON.stringify(userAlreadyDidAction));
}, POWER_USER_SURVEY_TIME);
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "powerDialog", 1);
_showDialogSurvey($surveyFrame);
}, SURVEY_PRELOAD_DELAY);
} else {
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "powerNotification", 1);
_showSurveyNotification(surveyURL, title);
}
nextShowDate.setUTCDate(nextShowDate.getUTCDate() + intervalDays);
userAlreadyDidAction.nextPowerSurveyShowDate = nextShowDate.getTime();
PhStore.setItem(GUIDED_TOUR_LOCAL_STORAGE_KEY, JSON.stringify(userAlreadyDidAction));
}

async function _showSurveys() {
Expand All @@ -319,14 +343,20 @@ define(function (require, exports, module) {
if(!Phoenix.isNativeApp && surveyJSON.browser) {
surveyJSON = {
newUser: surveyJSON.browser.newUser || surveyJSON.newUser,
newUserTitle: surveyJSON.browser.newUserTitle || surveyJSON.newUserTitle,
newUserShowDelayMS: surveyJSON.browser.newUserShowDelayMS || surveyJSON.newUserShowDelayMS,
newUserUseDialog: surveyJSON.browser.newUserUseDialog || surveyJSON.newUserUseDialog,
powerUser: surveyJSON.browser.powerUser || surveyJSON.powerUser,
powerUserTitle: surveyJSON.browser.powerUserTitle || surveyJSON.powerUserTitle,
powerUserShowIntervalDays: surveyJSON.browser.powerUserShowIntervalDays
|| surveyJSON.powerUserShowIntervalDays
|| surveyJSON.powerUserShowIntervalDays,
powerUserUseDialog: surveyJSON.browser.powerUserUseDialog || surveyJSON.powerUserUseDialog
};
}
surveyJSON.newUser && _showGeneralSurvey(surveyJSON.newUser, surveyJSON.newUserShowDelayMS);
surveyJSON.powerUser && _showPowerUserSurvey(surveyJSON.powerUser, surveyJSON.powerUserShowIntervalDays);
surveyJSON.newUser && _showFirstUseSurvey(surveyJSON.newUser, surveyJSON.newUserShowDelayMS,
surveyJSON.newUserTitle, surveyJSON.newUserUseDialog);
surveyJSON.powerUser && _showRepeatUserSurvey(surveyJSON.powerUser, surveyJSON.powerUserShowIntervalDays,
surveyJSON.powerUserTitle, surveyJSON.powerUserUseDialog);
} catch (e) {
console.error("Error fetching survey link", surveyLinksURL, e);
Metrics.countEvent(Metrics.EVENT_TYPE.USER, "survey", "fetchError", 1);
Expand Down
5 changes: 4 additions & 1 deletion src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1528,5 +1528,8 @@ define({
"ERROR_PUSHING_OPERATION": "Pushing operation failed",
"ERROR_NO_REMOTE_SELECTED": "No remote has been selected for {0}!",
"ERROR_BRANCH_LIST": "Getting branch list failed",
"ERROR_FETCH_REMOTE": "Fetching remote information failed"
"ERROR_FETCH_REMOTE": "Fetching remote information failed",

// surveys
"SURVEY_TITLE_VOTE_FOR_FEATURES_YOU_WANT": "Vote for the features you want to see next!"
});
10 changes: 9 additions & 1 deletion src/styles/brackets.less
Original file line number Diff line number Diff line change
Expand Up @@ -3267,7 +3267,7 @@ label input {
}

.notification-popup-close-button {
font-size: 16px;
font-size: 24px;
font-weight: 900;
position: absolute;
top: 0;
Expand Down Expand Up @@ -3557,3 +3557,11 @@ label input {
flex-direction: column;
justify-content: space-between
}

.notification-popup-container.survey-notification-big {
margin-left: -274px;
background-color: #f3f5f9; // match with survey planet
.notification-popup-close-button:hover {
color: @bc-primary-btn-border-focused-glow;
}
}
3 changes: 2 additions & 1 deletion src/widgets/NotificationUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,11 @@ define(function (require, exports, module) {
* The message can either be a string or a jQuery object representing a DOM node that is *not* in the current DOM.
*
* Creating a toast notification popup
*
* ```js
* // note that you can even provide an HTML Element node with
* // custom event handlers directly here instead of HTML text.
* let notification1 = NotificationUI.createToastFromTemplate( "Title here",
* ```js
* "<div>Click me to locate the file in file tree</div>", {
* dismissOnClick: false,
* autoCloseTimeS: 300 // auto close the popup after 5 minutes
Expand Down

0 comments on commit 229f5d2

Please sign in to comment.