Skip to content

Commit ff90273

Browse files
committed
chore: do not create projects in user documents dir if not needed
1 parent 0c7ba3e commit ff90273

File tree

9 files changed

+174
-132
lines changed

9 files changed

+174
-132
lines changed

src/assets/new-project/assets/js/code-editor.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ function initCodeEditor() {
214214
};
215215
document.getElementById("exploreBtn").onclick = function() {
216216
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "games");
217-
openProject(newProjectExtension.getExploreProjectPath());
217+
newProjectExtension.setupExploreProject()
218+
.then(()=>{
219+
openProject(newProjectExtension.getExploreProjectPath());
220+
}).catch(console.error);
218221
};
219222
document.getElementById("defaultProjectButton").onclick = function() {
220223
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "default-project");

src/extensionsIntegrated/Phoenix/default-projects.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ define(function (require, exports, module) {
4545
});
4646
});
4747
}
48-
async function _setupExploreProject() {
48+
async function setupExploreProject() {
4949
let exploreProjectPath = ProjectManager.getExploreProjectPath();
5050
let exists = await Phoenix.VFS.existsAsync(exploreProjectPath);
5151
if(!exists){
@@ -54,10 +54,18 @@ define(function (require, exports, module) {
5454
}
5555
}
5656

57+
exports.setupExploreProject = setupExploreProject;
58+
5759
exports.init = async function () {
5860
if(Phoenix.firstBoot){
5961
_setupStartupProject();
6062
}
61-
_setupExploreProject();
63+
if(!Phoenix.isNativeApp) {
64+
// in browsers, we do this as the user wont see the explore project in documents folder anyway and will
65+
// help in improved ux of fast project open. In desktop, we got complaint that users document dir is getting
66+
// polluted with unwanted projects, so we dont do this on desktop and only create this when user explicitly
67+
// clicks to open explore project.
68+
setupExploreProject();
69+
}
6270
};
6371
});

src/extensionsIntegrated/Phoenix/main.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ define(function (require, exports, module) {
3232
Strings = require("strings"),
3333
Dialogs = require("widgets/Dialogs"),
3434
NotificationUI = require("widgets/NotificationUI"),
35-
FileSystem = require("filesystem/FileSystem"),
36-
FileViewController = require("project/FileViewController"),
3735
DefaultDialogs = require("widgets/DefaultDialogs");
3836

3937
const PERSIST_STORAGE_DIALOG_DELAY_SECS = 60000;

src/extensionsIntegrated/Phoenix/new-project.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ define(function (require, exports, module) {
4141
createProjectDialogue = require("text!./html/create-project-dialogue.html"),
4242
replaceProjectDialogue = require("text!./html/replace-project-dialogue.html"),
4343
replaceKeepProjectDialogue = require("text!./html/replace-keep-project-dialogue.html"),
44+
defaultProjects = require("./default-projects"),
4445
guidedTour = require("./guided-tour");
4546

4647
EventDispatcher.makeEventDispatcher(exports);
@@ -386,6 +387,7 @@ define(function (require, exports, module) {
386387
exports.downloadAndOpenProject = downloadAndOpenProject;
387388
exports.showFolderSelect = showFolderSelect;
388389
exports.showErrorDialogue = showErrorDialogue;
390+
exports.setupExploreProject = defaultProjects.setupExploreProject;
389391
exports.alreadyExists = window.Phoenix.VFS.existsAsync;
390392
exports.Metrics = Metrics;
391393
exports.EVENT_NEW_PROJECT_DIALOGUE_CLOSED = "newProjectDlgClosed";

src/phoenix/init_vfs.js

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ function _setupVFS(fsLib, pathLib){
9595
});
9696
});
9797
},
98+
ensureExistsDirAsync: async function (path) {
99+
return new Promise((resolve, reject)=>{
100+
Phoenix.VFS.ensureExistsDir(path, (err) =>{
101+
if(err){
102+
reject(err);
103+
} else {
104+
resolve();
105+
}
106+
});
107+
});
108+
},
98109
/**
99110
* Converts a phoenix virtual serving url to absolute path in file system or null
100111
* http://localhost:8000/src/phoenix/vfs/fs/app/extensions/user/themesforbrackets/requirejs-config.json
@@ -127,17 +138,6 @@ function _setupVFS(fsLib, pathLib){
127138
}
128139
return window.fsServerUrl.slice(0, -1) + fullPath;
129140
},
130-
ensureExistsDirAsync: async function (path) {
131-
return new Promise((resolve, reject)=>{
132-
Phoenix.VFS.ensureExistsDir(path, (err) =>{
133-
if(err){
134-
reject(err);
135-
} else {
136-
resolve();
137-
}
138-
});
139-
});
140-
},
141141
exists: function (path, cb) {
142142
fs.stat(path, function (err, stats){
143143
if (stats && !err) {
@@ -163,41 +163,6 @@ function _setupVFS(fsLib, pathLib){
163163
return Phoenix.VFS;
164164
}
165165

166-
const _SAMPLE_HTML = `<!DOCTYPE html>
167-
<html>
168-
<head>
169-
<title>Phoenix Editor for the web</title>
170-
</head>
171-
172-
<body>
173-
<h1>Welcome to Phoenix</h1>
174-
<p> Modern, Open-source, IDE For The Web.</p>
175-
</body>
176-
</html>`;
177-
178-
// always resolves even if error
179-
function _tryCreateDefaultProject() {
180-
// Create phoenix app dirs
181-
// Create Phoenix default project if it doesnt exist
182-
return new Promise((resolve)=>{
183-
let projectDir = Phoenix.VFS.getDefaultProjectDir();
184-
Phoenix.VFS.exists(projectDir, (exists)=>{
185-
if(!exists){
186-
Phoenix.VFS.ensureExistsDir(projectDir, (err)=>{
187-
if(err){
188-
logger.reportError(err, "Error creating default project");
189-
}
190-
let indexFile = Phoenix.VFS.path.normalize(`${projectDir}/index.html`);
191-
Phoenix.VFS.fs.writeFile(indexFile, _SAMPLE_HTML, 'utf8', ()=>{});
192-
resolve();
193-
});
194-
return;
195-
}
196-
resolve();
197-
});
198-
});
199-
}
200-
201166
async function setupAppSupportAndExtensionsDir() {
202167
if(Phoenix.isNativeApp) {
203168
appSupportDIR = fs.getTauriVirtualPath(window._tauriBootVars.appLocalDir);
@@ -240,7 +205,6 @@ async function setupDocumentsDir() {
240205
userProjectsDir = documentsDIR;
241206
}
242207
await Phoenix.VFS.ensureExistsDirAsync(documentsDIR);
243-
await _tryCreateDefaultProject();
244208
console.log("Documents dir setup done");
245209
}
246210

src/project/ProjectManager.js

Lines changed: 131 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,11 @@ define(function (require, exports, module) {
739739
* @return {!string} fullPath reference
740740
*/
741741
function getWelcomeProjectPath() {
742-
return ProjectModel._getWelcomeProjectPath(Urls.GETTING_STARTED, Phoenix.VFS.getDefaultProjectDir());
742+
return ProjectModel._ensureTrailingSlash(Phoenix.VFS.getDefaultProjectDir());
743+
}
744+
745+
function _getPlaceholderProjectPath() {
746+
return "/no_project_loaded";
743747
}
744748

745749
function getExploreProjectPath() {
@@ -1115,52 +1119,132 @@ define(function (require, exports, module) {
11151119
});
11161120
}
11171121

1118-
function _loadProjectInternal(rootPath) {
1122+
function _loadExistingProject(rootEntry) {
1123+
const rootPath = rootEntry.fullPath;
11191124
const result = new $.Deferred();
1120-
const rootEntry = FileSystem.getDirectoryForPath(rootPath);
1121-
rootEntry.exists(function (err, exists) {
1122-
if (exists) {
1123-
var projectRootChanged = (!model.projectRoot || !rootEntry) ||
1124-
model.projectRoot.fullPath !== rootEntry.fullPath;
1125+
var projectRootChanged = (!model.projectRoot || !rootEntry) ||
1126+
model.projectRoot.fullPath !== rootEntry.fullPath;
11251127

1126-
// Success!
1127-
var perfTimerName = PerfUtils.markStart("Load Project: " + rootPath);
1128+
// Success!
1129+
var perfTimerName = PerfUtils.markStart("Load Project: " + rootPath);
11281130

1129-
_projectWarnedForTooManyFiles = false;
1131+
_projectWarnedForTooManyFiles = false;
11301132

1131-
_setProjectRoot(rootEntry).always(function () {
1132-
model.setBaseUrl(PreferencesManager.getViewState("project.baseUrl", PreferencesManager.STATE_PROJECT_CONTEXT) || "");
1133+
_setProjectRoot(rootEntry).always(function () {
1134+
model.setBaseUrl(PreferencesManager.getViewState("project.baseUrl", PreferencesManager.STATE_PROJECT_CONTEXT) || "");
11331135

1134-
if (projectRootChanged) {
1135-
_reloadProjectPreferencesScope();
1136-
PreferencesManager._setCurrentFile(rootPath);
1137-
}
1138-
_watchProjectRoot(rootPath);
1136+
if (projectRootChanged) {
1137+
_reloadProjectPreferencesScope();
1138+
PreferencesManager._setCurrentFile(rootPath);
1139+
}
1140+
_watchProjectRoot(rootPath);
11391141

1140-
// If this is the most current welcome project, record it. In future launches, we want
1141-
// to substitute the latest welcome project from the current build instead of using an
1142-
// outdated one (when loading recent projects or the last opened project).
1143-
if (rootPath === getWelcomeProjectPath()) {
1144-
addWelcomeProjectPath(rootPath);
1145-
}
1142+
// If this is the most current welcome project, record it. In future launches, we want
1143+
// to substitute the latest welcome project from the current build instead of using an
1144+
// outdated one (when loading recent projects or the last opened project).
1145+
if (rootPath === getWelcomeProjectPath()) {
1146+
addWelcomeProjectPath(rootPath);
1147+
}
11461148

1147-
if (projectRootChanged) {
1148-
// Allow asynchronous event handlers to finish before resolving result by collecting promises from them
1149-
exports.trigger(EVENT_PROJECT_OPEN, model.projectRoot);
1150-
result.resolve();
1151-
exports.trigger(EVENT_AFTER_PROJECT_OPEN, model.projectRoot);
1152-
} else {
1153-
exports.trigger(EVENT_PROJECT_REFRESH, model.projectRoot);
1154-
result.resolve();
1149+
if (projectRootChanged) {
1150+
// Allow asynchronous event handlers to finish before resolving result by collecting promises from them
1151+
exports.trigger(EVENT_PROJECT_OPEN, model.projectRoot);
1152+
result.resolve();
1153+
exports.trigger(EVENT_AFTER_PROJECT_OPEN, model.projectRoot);
1154+
} else {
1155+
exports.trigger(EVENT_PROJECT_REFRESH, model.projectRoot);
1156+
result.resolve();
1157+
}
1158+
let projectLoadTime = PerfUtils.addMeasurement(perfTimerName);
1159+
Metrics.valueEvent(Metrics.EVENT_TYPE.PERFORMANCE, "projectLoad",
1160+
"timeMs", Number(projectLoadTime));
1161+
});
1162+
return result.promise();
1163+
}
1164+
1165+
const _SAMPLE_HTML = `<!DOCTYPE html>
1166+
<html>
1167+
<head>
1168+
<title>Phoenix Editor for the web</title>
1169+
</head>
1170+
1171+
<body>
1172+
<h1>Welcome to Phoenix</h1>
1173+
<p> Modern, Open-source, IDE For The Web.</p>
1174+
</body>
1175+
</html>`;
1176+
1177+
function _createDefaultProject() {
1178+
// Create phoenix app dirs
1179+
// Create Phoenix default project if it doesnt exist
1180+
return new Promise((resolve, reject)=>{
1181+
let projectDir = getWelcomeProjectPath();
1182+
Phoenix.VFS.exists(projectDir, (exists)=>{
1183+
if(exists) {
1184+
resolve();
1185+
return;
1186+
}
1187+
Phoenix.VFS.ensureExistsDir(projectDir, (err)=>{
1188+
if(err){
1189+
window.logger.reportError(err, "Error creating default project");
1190+
reject(err);
1191+
return;
11551192
}
1156-
let projectLoadTime = PerfUtils.addMeasurement(perfTimerName);
1157-
Metrics.valueEvent(Metrics.EVENT_TYPE.PERFORMANCE, "projectLoad",
1158-
"timeMs", Number(projectLoadTime));
1193+
let indexFile = Phoenix.VFS.path.normalize(`${projectDir}/index.html`);
1194+
Phoenix.VFS.fs.writeFile(indexFile, _SAMPLE_HTML, 'utf8', ()=>{});
1195+
resolve();
11591196
});
1197+
});
1198+
});
1199+
}
1200+
1201+
function _loadWelcomeProject(welcomeProjectPath) {
1202+
const result = new $.Deferred();
1203+
const rootEntry = FileSystem.getDirectoryForPath(welcomeProjectPath);
1204+
function _loadRootEntry() {
1205+
_loadExistingProject(rootEntry)
1206+
.done(result.resolve)
1207+
.fail(result.reject);
1208+
}
1209+
rootEntry.exists(function (err, exists) {
1210+
if (exists) {
1211+
_loadRootEntry();
1212+
} else {
1213+
// create the welcome project
1214+
_createDefaultProject()
1215+
.then(_loadRootEntry)
1216+
.catch(()=>{
1217+
// default project could not be created. As a last ditch effort to continue boot, we will
1218+
// use a vfs path `/no_project_loaded` to continue boot.
1219+
Phoenix.VFS.ensureExistsDir(_getPlaceholderProjectPath(), (placeHolderErr)=>{
1220+
if(placeHolderErr){
1221+
window.logger.reportError(placeHolderErr, "Error creating /no_project_loaded");
1222+
alert("Unrecoverable error, startup project could not be created.");
1223+
return;
1224+
}
1225+
const placeholderProject = FileSystem.getDirectoryForPath(_getPlaceholderProjectPath());
1226+
_loadExistingProject(placeholderProject)
1227+
.done(result.resolve)
1228+
.fail(result.reject);
1229+
});
1230+
});
1231+
}
1232+
});
1233+
return result.promise();
1234+
}
1235+
1236+
function _loadProjectInternal(rootPath) {
1237+
const result = new $.Deferred();
1238+
const rootEntry = FileSystem.getDirectoryForPath(rootPath);
1239+
rootEntry.exists(function (err, exists) {
1240+
if (exists) {
1241+
_loadExistingProject(rootEntry)
1242+
.done(result.resolve)
1243+
.fail(result.reject);
11601244
} else {
11611245
console.error("error loading project");
11621246
exports.trigger(EVENT_PROJECT_OPEN_FAILED, rootPath);
1163-
_showErrorDialog(ERR_TYPE_LOADING_PROJECT_NATIVE, true, err || FileSystemError.NOT_FOUND, rootPath)
1247+
_showErrorDialog(ERR_TYPE_LOADING_PROJECT_NATIVE, true, FileSystemError.NOT_FOUND, rootPath)
11641248
.done(function () {
11651249
// Reset _projectRoot to null so that the following _loadProject call won't
11661250
// run the 'beforeProjectClose' event a second time on the original project,
@@ -1182,7 +1266,6 @@ define(function (require, exports, module) {
11821266
});
11831267
}
11841268
});
1185-
11861269
return result.promise();
11871270
}
11881271

@@ -1223,8 +1306,10 @@ define(function (require, exports, module) {
12231306
PreferencesManager._reloadUserPrefs(model.projectRoot);
12241307
exports.trigger(EVENT_PROJECT_CLOSE, model.projectRoot);
12251308
}
1226-
1227-
// Point at a real folder structure on local disk
1309+
if(rootPath === getWelcomeProjectPath()) {
1310+
// welcome project path is always guaranteed to be present!
1311+
return _loadWelcomeProject(rootPath);
1312+
}
12281313
return _loadProjectInternal(rootPath);
12291314
}
12301315

@@ -1945,17 +2030,22 @@ define(function (require, exports, module) {
19452030
function _flagProjectNotExitedSafely(projectRootPath) {
19462031
// we store this in local storage as these checks happen in app exit/startup flows where
19472032
// phstore is not reliable. It's ok to lose this data.
1948-
localStorage.setItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath, "true");
2033+
PhStore.setItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath, true);
2034+
PhStore.persistDBForReboot(); // promise is ignored here.
19492035
}
19502036
function _flagProjectExitedSafely(projectRootPath) {
1951-
localStorage.removeItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath);
2037+
PhStore.removeItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath);
19522038
}
19532039
function _isProjectSafeToStartup(projectRootPath) {
19542040
// In some cases, For eg: user tries to open a whole drive with phcode (or any project that makes phcode crash),
19552041
// phoenix may get stuck and never open again with the bad project as we try to open the same bad project
19562042
// on restart. So we keep a safe exit flag with each project. We only start the project on boot if it has
19572043
// been marked safe at previous exit or project switch. So on unsafe exit, the default project will be opened.
1958-
const unsafeExit = (localStorage.getItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath) === "true");
2044+
const unsafeExit = (PhStore.getItem(UNSAFE_PROJECT_EXIT_PREFIX+projectRootPath) === true);
2045+
if(unsafeExit){
2046+
console.error(`Project ${projectRootPath} marked as not safe to startup during`+
2047+
`previous exit. Starting default project`);
2048+
}
19592049
return !unsafeExit;
19602050
}
19612051

0 commit comments

Comments
 (0)