Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 4729958

Browse files
committed
Check for extensions updates
Updated design Check registry at most once a day Remove updated and uninstalled extensions from update list Use new icon sprite Calculate when an actual update should be launched Calculate updateCompatible for extensions toggle icon even when registry are downloaded through extension manager checkForUpdate is now called directly on the startup as it was before combine the functions download only if there're no updates available delete icon that is no longer user This sorts installed extensions by updateAvailable (#6634) align = in exports add an empty line use localeCompare instead of ifs use Array instead of array for preferences do not convert new preferences move event listener to the bottom of the file move function to the bottom to make JSLint happy count only compatible updates for notification icon add comments fix tests that were failing after changes to the model.notifyCount fix issue when update button is disabled but newer compatible version (not latest) exists add tests for ExtensionManager.getAvailableUpdates() method add tests for ExtensionManager.cleanAvailableUpdates() method move new tests
1 parent c439863 commit 4729958

9 files changed

Lines changed: 232 additions & 72 deletions

src/brackets.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,13 +296,9 @@ define(function (require, exports, module) {
296296

297297
// Check for updates
298298
if (!params.get("skipUpdateCheck") && !brackets.inBrowser) {
299-
// check once a day, plus 2 minutes,
300-
// as the check will skip if the last check was not -24h ago
301-
window.setInterval(UpdateNotification.checkForUpdate, 86520000);
302-
303-
// Check for updates on App Ready
304299
AppInit.appReady(function () {
305-
UpdateNotification.checkForUpdate();
300+
// launches periodic checks for updates cca every 24 hours
301+
UpdateNotification.launchAutomaticUpdate();
306302
});
307303
}
308304
}

src/extensibility/ExtensionManager.js

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
define(function (require, exports, module) {
4040
"use strict";
4141

42-
var FileUtils = require("file/FileUtils"),
42+
var _ = require("thirdparty/lodash"),
43+
FileUtils = require("file/FileUtils"),
4344
Package = require("extensibility/Package"),
4445
Async = require("utils/Async"),
4546
ExtensionLoader = require("utils/ExtensionLoader"),
@@ -105,13 +106,28 @@ define(function (require, exports, module) {
105106
}
106107

107108
entry.installInfo.owner = entry.registryInfo.owner;
108-
if (entry.installInfo.metadata && entry.installInfo.metadata.version && semver.lt(entry.installInfo.metadata.version, entry.registryInfo.metadata.version)) {
109+
110+
// Assume false
111+
entry.installInfo.updateAvailable = false;
112+
entry.registryInfo.updateAvailable = false;
113+
entry.installInfo.updateCompatible = false;
114+
entry.registryInfo.updateCompatible = false;
115+
116+
var currentVersion = entry.installInfo.metadata ? entry.installInfo.metadata.version : null;
117+
if (currentVersion && semver.lt(currentVersion, entry.registryInfo.metadata.version)) {
109118
// Note: available update may still be incompatible; we check for this when rendering the Update button in ExtensionManagerView._renderItem()
110-
entry.registryInfo.updateAvailable = true;
111-
entry.installInfo.updateAvailable = true;
112-
} else {
113-
entry.installInfo.updateAvailable = false;
114-
entry.registryInfo.updateAvailable = false;
119+
entry.registryInfo.updateAvailable = true;
120+
entry.installInfo.updateAvailable = true;
121+
// Calculate updateCompatible to check if there's an update for current version of Brackets
122+
var lastCompatibleVersionInfo = _.findLast(entry.registryInfo.versions, function (versionInfo) {
123+
return semver.satisfies(brackets.metadata.apiVersion, versionInfo.brackets);
124+
});
125+
if (lastCompatibleVersionInfo && lastCompatibleVersionInfo.version && semver.lt(currentVersion, lastCompatibleVersionInfo.version)) {
126+
entry.installInfo.updateCompatible = true;
127+
entry.registryInfo.updateCompatible = true;
128+
entry.installInfo.lastCompatibleVersion = lastCompatibleVersionInfo.version;
129+
entry.registryInfo.lastCompatibleVersion = lastCompatibleVersionInfo.version;
130+
}
115131
}
116132

117133
$(exports).triggerHandler("registryUpdate", [id]);
@@ -160,6 +176,7 @@ define(function (require, exports, module) {
160176
extensions[id].registryInfo = data[id];
161177
synchronizeEntry(id);
162178
});
179+
$(exports).triggerHandler("registryDownload");
163180
result.resolve();
164181
})
165182
.fail(function () {
@@ -506,39 +523,92 @@ define(function (require, exports, module) {
506523
);
507524
}
508525

526+
/**
527+
* Gets an array of extensions that are currently installed and can be updated to a new version
528+
* @return {Array} array of objects with properies id,installVersion,registryVersion
529+
* where id = extensionId
530+
* installVersion = currently installed version of extension
531+
* registryVersion = latest version compatible with current Brackets
532+
*/
533+
function getAvailableUpdates() {
534+
var result = [];
535+
Object.keys(extensions).forEach(function (extensionId) {
536+
var extensionInfo = extensions[extensionId];
537+
// skip extensions that are not installed or are not in the registry
538+
if (!extensionInfo.installInfo || !extensionInfo.registryInfo) {
539+
return;
540+
}
541+
if (extensionInfo.registryInfo.updateCompatible) {
542+
result.push({
543+
id: extensionId,
544+
installVersion: extensionInfo.installInfo.metadata.version,
545+
registryVersion: extensionInfo.registryInfo.lastCompatibleVersion
546+
});
547+
}
548+
});
549+
return result;
550+
}
551+
552+
/**
553+
* Takes array returned from getAvailableUpdates() as an input and removes those entries
554+
* that are no longer current - when currently installed version of an extension
555+
* is equal or newer than registryVersion returned by getAvailableUpdates().
556+
* This function is designed to work without the necessity to download extension registry
557+
* @param {Array} previous output of getAvailableUpdates()
558+
* @param {Array} filtered input as function description
559+
*/
560+
function cleanAvailableUpdates(updates) {
561+
return updates.reduce(function (arr, updateInfo) {
562+
var extDefinition = extensions[updateInfo.id];
563+
if (!extDefinition || !extDefinition.installInfo) {
564+
// extension has been uninstalled in the meantime
565+
return arr;
566+
}
567+
568+
var installedVersion = extDefinition.installInfo.metadata.version;
569+
if (semver.lt(installedVersion, updateInfo.registryVersion)) {
570+
arr.push(updateInfo);
571+
}
572+
573+
return arr;
574+
}, []);
575+
}
576+
509577
// Listen to extension load and loadFailed events
510578
$(ExtensionLoader)
511579
.on("load", _handleExtensionLoad)
512580
.on("loadFailed", _handleExtensionLoad);
513581

514582
// Public exports
515-
exports.downloadRegistry = downloadRegistry;
516-
exports.getCompatibilityInfo = getCompatibilityInfo;
517-
exports.getExtensionURL = getExtensionURL;
518-
exports.remove = remove;
519-
exports.update = update;
520-
exports.extensions = extensions;
521-
exports.cleanupUpdates = cleanupUpdates;
522-
exports.markForRemoval = markForRemoval;
523-
exports.isMarkedForRemoval = isMarkedForRemoval;
524-
exports.unmarkAllForRemoval = unmarkAllForRemoval;
525-
exports.hasExtensionsToRemove = hasExtensionsToRemove;
526-
exports.updateFromDownload = updateFromDownload;
527-
exports.removeUpdate = removeUpdate;
528-
exports.isMarkedForUpdate = isMarkedForUpdate;
529-
exports.hasExtensionsToUpdate = hasExtensionsToUpdate;
530-
exports.removeMarkedExtensions = removeMarkedExtensions;
531-
exports.updateExtensions = updateExtensions;
583+
exports.downloadRegistry = downloadRegistry;
584+
exports.getCompatibilityInfo = getCompatibilityInfo;
585+
exports.getExtensionURL = getExtensionURL;
586+
exports.remove = remove;
587+
exports.update = update;
588+
exports.extensions = extensions;
589+
exports.cleanupUpdates = cleanupUpdates;
590+
exports.markForRemoval = markForRemoval;
591+
exports.isMarkedForRemoval = isMarkedForRemoval;
592+
exports.unmarkAllForRemoval = unmarkAllForRemoval;
593+
exports.hasExtensionsToRemove = hasExtensionsToRemove;
594+
exports.updateFromDownload = updateFromDownload;
595+
exports.removeUpdate = removeUpdate;
596+
exports.isMarkedForUpdate = isMarkedForUpdate;
597+
exports.hasExtensionsToUpdate = hasExtensionsToUpdate;
598+
exports.removeMarkedExtensions = removeMarkedExtensions;
599+
exports.updateExtensions = updateExtensions;
600+
exports.getAvailableUpdates = getAvailableUpdates;
601+
exports.cleanAvailableUpdates = cleanAvailableUpdates;
532602

533-
exports.ENABLED = ENABLED;
534-
exports.START_FAILED = START_FAILED;
603+
exports.ENABLED = ENABLED;
604+
exports.START_FAILED = START_FAILED;
535605

536-
exports.LOCATION_DEFAULT = LOCATION_DEFAULT;
537-
exports.LOCATION_DEV = LOCATION_DEV;
538-
exports.LOCATION_USER = LOCATION_USER;
539-
exports.LOCATION_UNKNOWN = LOCATION_UNKNOWN;
606+
exports.LOCATION_DEFAULT = LOCATION_DEFAULT;
607+
exports.LOCATION_DEV = LOCATION_DEV;
608+
exports.LOCATION_USER = LOCATION_USER;
609+
exports.LOCATION_UNKNOWN = LOCATION_UNKNOWN;
540610

541611
// For unit testing only
542-
exports._reset = _reset;
543-
exports._setExtensions = _setExtensions;
544-
});
612+
exports._reset = _reset;
613+
exports._setExtensions = _setExtensions;
614+
});

src/extensibility/ExtensionManagerView.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ define(function (require, exports, module) {
219219

220220
var isInstalledInUserFolder = (entry.installInfo && entry.installInfo.locationType === ExtensionManager.LOCATION_USER);
221221
context.allowRemove = isInstalledInUserFolder;
222-
context.allowUpdate = context.showUpdateButton && context.isCompatible && context.isCompatibleLatest && isInstalledInUserFolder;
222+
context.allowUpdate = context.showUpdateButton && context.isCompatible && context.updateCompatible && isInstalledInUserFolder;
223223
if (!context.allowUpdate) {
224224
context.updateNotAllowedReason = isInstalledInUserFolder ? Strings.CANT_UPDATE : Strings.CANT_UPDATE_DEV;
225225
}
@@ -310,4 +310,4 @@ define(function (require, exports, module) {
310310
};
311311

312312
exports.ExtensionManagerView = ExtensionManagerView;
313-
});
313+
});

src/extensibility/ExtensionManagerViewModel.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,13 @@ define(function (require, exports, module) {
366366
*/
367367
function InstalledViewModel() {
368368
ExtensionManagerViewModel.call(this);
369+
370+
// when registry is downloaded, sort extensions again - those with updates will be before others
371+
var self = this;
372+
$(ExtensionManager).on("registryDownload", function () {
373+
self._sortFullSet();
374+
self._setInitialFilter();
375+
});
369376
}
370377

371378
InstalledViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
@@ -405,17 +412,22 @@ define(function (require, exports, module) {
405412
var self = this;
406413

407414
this.sortedFullSet = this.sortedFullSet.sort(function (key1, key2) {
415+
// before sorting by name, put first extensions that have updates
416+
var ua1 = self.extensions[key1].installInfo.updateAvailable,
417+
ua2 = self.extensions[key2].installInfo.updateAvailable;
418+
419+
if (ua1 && !ua2) {
420+
return -1;
421+
} else if (!ua1 && ua2) {
422+
return 1;
423+
}
424+
408425
var metadata1 = self.extensions[key1].installInfo.metadata,
409426
metadata2 = self.extensions[key2].installInfo.metadata,
410427
id1 = (metadata1.title || metadata1.name).toLowerCase(),
411428
id2 = (metadata2.title || metadata2.name).toLowerCase();
412-
if (id1 < id2) {
413-
return -1;
414-
} else if (id1 === id2) {
415-
return 0;
416-
} else {
417-
return 1;
418-
}
429+
430+
return id1.localeCompare(id2);
419431
});
420432
};
421433

@@ -427,7 +439,7 @@ define(function (require, exports, module) {
427439
var self = this;
428440
this.notifyCount = 0;
429441
this.sortedFullSet.forEach(function (key) {
430-
if (self.extensions[key].installInfo.updateAvailable) {
442+
if (self.extensions[key].installInfo.updateCompatible) {
431443
self.notifyCount++;
432444
}
433445
});
@@ -483,4 +495,4 @@ define(function (require, exports, module) {
483495

484496
exports.RegistryViewModel = RegistryViewModel;
485497
exports.InstalledViewModel = InstalledViewModel;
486-
});
498+
});

src/styles/brackets.less

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,10 @@ a, img {
500500
}
501501

502502
#toolbar-extension-manager {
503-
.sprite-icon(0, 0, 24px, 24px, "images/topcoat-plugin-20.svg");
503+
.sprite-icon(0, 0, 24px, 24px, "images/extension-manager-sprite.svg");
504+
&.updatesAvailable {
505+
.sprite-icon(0, 24px, 24px, 24px, "images/extension-manager-sprite.svg");
506+
}
504507
}
505508

506509
/* Project panel */
Lines changed: 9 additions & 0 deletions
Loading

src/styles/images/topcoat-plugin-20.svg

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)