437 lines
12 KiB
JavaScript
437 lines
12 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/. */
|
|
|
|
/* eslint-env browser */
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
createFactory,
|
|
PureComponent,
|
|
} = require("resource://devtools/client/shared/vendor/react.mjs");
|
|
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
|
|
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
|
|
const {
|
|
connect,
|
|
} = require("resource://devtools/client/shared/vendor/react-redux.js");
|
|
|
|
const Toolbar = createFactory(
|
|
require("resource://devtools/client/responsive/components/Toolbar.js")
|
|
);
|
|
|
|
loader.lazyGetter(this, "DeviceModal", () =>
|
|
createFactory(
|
|
require("resource://devtools/client/responsive/components/DeviceModal.js")
|
|
)
|
|
);
|
|
|
|
const {
|
|
changeNetworkThrottling,
|
|
} = require("resource://devtools/client/shared/components/throttling/actions.js");
|
|
const {
|
|
addCustomDevice,
|
|
editCustomDevice,
|
|
removeCustomDevice,
|
|
updateDeviceDisplayed,
|
|
updateDeviceModal,
|
|
updatePreferredDevices,
|
|
} = require("resource://devtools/client/responsive/actions/devices.js");
|
|
const {
|
|
takeScreenshot,
|
|
} = require("resource://devtools/client/responsive/actions/screenshot.js");
|
|
const {
|
|
changeUserAgent,
|
|
toggleLeftAlignment,
|
|
toggleReloadOnTouchSimulation,
|
|
toggleReloadOnUserAgent,
|
|
toggleTouchSimulation,
|
|
toggleUserAgentInput,
|
|
} = require("resource://devtools/client/responsive/actions/ui.js");
|
|
const {
|
|
changeDevice,
|
|
changePixelRatio,
|
|
changeViewportAngle,
|
|
removeDeviceAssociation,
|
|
resizeViewport,
|
|
rotateViewport,
|
|
} = require("resource://devtools/client/responsive/actions/viewports.js");
|
|
const {
|
|
getOrientation,
|
|
} = require("resource://devtools/client/responsive/utils/orientation.js");
|
|
|
|
const Types = require("resource://devtools/client/responsive/types.js");
|
|
|
|
class App extends PureComponent {
|
|
static get propTypes() {
|
|
return {
|
|
devices: PropTypes.shape(Types.devices).isRequired,
|
|
dispatch: PropTypes.func.isRequired,
|
|
leftAlignmentEnabled: PropTypes.bool.isRequired,
|
|
networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
|
|
screenshot: PropTypes.shape(Types.screenshot).isRequired,
|
|
viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
|
|
};
|
|
}
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
|
|
this.onChangeDevice = this.onChangeDevice.bind(this);
|
|
this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
|
|
this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
|
|
this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
|
|
this.onChangeUserAgent = this.onChangeUserAgent.bind(this);
|
|
this.onChangeViewportOrientation =
|
|
this.onChangeViewportOrientation.bind(this);
|
|
this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
|
|
this.onEditCustomDevice = this.onEditCustomDevice.bind(this);
|
|
this.onExit = this.onExit.bind(this);
|
|
this.onRemoveCustomDevice = this.onRemoveCustomDevice.bind(this);
|
|
this.onRemoveDeviceAssociation = this.onRemoveDeviceAssociation.bind(this);
|
|
this.doResizeViewport = this.doResizeViewport.bind(this);
|
|
this.onRotateViewport = this.onRotateViewport.bind(this);
|
|
this.onScreenshot = this.onScreenshot.bind(this);
|
|
this.onToggleLeftAlignment = this.onToggleLeftAlignment.bind(this);
|
|
this.onToggleReloadOnTouchSimulation =
|
|
this.onToggleReloadOnTouchSimulation.bind(this);
|
|
this.onToggleReloadOnUserAgent = this.onToggleReloadOnUserAgent.bind(this);
|
|
this.onToggleUserAgentInput = this.onToggleUserAgentInput.bind(this);
|
|
this.onUpdateDeviceDisplayed = this.onUpdateDeviceDisplayed.bind(this);
|
|
this.onUpdateDeviceModal = this.onUpdateDeviceModal.bind(this);
|
|
}
|
|
|
|
onAddCustomDevice(device) {
|
|
this.props.dispatch(addCustomDevice(device));
|
|
}
|
|
|
|
onChangeDevice(id, device, deviceType) {
|
|
// Resize the viewport first.
|
|
this.doResizeViewport(id, device.width, device.height);
|
|
|
|
// TODO: Bug 1332754: Move messaging and logic into the action creator so that the
|
|
// message is sent from the action creator and device property changes are sent from
|
|
// there instead of this function.
|
|
window.postMessage(
|
|
{
|
|
type: "change-device",
|
|
device,
|
|
viewport: device,
|
|
},
|
|
"*"
|
|
);
|
|
|
|
const orientation = getOrientation(device, device);
|
|
|
|
this.props.dispatch(changeViewportAngle(0, orientation.angle));
|
|
this.props.dispatch(changeDevice(id, device.name, deviceType));
|
|
this.props.dispatch(changePixelRatio(id, device.pixelRatio));
|
|
this.props.dispatch(changeUserAgent(device.userAgent));
|
|
this.props.dispatch(toggleTouchSimulation(device.touch));
|
|
}
|
|
|
|
onChangeNetworkThrottling(enabled, profile) {
|
|
window.postMessage(
|
|
{
|
|
type: "change-network-throttling",
|
|
enabled,
|
|
profile,
|
|
},
|
|
"*"
|
|
);
|
|
this.props.dispatch(changeNetworkThrottling(enabled, profile));
|
|
}
|
|
|
|
onChangePixelRatio(pixelRatio) {
|
|
window.postMessage(
|
|
{
|
|
type: "change-pixel-ratio",
|
|
pixelRatio,
|
|
},
|
|
"*"
|
|
);
|
|
this.props.dispatch(changePixelRatio(0, pixelRatio));
|
|
}
|
|
|
|
onChangeTouchSimulation(enabled) {
|
|
window.postMessage(
|
|
{
|
|
type: "change-touch-simulation",
|
|
enabled,
|
|
},
|
|
"*"
|
|
);
|
|
this.props.dispatch(toggleTouchSimulation(enabled));
|
|
}
|
|
|
|
onChangeUserAgent(userAgent) {
|
|
window.postMessage(
|
|
{
|
|
type: "change-user-agent",
|
|
userAgent,
|
|
},
|
|
"*"
|
|
);
|
|
this.props.dispatch(changeUserAgent(userAgent));
|
|
}
|
|
|
|
onChangeViewportOrientation(id, type, angle, isViewportRotated = false) {
|
|
window.postMessage(
|
|
{
|
|
type: "viewport-orientation-change",
|
|
orientationType: type,
|
|
angle,
|
|
isViewportRotated,
|
|
},
|
|
"*"
|
|
);
|
|
|
|
if (isViewportRotated) {
|
|
this.props.dispatch(changeViewportAngle(id, angle));
|
|
}
|
|
}
|
|
|
|
onDeviceListUpdate(devices) {
|
|
updatePreferredDevices(devices);
|
|
}
|
|
|
|
onEditCustomDevice(oldDevice, newDevice) {
|
|
// If the edited device is currently selected, then update its original association
|
|
// and reset UI state.
|
|
let viewport = this.props.viewports.find(
|
|
({ device }) => device === oldDevice.name
|
|
);
|
|
|
|
if (viewport) {
|
|
viewport = {
|
|
...viewport,
|
|
device: newDevice.name,
|
|
deviceType: "custom",
|
|
height: newDevice.height,
|
|
width: newDevice.width,
|
|
pixelRatio: newDevice.pixelRatio,
|
|
touch: newDevice.touch,
|
|
userAgent: newDevice.userAgent,
|
|
};
|
|
}
|
|
|
|
this.props.dispatch(editCustomDevice(viewport, oldDevice, newDevice));
|
|
}
|
|
|
|
onExit() {
|
|
window.postMessage({ type: "exit" }, "*");
|
|
}
|
|
|
|
onRemoveCustomDevice(device) {
|
|
// If the custom device is currently selected on any of the viewports,
|
|
// remove the device association and reset all the ui state.
|
|
for (const viewport of this.props.viewports) {
|
|
if (viewport.device === device.name) {
|
|
this.onRemoveDeviceAssociation(viewport.id, { resetProfile: true });
|
|
}
|
|
}
|
|
|
|
this.props.dispatch(removeCustomDevice(device));
|
|
}
|
|
|
|
onRemoveDeviceAssociation(id, { resetProfile }) {
|
|
// TODO: Bug 1332754: Move messaging and logic into the action creator so that device
|
|
// property changes are sent from there instead of this function.
|
|
this.props.dispatch(removeDeviceAssociation(id, { resetProfile }));
|
|
if (resetProfile) {
|
|
this.props.dispatch(toggleTouchSimulation(false));
|
|
this.props.dispatch(changePixelRatio(id, 0));
|
|
this.props.dispatch(changeUserAgent(""));
|
|
}
|
|
}
|
|
|
|
doResizeViewport(id, width, height) {
|
|
// This is the setter function that we pass to Toolbar and Viewports
|
|
// so they can modify the viewport.
|
|
window.postMessage(
|
|
{
|
|
type: "viewport-resize",
|
|
width,
|
|
height,
|
|
},
|
|
"*"
|
|
);
|
|
this.props.dispatch(resizeViewport(id, width, height));
|
|
}
|
|
|
|
/**
|
|
* Dispatches the rotateViewport action creator. This utilized by the RDM toolbar as
|
|
* a prop.
|
|
*
|
|
* @param {Number} id
|
|
* The viewport ID.
|
|
*/
|
|
onRotateViewport(id) {
|
|
let currentDevice;
|
|
const viewport = this.props.viewports[id];
|
|
|
|
for (const type of this.props.devices.types) {
|
|
for (const device of this.props.devices[type]) {
|
|
if (viewport.device === device.name) {
|
|
currentDevice = device;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no device is selected, then assume the selected device's primary orientation is
|
|
// opposite of the viewport orientation.
|
|
if (!currentDevice) {
|
|
currentDevice = {
|
|
height: viewport.width,
|
|
width: viewport.height,
|
|
};
|
|
}
|
|
|
|
const currentAngle = Services.prefs.getIntPref(
|
|
"devtools.responsive.viewport.angle"
|
|
);
|
|
const angleToRotateTo = currentAngle === 90 ? 0 : 90;
|
|
const { type, angle } = getOrientation(
|
|
currentDevice,
|
|
viewport,
|
|
angleToRotateTo
|
|
);
|
|
|
|
this.onChangeViewportOrientation(id, type, angle, true);
|
|
this.props.dispatch(rotateViewport(id));
|
|
|
|
window.postMessage(
|
|
{
|
|
type: "viewport-resize",
|
|
height: viewport.width,
|
|
width: viewport.height,
|
|
},
|
|
"*"
|
|
);
|
|
}
|
|
|
|
onScreenshot() {
|
|
this.props.dispatch(takeScreenshot());
|
|
}
|
|
|
|
onToggleLeftAlignment() {
|
|
this.props.dispatch(toggleLeftAlignment());
|
|
|
|
window.postMessage(
|
|
{
|
|
type: "toggle-left-alignment",
|
|
leftAlignmentEnabled: this.props.leftAlignmentEnabled,
|
|
},
|
|
"*"
|
|
);
|
|
}
|
|
|
|
onToggleReloadOnTouchSimulation() {
|
|
this.props.dispatch(toggleReloadOnTouchSimulation());
|
|
}
|
|
|
|
onToggleReloadOnUserAgent() {
|
|
this.props.dispatch(toggleReloadOnUserAgent());
|
|
}
|
|
|
|
onToggleUserAgentInput() {
|
|
this.props.dispatch(toggleUserAgentInput());
|
|
}
|
|
|
|
onUpdateDeviceDisplayed(device, deviceType, displayed) {
|
|
this.props.dispatch(updateDeviceDisplayed(device, deviceType, displayed));
|
|
}
|
|
|
|
onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
|
|
this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
|
|
window.postMessage({ type: "update-device-modal", isOpen }, "*");
|
|
}
|
|
|
|
render() {
|
|
const { devices, networkThrottling, screenshot, viewports } = this.props;
|
|
|
|
const {
|
|
onAddCustomDevice,
|
|
onChangeDevice,
|
|
onChangeNetworkThrottling,
|
|
onChangePixelRatio,
|
|
onChangeTouchSimulation,
|
|
onChangeUserAgent,
|
|
onDeviceListUpdate,
|
|
onEditCustomDevice,
|
|
onExit,
|
|
onRemoveCustomDevice,
|
|
onRemoveDeviceAssociation,
|
|
doResizeViewport,
|
|
onRotateViewport,
|
|
onScreenshot,
|
|
onToggleLeftAlignment,
|
|
onToggleReloadOnTouchSimulation,
|
|
onToggleReloadOnUserAgent,
|
|
onToggleUserAgentInput,
|
|
onUpdateDeviceDisplayed,
|
|
onUpdateDeviceModal,
|
|
} = this;
|
|
|
|
if (!viewports.length) {
|
|
return null;
|
|
}
|
|
|
|
const selectedDevice = viewports[0].device;
|
|
const selectedPixelRatio = viewports[0].pixelRatio;
|
|
|
|
let deviceAdderViewportTemplate = {};
|
|
if (devices.modalOpenedFromViewport !== null) {
|
|
deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
|
|
}
|
|
|
|
return dom.div(
|
|
{ id: "app" },
|
|
Toolbar({
|
|
devices,
|
|
networkThrottling,
|
|
screenshot,
|
|
selectedDevice,
|
|
selectedPixelRatio,
|
|
viewport: viewports[0],
|
|
onChangeDevice,
|
|
onChangeNetworkThrottling,
|
|
onChangePixelRatio,
|
|
onChangeTouchSimulation,
|
|
onChangeUserAgent,
|
|
onExit,
|
|
onRemoveDeviceAssociation,
|
|
doResizeViewport,
|
|
onRotateViewport,
|
|
onScreenshot,
|
|
onToggleLeftAlignment,
|
|
onToggleReloadOnTouchSimulation,
|
|
onToggleReloadOnUserAgent,
|
|
onToggleUserAgentInput,
|
|
onUpdateDeviceModal,
|
|
}),
|
|
devices.isModalOpen
|
|
? DeviceModal({
|
|
deviceAdderViewportTemplate,
|
|
devices,
|
|
onAddCustomDevice,
|
|
onDeviceListUpdate,
|
|
onEditCustomDevice,
|
|
onRemoveCustomDevice,
|
|
onUpdateDeviceDisplayed,
|
|
onUpdateDeviceModal,
|
|
})
|
|
: null
|
|
);
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = state => {
|
|
return {
|
|
...state,
|
|
leftAlignmentEnabled: state.ui.leftAlignmentEnabled,
|
|
};
|
|
};
|
|
|
|
module.exports = connect(mapStateToProps)(App);
|