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

Commit 36f4365

Browse files
committed
Merge pull request #5838 from adobe/pflynn/extman-update-notification
Show update notification icon on Extension Manager "Installed" tab
2 parents d0ba7ec + f67f898 commit 36f4365

5 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/extensibility/ExtensionManagerDialog.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,36 @@ define(function (require, exports, module) {
191191
$(this).tab("show");
192192
});
193193

194+
// Update & hide/show the notification overlay on a tab's icon, based on its model's notifyCount
195+
function updateNotificationIcon(index) {
196+
var model = models[index],
197+
$notificationIcon = $dlg.find(".nav-tabs li").eq(index).find(".notification");
198+
if (model.notifyCount) {
199+
$notificationIcon.text(model.notifyCount);
200+
$notificationIcon.show();
201+
} else {
202+
$notificationIcon.hide();
203+
}
204+
}
205+
194206
// Initialize models and create a view for each model
195207
var modelInitPromise = Async.doInParallel(models, function (model, index) {
196208
var view = new ExtensionManagerView(),
197-
promise = view.initialize(model);
209+
promise = view.initialize(model),
210+
lastNotifyCount;
198211

199212
promise.always(function () {
200213
views[index] = view;
214+
215+
lastNotifyCount = model.notifyCount;
216+
updateNotificationIcon(index);
217+
});
218+
219+
$(model).on("change", function () {
220+
if (lastNotifyCount !== model.notifyCount) {
221+
lastNotifyCount = model.notifyCount;
222+
updateNotificationIcon(index);
223+
}
201224
});
202225

203226
return promise;

src/extensibility/ExtensionManagerViewModel.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ define(function (require, exports, module) {
117117
*/
118118
ExtensionManagerViewModel.prototype.message = null;
119119

120+
/**
121+
* @type {number}
122+
* Number to show in tab's notification icon. No icon shown if 0.
123+
*/
124+
ExtensionManagerViewModel.prototype.notifyCount = 0;
125+
120126
/**
121127
* @private {$.Promise}
122128
* Internal use only to track when initialization fails, see usage in _updateMessage.
@@ -374,6 +380,8 @@ define(function (require, exports, module) {
374380
});
375381
this._sortFullSet();
376382
this._setInitialFilter();
383+
this._countUpdates();
384+
377385
return new $.Deferred().resolve().promise();
378386
};
379387

@@ -399,6 +407,20 @@ define(function (require, exports, module) {
399407
});
400408
};
401409

410+
/**
411+
* @private
412+
* Updates notifyCount based on number of extensions with an update available
413+
*/
414+
InstalledViewModel.prototype._countUpdates = function () {
415+
var self = this;
416+
this.notifyCount = 0;
417+
this.sortedFullSet.forEach(function (key) {
418+
if (self.extensions[key].installInfo.updateAvailable) {
419+
self.notifyCount++;
420+
}
421+
});
422+
};
423+
402424
/**
403425
* @private
404426
* Updates the initial set and filter as necessary when the status of an extension changes,
@@ -412,6 +434,7 @@ define(function (require, exports, module) {
412434
if (index !== -1 && !this.extensions[id].installInfo) {
413435
// This was in our set, but was uninstalled. Remove it.
414436
this.sortedFullSet.splice(index, 1);
437+
this._countUpdates(); // may also affect update count
415438
refilter = true;
416439
} else if (index === -1 && this.extensions[id].installInfo) {
417440
// This was not in our set, but is now installed. Add it and resort.
@@ -422,6 +445,12 @@ define(function (require, exports, module) {
422445
if (refilter) {
423446
this.filter(this._lastQuery || "", true);
424447
}
448+
449+
if (this.extensions[id].installInfo) {
450+
// If our count of available updates may have been affected, re-count
451+
this._countUpdates();
452+
}
453+
425454
ExtensionManagerViewModel.prototype._handleStatusChange.call(this, e, id);
426455
};
427456

src/htmlContent/extension-manager-dialog.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
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>
66
<!--<li><a href="#updates" class="updates" data-toggle="tab"><img src="styles/images/extension-manager-updates.svg"/><br/>{{Strings.EXTENSIONS_UPDATES_TITLE}}</a></li>-->
77
{{/showRegistry}}
8-
<li><a href="#installed" class="installed" data-toggle="tab"><img src="styles/images/extension-manager-installed.svg"/><br/>{{Strings.EXTENSIONS_INSTALLED_TITLE}}</a></li>
8+
<li><a href="#installed" class="installed" data-toggle="tab"><img src="styles/images/extension-manager-installed.svg"/><br/>{{Strings.EXTENSIONS_INSTALLED_TITLE}}</a><span class="notification"></span></li>
99
</ul>
1010
<div>
1111
<button class="search-clear"></button>

src/styles/brackets_patterns_override.less

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,20 @@ a[href^="http"] {
727727
background-color: #dfe2e2;
728728
border-color: #b4b7b7 #b4b7b7 transparent #b4b7b7;
729729
}
730+
731+
> li {
732+
position: relative; // for positioning .notification icon
733+
> .notification {
734+
display: none; // hidden by default
735+
background-color: #91CC41;
736+
color: white;
737+
border-radius: 16px;
738+
padding: 0 5px;
739+
position: absolute;
740+
right: 19px;
741+
top: 8px;
742+
}
743+
}
730744
}
731745

732746
/* Search box */

test/spec/ExtensionManager-test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,11 +1040,18 @@ define(function (require, exports, module) {
10401040
item.metadata.name === "mock-legacy-extension") {
10411041
expect(view).toHaveText(item.metadata.name);
10421042
expect($button.length).toBe(1);
1043+
1044+
// But no update button
1045+
var $updateButton = $("button.update[data-extension-id=" + item.metadata.name + "]", view.$el);
1046+
expect($updateButton.length).toBe(0);
10431047
} else {
10441048
expect(view).not.toHaveText(item.metadata.name);
10451049
expect($button.length).toBe(0);
10461050
}
10471051
});
1052+
1053+
// And no overall update icon overlay
1054+
expect(model.notifyCount).toBe(0);
10481055
});
10491056
});
10501057

@@ -1172,6 +1179,7 @@ define(function (require, exports, module) {
11721179
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
11731180
expect($button.length).toBe(1);
11741181
expect($button.prop("disabled")).toBeFalsy();
1182+
expect(model.notifyCount).toBe(1);
11751183

11761184
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
11771185
});
@@ -1189,6 +1197,7 @@ define(function (require, exports, module) {
11891197
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
11901198
expect($button.length).toBe(1);
11911199
expect($button.prop("disabled")).toBeTruthy();
1200+
expect(model.notifyCount).toBe(1);
11921201

11931202
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
11941203
expect($(".alert.warning", view.$el).length).toBe(0);
@@ -1206,6 +1215,7 @@ define(function (require, exports, module) {
12061215
var $button = $("button.update[data-extension-id=mock-extension]", view.$el);
12071216
expect($button.length).toBe(1);
12081217
expect($button.prop("disabled")).toBeTruthy();
1218+
expect(model.notifyCount).toBe(1);
12091219

12101220
expect($("button.install[data-extension-id=mock-extension]", view.$el).length).toBe(0);
12111221
expect($(".alert.warning", view.$el).length).toBe(0);

0 commit comments

Comments
 (0)