Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions docs/02-List-of-Built-In-Libraries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@


# Built-in Libraries

This document lists the standard libraries that are pre-integrated into @codebase and available for use in your extensions and projects.

## Icon Libraries

### Font Awesome
Font Awesome provides scalable vector icons that can be customized with CSS.

**Import:**
Font Awesome is globally available

```js
// Add a Font Awesome icon to an element
element.innerHTML = '<i class="fas fa-save"></i>';
```


### Devicons
Developer-focused icons for programming languages and development tools.

**Import:**
Devicons are available globally.

```js
// Add a Devicon to an element
element.innerHTML = '<i class="devicon-javascript-plain"></i>';
```


### Octicons
GitHub's icon set.

**Import:**
Octicons are available globally.

```js
// Add an Octicon to an element
element.innerHTML = '<i class="octicon octicon-mark-github"></i>';
```

## Template Engines

### Mustache
Logic-less templates.

**Import:**
Mustache is available globally.

```js
const Mustache = brackets.getModule("thirdparty/mustache/mustache");
// example
const template = "Hello {{name}}!";
const data = { name: "World" };
const output = Mustache.render(template, data);
```


## Utility Libraries

### Lodash
A modern JavaScript utility library delivering modularity, performance & extras.

**Import:**
Lodash is available globally.

```js
const _ = brackets.getModule("thirdparty/lodash");
```


### Marked
A markdown parser and compiler.

**Import:**
Marked is available globally.

```js
const marked = brackets.getModule('thirdparty/marked.min');
const html = marked("# I am using __markdown__.");
```


## Phoenix-specific Libraries

These libraries are available through the Phoenix.libs namespace:

### LRU Cache
Least Recently Used (LRU) cache implementation.

**Import:**
```js
const { LRUCache } = Phoenix.libs;
// example
const cache = new LRUCache(100); // Create cache with max 100 items
cache.set('key', 'value');
const value = cache.get('key');
```


### Highlight.js
Syntax highlighting for code.

**Import:**

```js
const { hljs } = Phoenix.libs;
// see hilight js docs for usage
```


### iconv
Character encoding conversion.

**Import:**
```js
const { iconv } = Phoenix.libs;
// Example
const buffer = iconv.encode("Hello", 'utf8');
const text = iconv.decode(buffer, 'utf8');
```

### picomatch
Glob matching and pattern matching.

**Import:**
```js
const { picomatch } = Phoenix.libs;
// Example
const isMatch = picomatch('.js');
console.log(isMatch('file.js')); // true
console.log(isMatch('file.css')); // false
```



## Notes

- All libraries are pre-loaded when your extension starts up
- Some libraries are available globally through the `window` object
- Phoenix-specific libraries are accessed through `Phoenix.libs`
- Version information for each library can be found in the package dependencies
26 changes: 24 additions & 2 deletions src/extensions/default/HealthData/HealthDataManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,29 @@ define(function (require, exports, module) {
TEN_SECOND = 10 * ONE_SECOND,
ONE_MINUTE = 60000,
MAX_DAYS_TO_KEEP_COUNTS = 60,
// 'healthDataUsage' key is used in other places tho private to phoenix. search for other usage before rename
USAGE_COUNTS_KEY = "healthDataUsage";

/**
* A power user is someone who has used Phoenix at least 3 days or 8 hours in the last two weeks
* @returns {boolean}
*/
function isPowerUser() {
let usageData = PreferencesManager.getViewState(USAGE_COUNTS_KEY) || {},
dateKeys = Object.keys(usageData),
dateBefore14Days = new Date(),
totalUsageMinutes = 0,
totalUsageDays = 0;
dateBefore14Days.setUTCDate(dateBefore14Days.getUTCDate()-14);
for(let dateKey of dateKeys){
let date = new Date(dateKey);
if(date >= dateBefore14Days) {
totalUsageDays ++;
totalUsageMinutes = totalUsageMinutes + usageData[dateKey];
}
}
return totalUsageDays >= 3 || (totalUsageMinutes/60) >= 8;
}

let healthDataDisabled;

prefs.definePreference("healthDataTracking", "boolean", true, {
Expand Down Expand Up @@ -85,7 +105,9 @@ define(function (require, exports, module) {
}

AppInit.appReady(function () {
Metrics.init();
Metrics.init({
isPowerUserFn: isPowerUser
});
healthDataDisabled = !prefs.get("healthDataTracking");
Metrics.setDisabled(healthDataDisabled);
SendToAnalytics.sendPlatformMetrics();
Expand Down
150 changes: 80 additions & 70 deletions src/extensionsIntegrated/Phoenix/guided-tour.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ define(function (require, exports, module) {
Metrics = require("utils/Metrics"),
Dialogs = require("widgets/Dialogs"),
Mustache = require("thirdparty/mustache/mustache"),
PreferencesManager = require("preferences/PreferencesManager"),
SurveyTemplate = require("text!./html/survey-template.html"),
NOTIFICATION_BACKOFF = 10000,
GUIDED_TOUR_LOCAL_STORAGE_KEY = "guidedTourActions";
Expand All @@ -38,11 +37,10 @@ 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,
USAGE_COUNTS_KEY = "healthDataUsage"; // private to phoenix, set from health data extension
POWER_USER_SURVEY_INTERVAL_DAYS = 35;

const userAlreadyDidAction = PhStore.getItem(GUIDED_TOUR_LOCAL_STORAGE_KEY)
? JSON.parse(PhStore.getItem(GUIDED_TOUR_LOCAL_STORAGE_KEY)) : {
Expand Down Expand Up @@ -214,50 +212,28 @@ 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);
}

// a power user is someone who has used Phoenix at least 3 days or 8 hours in the last two weeks
function _isPowerUser() {
let usageData = PreferencesManager.getViewState(USAGE_COUNTS_KEY) || {},
dateKeys = Object.keys(usageData),
dateBefore14Days = new Date(),
totalUsageMinutes = 0,
totalUsageDays = 0;
dateBefore14Days.setUTCDate(dateBefore14Days.getUTCDate()-14);
for(let dateKey of dateKeys){
let date = new Date(dateKey);
if(date >= dateBefore14Days) {
totalUsageDays ++;
totalUsageMinutes = totalUsageMinutes + usageData[dateKey];
}
}
return totalUsageDays >= 3 || (totalUsageMinutes/60) >= 8;
}

function addSurveyIframe(surveyURL) {
const $surveyFrame = $('<iframe>', {
src: surveyURL,
Expand Down Expand Up @@ -295,38 +271,66 @@ define(function (require, exports, module) {
return resizeObserver;
}

function _showPowerUserSurvey(surveyURL, intervalOverride) {
if(_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 @@ -339,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
Loading
Loading