Bug 986677 - Add time left and activity state to experiments in the Addon Manager UI. r=unfocused

This commit is contained in:
Georg Fritzsche 2014-04-23 14:34:49 +02:00
parent 106031c13f
commit 02104e5d26
9 changed files with 249 additions and 4 deletions

View File

@ -592,6 +592,28 @@ Experiments.Experiments.prototype = {
}.bind(this));
},
/**
* Returns the ExperimentInfo for the active experiment, or null
* if there is none.
*/
getActiveExperiment: function () {
let experiment = this._getActiveExperiment();
if (!experiment) {
return null;
}
let info = {
id: experiment.id,
name: experiment._name,
description: experiment._description,
active: experiment.enabled,
endDate: experiment.endDate.getTime(),
detailURL: experiment._homepageURL,
};
return info;
},
/**
* Determine whether another date has the same UTC day as now().
*/
@ -742,6 +764,7 @@ Experiments.Experiments.prototype = {
return;
}
this._log.trace("onInstallStarted() - " + install.addon.id);
if (install.addon.appDisabled) {
// This is a PreviousExperiment
return;
@ -2061,6 +2084,7 @@ function PreviousExperimentAddon(experiment) {
this._id = experiment.id;
this._name = experiment.name;
this._endDate = experiment.endDate;
this._description = experiment.description;
}
PreviousExperimentAddon.prototype = Object.freeze({
@ -2134,7 +2158,9 @@ PreviousExperimentAddon.prototype = Object.freeze({
// BEGIN OPTIONAL PROPERTIES
// TODO description
get description() {
return this._description;
},
get updateDate() {
return new Date(this._endDate);
@ -2155,4 +2181,12 @@ PreviousExperimentAddon.prototype = Object.freeze({
// END REQUIRED METHODS
/**
* The end-date of the experiment, required for the Addon Manager UI.
*/
get endDate() {
return this._endDate;
},
});

View File

@ -98,6 +98,32 @@ details.notification.uninstall=%1$S will be uninstalled after you restart %2$S.
#LOCALIZATION NOTE (details.notification.upgrade) %1$S is the add-on name, %2$S is brand name
details.notification.upgrade=%1$S will be updated after you restart %2$S.
#LOCALIZATION NOTE (details.experiment.time.daysRemaining) #1 is the number of days from now that the experiment will remain active (detail view).
details.experiment.time.daysRemaining=#1 day remaining;#1 days remaining
#LOCALIZATION NOTE (details.experiment.time.endsToday) The experiment will end in less than a day (detail view).
details.experiment.time.endsToday=Less than a day remaining
#LOCALIZATION NOTE (details.experiment.time.daysPassed) #1 is the number of days since the experiment ran (detail view).
details.experiment.time.daysPassed=#1 day ago;#1 days ago
#LOCALIZATION NOTE (details.experiment.time.endedToday) The experiment ended less than a day ago (detail view).
details.experiment.time.endedToday=Less than a day ago
#LOCALIZATION NOTE (details.experiment.state.active) This experiment is active (detail view).
details.experiment.state.active=Active
#LOCALIZATION NOTE (details.experiment.state.complete) This experiment is complete (it was previously active) (detail view).
details.experiment.state.complete=Complete
#LOCALIZATION NOTE (experiment.time.daysRemaining) #1 is the number of days from now that the experiment will remain active (list view item).
experiment.time.daysRemaining=#1 day remaining;#1 days remaining
#LOCALIZATION NOTE (experiment.time.endsToday) The experiment will end in less than a day (list view item).
experiment.time.endsToday=Less than a day remaining
#LOCALIZATION NOTE (experiment.time.daysPassed) #1 is the number of days since the experiment ran (list view item).
experiment.time.daysPassed=#1 day ago;#1 days ago
#LOCALIZATION NOTE (experiment.time.endedToday) The experiment ended less than a day ago (list view item).
experiment.time.endedToday=Less than a day ago
#LOCALIZATION NOTE (experiment.state.active) This experiment is active (list view item).
experiment.state.active=Active
#LOCALIZATION NOTE (experiment.state.complete) This experiment is complete (it was previously active) (list view item).
experiment.state.complete=Complete
installFromFile.dialogTitle=Select add-on to install
installFromFile.filterName=Add-ons

View File

@ -245,3 +245,8 @@ richlistitem:not([selected]) * {
#detail-view[type="experiment"] #detail-creator {
display: none;
}
.view-pane:not([type="experiment"]) .experiment-container,
.view-pane:not([type="experiment"]) #detail-experiment-container {
display: none;
}

View File

@ -20,6 +20,8 @@ XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function () {
return Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {}).
BrowserToolboxProcess;
});
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
"resource:///modules/experiments/Experiments.jsm");
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
@ -208,6 +210,23 @@ function isDiscoverEnabled() {
return true;
}
function getExperimentEndDate(aAddon) {
if (!("@mozilla.org/browser/experiments-service;1" in Cc)) {
return 0;
}
if (!aAddon.isActive) {
return aAddon.endDate;
}
let experiment = Experiments.instance().getActiveExperiment();
if (!experiment) {
return 0;
}
return experiment.endDate;
}
/**
* Obtain the main DOMWindow for the current context.
*/
@ -1426,6 +1445,10 @@ function createItem(aObj, aIsInstall, aIsRemote) {
// the binding handles the rest
item.setAttribute("value", aObj.id);
if (aObj.type == "experiment") {
item.endDate = getExperimentEndDate(aObj);
}
return item;
}
@ -2874,6 +2897,34 @@ var gDetailView = {
}
}
if (this._addon.type == "experiment") {
let prefix = "details.experiment.";
let active = this._addon.isActive;
let stateKey = prefix + "state." + (active ? "active" : "complete");
let node = document.getElementById("detail-experiment-state");
node.value = gStrings.ext.GetStringFromName(stateKey);
let now = Date.now();
let end = getExperimentEndDate(this._addon);
let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
let timeKey = prefix + "time.";
let timeMessage;
if (days < 1) {
timeKey += (active ? "endsToday" : "endedToday");
timeMessage = gStrings.ext.GetStringFromName(timeKey);
} else {
timeKey += (active ? "daysRemaining" : "daysPassed");
days = Math.round(days);
let timeString = gStrings.ext.GetStringFromName(timeKey);
timeMessage = PluralForm.get(days, timeString)
.replace("#1", days);
}
document.getElementById("detail-experiment-time").value = timeMessage;
}
this.fillSettingsRows(aScrollToPreferences, (function updateView_fillSettingsRows() {
this.updateState();
gViewController.notifyViewChanged();

View File

@ -804,6 +804,15 @@
<xul:label anonid="date-updated" class="date-updated"
unknown="&addon.unknownDate;"/>
</xul:hbox>
<xul:hbox class="experiment-container">
<svg width="6" height="6" viewBox="0 0 6 6" version="1.1"
xmlns="http://www.w3.org/2000/svg"
class="experiment-bullet-container">
<circle cx="3" cy="3" r="3" class="experiment-bullet"/>
</svg>
<xul:label anonid="experiment-state" class="experiment-state"/>
<xul:label anonid="experiment-time" class="experiment-time"/>
</xul:hbox>
<xul:hbox class="advancedinfo-container" flex="1">
<xul:vbox class="description-outer-container" flex="1">
@ -973,6 +982,12 @@
<field name="_version">
document.getAnonymousElementByAttribute(this, "anonid", "version");
</field>
<field name="_experimentState">
document.getAnonymousElementByAttribute(this, "anonid", "experiment-state");
</field>
<field name="_experimentTime">
document.getAnonymousElementByAttribute(this, "anonid", "experiment-time");
</field>
<field name="_icon">
document.getAnonymousElementByAttribute(this, "anonid", "icon");
</field>
@ -1347,6 +1362,33 @@
Services.prefs.getBoolPref('devtools.debugger.remote-enabled');
this._debugBtn.disabled = this._debugBtn.hidden = !debuggable
if (this.mAddon.type == "experiment") {
let prefix = "experiment.";
let active = this.mAddon.isActive;
let stateKey = prefix + "state." + (active ? "active" : "complete");
this._experimentState.value = gStrings.ext.GetStringFromName(stateKey);
let now = Date.now();
let end = this.endDate;
let days = Math.abs(end - now) / (24 * 60 * 60 * 1000);
let timeKey = prefix + "time.";
let timeMessage;
if (days < 1) {
timeKey += (active ? "endsToday" : "endedToday");
timeMessage = gStrings.ext.GetStringFromName(timeKey);
} else {
timeKey += (active ? "daysRemaining" : "daysPassed");
days = Math.round(days);
let timeString = gStrings.ext.GetStringFromName(timeKey);
timeMessage = PluralForm.get(days, timeString)
.replace("#1", days);
}
this._experimentTime.value = timeMessage;
}
]]></body>
</method>

View File

@ -532,6 +532,15 @@
</hbox>
<label id="detail-creator" class="creator"/>
</vbox>
<hbox id="detail-experiment-container">
<svg width="8" height="8" viewBox="0 0 8 8" version="1.1"
xmlns="http://www.w3.org/2000/svg"
id="detail-experiment-bullet-container">
<circle cx="4" cy="4" r="4" id="detail-experiment-bullet"/>
</svg>
<label id="detail-experiment-state"/>
<label id="detail-experiment-time"/>
</hbox>
<hbox id="detail-desc-container" align="start">
<vbox pack="center"> <!-- Necessary to work around bug 394738 -->
<image id="detail-screenshot" hidden="true"/>

View File

@ -923,3 +923,30 @@ setting[type="radio"] > radiogroup {
.header-button .toolbarbutton-text {
display: none;
}
/*** telemetry experiments ***/
#detail-experiment-container {
font-size: 80%;
margin-bottom: 1em;
}
#detail-experiment-bullet-container,
#detail-experiment-state,
#detail-experiment-time,
.experiment-bullet-container,
.experiment-state,
.experiment-time {
vertical-align: middle;
display: inline-block;
}
.addon .experiment-bullet,
#detail-experiment-bullet {
fill: rgb(158, 158, 158);
}
.addon[active="true"] .experiment-bullet,
#detail-view[active="true"] #detail-experiment-bullet {
fill: rgb(106, 201, 20);
}

View File

@ -6,7 +6,6 @@
%include ../../global/shared.inc
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");
@ -1163,3 +1162,30 @@ button.button-link:not([disabled="true"]):active:hover {
box-shadow: inset 0 0 4px rgb(45,54,71), 0 1px rgba(255,255,255,0.25);
background-image: linear-gradient(rgba(45,54,71,0.6), rgba(45,54,71,0));
}
/*** telemetry experiments ***/
#detail-experiment-container {
font-size: 80%;
margin-bottom: 1em;
}
#detail-experiment-bullet-container,
#detail-experiment-state,
#detail-experiment-time,
.experiment-bullet-container,
.experiment-state,
.experiment-time {
vertical-align: middle;
display: inline-block;
}
.addon .experiment-bullet,
#detail-experiment-bullet {
fill: rgb(158, 158, 158);
}
.addon[active="true"] .experiment-bullet,
#detail-view[active="true"] #detail-experiment-bullet {
fill: rgb(106, 201, 20);
}

View File

@ -4,8 +4,6 @@
@import url("chrome://global/skin/inContentUI.css");
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
.nav-button {
list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
@ -1197,3 +1195,30 @@ button.button-link:not([disabled="true"]):active:hover {
.header-button > .toolbarbutton-text {
display: none;
}
/*** telemetry experiments ***/
#detail-experiment-container {
font-size: 80%;
margin-bottom: 1em;
}
#detail-experiment-bullet-container,
#detail-experiment-state,
#detail-experiment-time,
.experiment-bullet-container,
.experiment-state,
.experiment-time {
vertical-align: middle;
display: inline-block;
}
.addon .experiment-bullet,
#detail-experiment-bullet {
fill: rgb(158, 158, 158);
}
.addon[active="true"] .experiment-bullet,
#detail-view[active="true"] #detail-experiment-bullet {
fill: rgb(106, 201, 20);
}