gecko-dev/devtools/client/shared/devices.js
J. Ryan Stinnett b746489c0b Bug 1412359 - Filter to matching device on remove. r=gl
The local device removal path used by RDM had a bug in its `findIndex` call
which caused it to always return `true` for the first device.

Effectively this meant that each separate device removal button always removed
the first device!  This would lead to all sorts of user confusion and UI
divergence.

Here we clean this up by allowing the caller (RDM in this case) to specify via a
callback which device is intended for removal.

MozReview-Commit-ID: 22VwEDZAXOa

--HG--
extra : rebase_source : a48b314090a321aa13cf8ca436e2beefa3dcc392
2017-10-30 11:00:55 -05:00

144 lines
3.8 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { getJSON } = require("devtools/client/shared/getjson");
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/device.properties");
loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
const DEVICES_URL = "devtools.devices.url";
const LOCAL_DEVICES = "devtools.devices.local";
/* This is a catalog of common web-enabled devices and their properties,
* intended for (mobile) device emulation.
*
* The properties of a device are:
* - name: brand and model(s).
* - width: viewport width.
* - height: viewport height.
* - pixelRatio: ratio from viewport to physical screen pixels.
* - userAgent: UA string of the device's browser.
* - touch: whether it has a touch screen.
* - os: default OS, such as "ios", "fxos", "android".
*
* The device types are:
* ["phones", "tablets", "laptops", "televisions", "consoles", "watches"].
*
* To propose new devices for the shared catalog, check out the repo at
* https://github.com/mozilla/simulated-devices and file a pull request.
*
* You can easily add more devices to this catalog from your own code (e.g. an
* addon) like so:
*
* var myPhone = { name: "My Phone", ... };
* require("devtools/client/shared/devices").addDevice(myPhone, "phones");
*/
// Local devices catalog that addons can add to.
let localDevices;
let localDevicesLoaded = false;
/**
* Load local devices from storage.
*/
async function loadLocalDevices() {
if (localDevicesLoaded) {
return;
}
let devicesJSON = await asyncStorage.getItem(LOCAL_DEVICES);
if (!devicesJSON) {
devicesJSON = "{}";
}
localDevices = JSON.parse(devicesJSON);
localDevicesLoaded = true;
}
/**
* Add a device to the local catalog.
* Returns `true` if the device is added, `false` otherwise.
*/
async function addDevice(device, type = "phones") {
await loadLocalDevices();
let list = localDevices[type];
if (!list) {
list = localDevices[type] = [];
}
// Ensure the new device is has a unique name
let exists = list.some(entry => entry.name == device.name);
if (exists) {
return false;
}
list.push(Object.assign({}, device));
await asyncStorage.setItem(LOCAL_DEVICES, JSON.stringify(localDevices));
return true;
}
/**
* Remove a device from the local catalog.
* Returns `true` if the device is removed, `false` otherwise.
*/
async function removeDevice(device, type = "phones") {
await loadLocalDevices();
let list = localDevices[type];
if (!list) {
return false;
}
let index = list.findIndex(entry => entry.name == device.name);
if (index == -1) {
return false;
}
list.splice(index, 1);
await asyncStorage.setItem(LOCAL_DEVICES, JSON.stringify(localDevices));
return true;
}
/**
* Remove all local devices. Useful to clear everything when testing.
*/
async function removeLocalDevices() {
await asyncStorage.removeItem(LOCAL_DEVICES);
localDevices = {};
}
/**
* Get the complete devices catalog.
*/
async function getDevices() {
// Fetch common devices from Mozilla's CDN.
let devices = await getJSON(DEVICES_URL);
await loadLocalDevices();
for (let type in localDevices) {
if (!devices[type]) {
devices.TYPES.push(type);
devices[type] = [];
}
devices[type] = localDevices[type].concat(devices[type]);
}
return devices;
}
/**
* Get the localized string for a device type.
*/
function getDeviceString(deviceType) {
return L10N.getStr("device." + deviceType);
}
module.exports = {
addDevice,
removeDevice,
removeLocalDevices,
getDevices,
getDeviceString,
};