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

Commit 8712385

Browse files
committed
Merge pull request #8759 from MiguelCastillo/themes-tab
Themes tab
2 parents 98b8ea7 + a03a2df commit 8712385

9 files changed

Lines changed: 354 additions & 9 deletions

File tree

src/extensibility/ExtensionManager.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ define(function (require, exports, module) {
5252
// semver.browser is an AMD-compatible module
5353
var semver = require("extensibility/node/node_modules/semver/semver.browser");
5454

55+
/**
56+
* @private
57+
* @type {$.Deferred} Keeps track of the current registry download so that if a request is already
58+
* in progress and another request to download the registry comes in, we don't send yet another request.
59+
* This is primarily used when multiple view models need to download the registry at the same time.
60+
*/
61+
var pendingDownloadRegistry = null;
62+
5563
/**
5664
* Extension status constants.
5765
*/
@@ -180,7 +188,12 @@ define(function (require, exports, module) {
180188
* or rejected if the server can't be reached.
181189
*/
182190
function downloadRegistry() {
183-
var result = new $.Deferred();
191+
if (pendingDownloadRegistry) {
192+
return pendingDownloadRegistry.promise();
193+
}
194+
195+
pendingDownloadRegistry = new $.Deferred();
196+
184197
$.ajax({
185198
url: brackets.config.extension_registry,
186199
dataType: "json",
@@ -195,12 +208,17 @@ define(function (require, exports, module) {
195208
synchronizeEntry(id);
196209
});
197210
$(exports).triggerHandler("registryDownload");
198-
result.resolve();
211+
pendingDownloadRegistry.resolve();
199212
})
200213
.fail(function () {
201-
result.reject();
214+
pendingDownloadRegistry.reject();
215+
})
216+
.always(function () {
217+
// Make sure to clean up the pending registry so that new requests can be made.
218+
pendingDownloadRegistry = null;
202219
});
203-
return result.promise();
220+
221+
return pendingDownloadRegistry.promise();
204222
}
205223

206224

@@ -450,7 +468,7 @@ define(function (require, exports, module) {
450468
if (installationResult.keepFile === undefined) {
451469
installationResult.keepFile = false;
452470
}
453-
471+
454472
var installationStatus = installationResult.installationStatus;
455473
if (installationStatus === Package.InstallationStatuses.ALREADY_INSTALLED ||
456474
installationStatus === Package.InstallationStatuses.NEEDS_UPDATE ||

src/extensibility/ExtensionManagerDialog.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ define(function (require, exports, module) {
272272
// Load registry only if the registry URL exists
273273
if (context.showRegistry) {
274274
models.push(new ExtensionManagerViewModel.RegistryViewModel());
275+
models.push(new ExtensionManagerViewModel.ThemesViewModel());
275276
}
276277

277278
models.push(new ExtensionManagerViewModel.InstalledViewModel());

src/extensibility/ExtensionManagerView.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ define(function (require, exports, module) {
218218
context.isMarkedForRemoval = ExtensionManager.isMarkedForRemoval(info.metadata.name);
219219
context.isMarkedForUpdate = ExtensionManager.isMarkedForUpdate(info.metadata.name);
220220

221-
context.showInstallButton = (this.model.source === this.model.SOURCE_REGISTRY) && !context.updateAvailable;
221+
context.showInstallButton = (this.model.source === this.model.SOURCE_REGISTRY || this.model.source === this.model.SOURCE_THEMES) && !context.updateAvailable;
222222
context.showUpdateButton = context.updateAvailable && !context.isMarkedForUpdate && !context.isMarkedForRemoval;
223223

224224
context.allowInstall = context.isCompatible && !context.isInstalled;

src/extensibility/ExtensionManagerViewModel.js

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ define(function (require, exports, module) {
6868
*/
6969
ExtensionManagerViewModel.prototype.SOURCE_REGISTRY = "registry";
7070

71+
/**
72+
* @type {string}
73+
* Constant indicating that this model/view should initialize from the main extension registry with only themes.
74+
*/
75+
ExtensionManagerViewModel.prototype.SOURCE_THEMES = "themes";
76+
7177
/**
7278
* @type {string}
7379
* Constant indicating that this model/view should initialize from the list of locally installed extensions.
@@ -158,7 +164,7 @@ define(function (require, exports, module) {
158164
*/
159165
ExtensionManagerViewModel.prototype.initialize = function () {
160166
var self = this;
161-
167+
162168
this._initializeFromSourcePromise = this._initializeFromSource().always(function () {
163169
self._updateMessage();
164170
});
@@ -326,7 +332,7 @@ define(function (require, exports, module) {
326332
// Sort the registry by last published date and store the sorted list of IDs.
327333
self.sortedFullSet = registry_utils.sortRegistry(self.extensions, "registryInfo")
328334
.filter(function (entry) {
329-
return entry.registryInfo !== undefined;
335+
return entry.registryInfo !== undefined && entry.registryInfo.metadata.theme === undefined;
330336
})
331337
.map(function (entry) {
332338
return entry.registryInfo.metadata.name;
@@ -495,6 +501,74 @@ define(function (require, exports, module) {
495501
return entry;
496502
};
497503

504+
/**
505+
* The model for the ExtensionManagerView that is responsible for handling registry-based theme extensions.
506+
* This extends ExtensionManagerViewModel.
507+
* Must be disposed with dispose() when done.
508+
*
509+
* Events:
510+
* - change - triggered when the data for a given extension changes. Second parameter is the extension id.
511+
* - filter - triggered whenever the filtered set changes (including on initialize).
512+
*
513+
* @constructor
514+
*/
515+
function ThemesViewModel() {
516+
ExtensionManagerViewModel.call(this);
517+
}
518+
519+
// Inheritance setup
520+
ThemesViewModel.prototype = Object.create(ExtensionManagerViewModel.prototype);
521+
ThemesViewModel.prototype.constructor = ThemesViewModel;
522+
523+
/**
524+
* @type {string}
525+
* ThemeViewModels always have a source of SOURCE_THEMES.
526+
*/
527+
ThemesViewModel.prototype.source = ExtensionManagerViewModel.prototype.SOURCE_THEMES;
528+
529+
/**
530+
* Initializes the model from the remote extension registry.
531+
* @return {$.Promise} a promise that's resolved with the registry JSON data.
532+
*/
533+
ThemesViewModel.prototype._initializeFromSource = function () {
534+
var self = this;
535+
return ExtensionManager.downloadRegistry()
536+
.done(function () {
537+
self.extensions = ExtensionManager.extensions;
538+
539+
// Sort the registry by last published date and store the sorted list of IDs.
540+
self.sortedFullSet = registry_utils.sortRegistry(self.extensions, "registryInfo")
541+
.filter(function (entry) {
542+
return entry.installInfo === undefined && entry.registryInfo !== undefined && entry.registryInfo.metadata.theme;
543+
})
544+
.map(function (entry) {
545+
return entry.registryInfo.metadata.name;
546+
});
547+
self._setInitialFilter();
548+
})
549+
.fail(function () {
550+
self.extensions = [];
551+
self.sortedFullSet = [];
552+
self.filterSet = [];
553+
});
554+
};
555+
556+
/**
557+
* @private
558+
* Finds the theme extension metadata by id. If there is no theme extension matching the given id,
559+
* this returns `null`.
560+
* @param {string} id of the theme extension
561+
* @return {Object?} extension metadata or null if there's no matching extension
562+
*/
563+
ThemesViewModel.prototype._getEntry = function (id) {
564+
var entry = this.extensions[id];
565+
if (entry) {
566+
return entry.registryInfo;
567+
}
568+
return entry;
569+
};
570+
498571
exports.RegistryViewModel = RegistryViewModel;
572+
exports.ThemesViewModel = ThemesViewModel;
499573
exports.InstalledViewModel = InstalledViewModel;
500574
});

src/htmlContent/extension-manager-dialog.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<ul class="nav nav-tabs">
44
{{#showRegistry}}
55
<li><a href="#registry" class="registry" data-toggle="tab"><img src="styles/images/extension-manager-registry.svg"/><br/>{{Strings.EXTENSIONS_AVAILABLE_TITLE}}</a></li>
6+
<li><a href="#themes" class="themes" data-toggle="tab"><img src="styles/images/themes-icon.svg"/><br/>{{Strings.EXTENSIONS_THEMES_TITLE}}</a></li>
67
<!--<li><a href="#updates" class="updates" data-toggle="tab"><img src="styles/images/extension-manager-updates.svg"/><br/>{{Strings.EXTENSIONS_UPDATES_TITLE}}</a></li>-->
78
{{/showRegistry}}
89
<li><a href="#installed" class="installed" data-toggle="tab"><span class="notification"></span><img src="styles/images/extension-manager-installed.svg"/><br/>{{Strings.EXTENSIONS_INSTALLED_TITLE}}</a></li>

src/nls/root/strings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ define({
520520
"REGISTRY_SANITY_CHECK_WARNING" : "NOTE: These extensions may come from different authors than {APP_NAME} itself. Extensions are not reviewed and have full local privileges. Be cautious when installing extensions from an unknown source.",
521521
"EXTENSIONS_INSTALLED_TITLE" : "Installed",
522522
"EXTENSIONS_AVAILABLE_TITLE" : "Available",
523+
"EXTENSIONS_THEMES_TITLE" : "Themes",
523524
"EXTENSIONS_UPDATES_TITLE" : "Updates",
524525

525526
"INLINE_EDITOR_NO_MATCHES" : "No matches available.",

src/styles/images/themes-icon.svg

Lines changed: 8 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)