Merge conflict, schema.rb
commit
7169ee78a4
5
Gemfile
5
Gemfile
|
@ -2,7 +2,7 @@ source "https://rubygems.org"
|
|||
ruby "2.5.1"
|
||||
|
||||
gem "active_model_serializers"
|
||||
gem "bullet"
|
||||
# gem "bullet"
|
||||
gem "bunny"
|
||||
gem "delayed_job_active_record"
|
||||
gem "delayed_job"
|
||||
|
@ -30,6 +30,9 @@ gem "thin"
|
|||
gem "tzinfo" # For validation of user selected timezone names
|
||||
gem "valid_url"
|
||||
gem "webpack-rails"
|
||||
# Probably safe to remove after next rails upgrade.
|
||||
# https://nvd.nist.gov/vuln/detail/CVE-2018-3741
|
||||
gem "rails-html-sanitizer", "~> 1.0.4"
|
||||
|
||||
group :development, :test do
|
||||
gem "capybara"
|
||||
|
|
|
@ -65,9 +65,6 @@ GEM
|
|||
arel (8.0.0)
|
||||
bcrypt (3.1.11)
|
||||
builder (3.2.3)
|
||||
bullet (5.7.5)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11.0)
|
||||
bunny (2.9.2)
|
||||
amq-protocol (~> 2.3.0)
|
||||
capybara (3.0.1)
|
||||
|
@ -327,7 +324,6 @@ GEM
|
|||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
uniform_notifier (1.11.0)
|
||||
url (0.3.2)
|
||||
useragent (0.16.10)
|
||||
valid_url (0.0.4)
|
||||
|
@ -348,7 +344,6 @@ PLATFORMS
|
|||
|
||||
DEPENDENCIES
|
||||
active_model_serializers
|
||||
bullet
|
||||
bunny
|
||||
capybara
|
||||
codecov
|
||||
|
@ -377,6 +372,7 @@ DEPENDENCIES
|
|||
rack-cors
|
||||
rails
|
||||
rails-erd
|
||||
rails-html-sanitizer (~> 1.0.4)
|
||||
rails_12factor
|
||||
request_store
|
||||
rollbar
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
module Points
|
||||
class Destroy < Mutations::Command
|
||||
STILL_IN_USE = "Could not delete the following item(s): %s. Item(s) are "\
|
||||
"in use by the following sequence(s): %s."
|
||||
STILL_IN_USE = "Could not delete the following item(s): %s. Item(s) are "\
|
||||
"in use by the following sequence(s): %s."
|
||||
JUST_ONE = "Could not delete %s. Item is in use by the following "\
|
||||
"sequence(s): %s."
|
||||
|
||||
required do
|
||||
model :device, class: Device
|
||||
|
@ -15,7 +17,7 @@ module Points
|
|||
|
||||
def validate
|
||||
# Collect names of sequences that still use this point.
|
||||
errors = (tool_seq + point_seq)
|
||||
problems = (tool_seq + point_seq)
|
||||
.group_by(&:sequence_name)
|
||||
.to_a
|
||||
.reduce({S => [], P => []}) do |total, (seq_name, data)|
|
||||
|
@ -24,13 +26,14 @@ module Points
|
|||
total
|
||||
end
|
||||
|
||||
points = errors[P].sort.uniq.join(", ")
|
||||
p = problems[P].sort.uniq.join(", ")
|
||||
|
||||
if points.present?
|
||||
sequences = errors[S].sort.uniq.join(", ")
|
||||
errors = STILL_IN_USE % [points, sequences]
|
||||
if p.present?
|
||||
sequences = problems[S].sort.uniq.join(", ")
|
||||
message = (point_ids.count > 1) ? STILL_IN_USE : JUST_ONE
|
||||
problems = message % [p, sequences]
|
||||
|
||||
add_error :whoops, :in_use, errors
|
||||
add_error :whoops, :in_use, problems
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,7 +69,8 @@ module Points
|
|||
|
||||
def every_tool_id_as_json
|
||||
points
|
||||
.where.not(tool_id: nil)
|
||||
.where
|
||||
.not(tool_id: nil)
|
||||
.pluck(:tool_id)
|
||||
.uniq
|
||||
.map(&:to_json)
|
||||
|
|
|
@ -9,10 +9,10 @@ Bundler.require(:default, Rails.env)
|
|||
module FarmBot
|
||||
class Application < Rails::Application
|
||||
Delayed::Worker.max_attempts = 4
|
||||
config.after_initialize do
|
||||
Bullet.enable = true
|
||||
Bullet.console = true
|
||||
end
|
||||
# config.after_initialize do
|
||||
# Bullet.enable = true
|
||||
# Bullet.console = true
|
||||
# end
|
||||
config.active_job.queue_adapter = :delayed_job
|
||||
config.action_dispatch.perform_deep_munge = false
|
||||
I18n.enforce_available_locales = false
|
||||
|
|
|
@ -11,11 +11,11 @@ FarmBot::Application.configure do
|
|||
config.log_formatter = ::Logger::Formatter.new
|
||||
config.log_level = :info
|
||||
config.public_file_server.enabled = false
|
||||
config.after_initialize do
|
||||
Bullet.enable = true
|
||||
Bullet.console = true
|
||||
Bullet.rollbar = true if ENV["ROLLBAR_ACCESS_TOKEN"]
|
||||
end
|
||||
# config.after_initialize do
|
||||
# Bullet.enable = true
|
||||
# Bullet.console = true
|
||||
# Bullet.rollbar = true if ENV["ROLLBAR_ACCESS_TOKEN"]
|
||||
# end
|
||||
# HACK AHEAD! Here's why:
|
||||
# 1. FarmBot Inc. Uses Sendgrid for email.
|
||||
# 2. FarmBot is an open source project that must be vendor neutral.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
FarmBot::Application.configure do
|
||||
config.after_initialize do
|
||||
Bullet.enable = true
|
||||
Bullet.console = true
|
||||
Bullet.raise = true
|
||||
end
|
||||
# config.after_initialize do
|
||||
# Bullet.enable = true
|
||||
# Bullet.console = true
|
||||
# Bullet.raise = true
|
||||
# end
|
||||
|
||||
# The test environment is used exclusively to run your application's
|
||||
# test suite. You never need to work with it otherwise. Remember that
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
class AddMoreMissingIndexes < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
def up
|
||||
add_index :logs, :verbosity
|
||||
change_column :logs, :verbosity, :integer, default: 1
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
||||
|
|
|
@ -534,7 +534,7 @@ ActiveRecord::Schema.define(version: 20180423202520) do
|
|||
SELECT sequences.id AS sequence_id,
|
||||
( SELECT count(*) AS count
|
||||
FROM edge_nodes
|
||||
WHERE ((edge_nodes.sequence_id = sequences.id) AND ((edge_nodes.kind)::text = 'sequence_id'::text) AND ((edge_nodes.value)::text = (sequences.id)::text))) AS edge_node_count,
|
||||
WHERE (((edge_nodes.kind)::text = 'sequence_id'::text) AND ((edge_nodes.value)::integer = sequences.id))) AS edge_node_count,
|
||||
( SELECT count(*) AS count
|
||||
FROM farm_events
|
||||
WHERE ((farm_events.executable_id = sequences.id) AND ((farm_events.executable_type)::text = 'Sequence'::text))) AS farm_event_count,
|
||||
|
|
|
@ -24,10 +24,10 @@ describe "Point deletion edge cases" do
|
|||
offset: { kind: "coordinate", args: { x: 0, y: 0, z: 0} }
|
||||
},
|
||||
}])
|
||||
result = Points::Destroy.run(point_ids: [tool_slot.id], device: device)
|
||||
errors = result.errors.message_list
|
||||
expected = "Could not delete the following item(s): foo tool. Item(s) are "\
|
||||
"in use by the following sequence(s): sequence."
|
||||
result = Points::Destroy.run(point_ids: [tool_slot.id], device: device)
|
||||
errors = result.errors.message_list
|
||||
expected = "Could not delete foo tool. Item is in use by the following "\
|
||||
"sequence(s): sequence."
|
||||
expect(errors).to include(expected)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,9 +48,8 @@ describe Points::Destroy do
|
|||
point_ids = [s.tool_slot.id]
|
||||
result = Points::Destroy.run(point_ids: point_ids, device: s.device)
|
||||
expect(result.success?).to be(false)
|
||||
expected = "Could not delete the following item(s): Scenario Tool. "\
|
||||
"Item(s) are in use by the following sequence(s): Scenario "\
|
||||
"Sequence."
|
||||
expected = "Could not delete Scenario Tool. Item is in use by the "\
|
||||
"following sequence(s): Scenario Sequence."
|
||||
expect(result.errors.message_list).to include(expected)
|
||||
end
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ export class Move extends React.Component<MoveProps, {}> {
|
|||
</fieldset>
|
||||
|
||||
<p>
|
||||
{t("Swap jog buttons")}
|
||||
{t("Swap jog buttons (and rotate map)")}
|
||||
</p>
|
||||
<fieldset>
|
||||
<label>
|
||||
|
|
|
@ -245,6 +245,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.virtual-bot-trail,
|
||||
.virtual-peripherals {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@ jest.mock("../../api/crud", () => ({
|
|||
edit: jest.fn()
|
||||
}));
|
||||
|
||||
import { movePlant, closePlantInfo } from "../actions";
|
||||
import { movePlant, closePlantInfo, setDragIcon } from "../actions";
|
||||
import { MovePlantProps } from "../interfaces";
|
||||
import { fakePlant } from "../../__test_support__/fake_state/resources";
|
||||
import { edit } from "../../api/crud";
|
||||
import { Actions } from "../../constants";
|
||||
import { DEFAULT_ICON } from "../../open_farm/icons";
|
||||
|
||||
describe("movePlant", () => {
|
||||
beforeEach(() => {
|
||||
|
@ -80,3 +81,23 @@ describe("closePlantInfo()", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setDragIcon()", () => {
|
||||
it("sets the drag icon", () => {
|
||||
const setDragImage = jest.fn();
|
||||
const e = { currentTarget: new Image(), dataTransfer: { setDragImage } };
|
||||
setDragIcon("icon")(e);
|
||||
const img = new Image();
|
||||
img.src = "data:image/svg+xml;utf8,icon";
|
||||
expect(setDragImage).toHaveBeenCalledWith(img, 0, 0);
|
||||
});
|
||||
|
||||
it("sets a default drag icon", () => {
|
||||
const setDragImage = jest.fn();
|
||||
const e = { currentTarget: new Image(), dataTransfer: { setDragImage } };
|
||||
setDragIcon(undefined)(e);
|
||||
const img = new Image();
|
||||
img.src = DEFAULT_ICON;
|
||||
expect(setDragImage).toHaveBeenCalledWith(img, 0, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,11 +27,6 @@ describe("mapStateToProps()", () => {
|
|||
|
||||
it("stepsPerMm is defined", () => {
|
||||
const state = fakeState();
|
||||
state.bot.hardware.informational_settings.firmware_version = "5.0.0R";
|
||||
state.bot.hardware.configuration.steps_per_mm_x = 1;
|
||||
state.bot.hardware.configuration.steps_per_mm_y = 2;
|
||||
expect(mapStateToProps(state).stepsPerMmXY).toEqual({ x: 1, y: 2 });
|
||||
state.bot.hardware.informational_settings.firmware_version = "5.0.6R";
|
||||
state.bot.hardware.mcu_params.movement_step_per_mm_x = 3;
|
||||
state.bot.hardware.mcu_params.movement_step_per_mm_y = 4;
|
||||
expect(mapStateToProps(state).stepsPerMmXY).toEqual({ x: 3, y: 4 });
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { MovePlantProps } from "./interfaces";
|
||||
import { MovePlantProps, DraggableEvent } from "./interfaces";
|
||||
import { defensiveClone } from "../util";
|
||||
import { edit } from "../api/crud";
|
||||
import * as _ from "lodash";
|
||||
import { history, getPathArray } from "../history";
|
||||
import { Actions } from "../constants";
|
||||
import { svgToUrl, DEFAULT_ICON } from "../open_farm/icons";
|
||||
|
||||
export function movePlant(payload: MovePlantProps) {
|
||||
const tr = payload.plant;
|
||||
|
@ -32,3 +33,13 @@ export const closePlantInfo = (dispatch: Function) => () => {
|
|||
history.push("/app/designer/plants");
|
||||
}
|
||||
};
|
||||
|
||||
export const setDragIcon =
|
||||
(icon: string | undefined) => (e: DraggableEvent) => {
|
||||
const dragImg = new Image();
|
||||
dragImg.src = icon ? svgToUrl(icon) : DEFAULT_ICON;
|
||||
const width = dragImg.naturalWidth;
|
||||
const height = dragImg.naturalHeight;
|
||||
e.dataTransfer.setDragImage
|
||||
&& e.dataTransfer.setDragImage(dragImg, width / 2, height / 2);
|
||||
};
|
||||
|
|
|
@ -373,8 +373,7 @@ describe("<GardenPlant/>", () => {
|
|||
expect(eggs.find("Bugs").length).toEqual(1);
|
||||
});
|
||||
|
||||
it(".drop-area: handles drag over", () => {
|
||||
mockPath = "/app/designer/plants/crop_search";
|
||||
const expectHandledDragOver = () => {
|
||||
const wrapper = shallow(<GardenMap {...fakeProps()} />);
|
||||
const e = {
|
||||
dataTransfer: { dropEffect: undefined },
|
||||
|
@ -382,6 +381,17 @@ describe("<GardenPlant/>", () => {
|
|||
};
|
||||
wrapper.find(".drop-area").simulate("dragOver", e);
|
||||
expect(e.dataTransfer.dropEffect).toEqual("move");
|
||||
expect(e.preventDefault).toHaveBeenCalled();
|
||||
};
|
||||
|
||||
it(".drop-area: handles drag over (crop page)", () => {
|
||||
mockPath = "/app/designer/plants/crop_search/mint";
|
||||
expectHandledDragOver();
|
||||
});
|
||||
|
||||
it(".drop-area: handles drag over (click-to-add mode)", () => {
|
||||
mockPath = "/app/designer/plants/crop_search/mint/add";
|
||||
expectHandledDragOver();
|
||||
});
|
||||
|
||||
it(".drop-area: handles drag start", () => {
|
||||
|
|
|
@ -174,6 +174,7 @@ export class GardenMap extends
|
|||
handleDragOver = (e: React.DragEvent<HTMLElement>) => {
|
||||
switch (getMode()) {
|
||||
case Mode.addPlant:
|
||||
case Mode.clickToAdd:
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ describe("<BotTrail/>", () => {
|
|||
const p = fakeProps();
|
||||
p.mapTransformProps.quadrant = 2;
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
const lines = wrapper.find(".virtual-bot-trail").find("line");
|
||||
expect(lines.length).toEqual(4);
|
||||
expect(lines.first().props()).toEqual({
|
||||
id: "trail-line-1",
|
||||
|
@ -44,7 +44,7 @@ describe("<BotTrail/>", () => {
|
|||
it("shows default length trail", () => {
|
||||
sessionStorage[VirtualTrail.length] = undefined;
|
||||
const wrapper = shallow(<BotTrail {...fakeProps()} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
const lines = wrapper.find(".virtual-bot-trail").find("line");
|
||||
expect(lines.length).toEqual(5);
|
||||
});
|
||||
|
||||
|
@ -53,13 +53,13 @@ describe("<BotTrail/>", () => {
|
|||
const p = fakeProps();
|
||||
p.position = { x: 4, y: 4, z: 0 };
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const lines = wrapper.find("#trail").find("line");
|
||||
const lines = wrapper.find(".virtual-bot-trail").find("line");
|
||||
expect(lines.length).toEqual(4);
|
||||
});
|
||||
|
||||
it("shows water", () => {
|
||||
const wrapper = shallow(<BotTrail {...fakeProps()} />);
|
||||
const circles = wrapper.find("#trail").find("circle");
|
||||
const circles = wrapper.find(".virtual-bot-trail").find("circle");
|
||||
expect(circles.length).toEqual(2);
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,7 @@ describe("<BotTrail/>", () => {
|
|||
p.position = { x: 4, y: 4, z: 0 };
|
||||
p.peripherals = [{ label: "water", value: true }];
|
||||
const wrapper = shallow(<BotTrail {...p} />);
|
||||
const water = wrapper.find("#trail").find("circle").last();
|
||||
const water = wrapper.find(".virtual-bot-trail").find("circle").last();
|
||||
expect(water.props().r).toEqual(21);
|
||||
});
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ export function BotTrail(props: BotTrailProps) {
|
|||
|
||||
const array = getNewTrailArray({ coord: { x, y }, water: 0 }, watering);
|
||||
|
||||
return <g id="trail">
|
||||
return <g className="virtual-bot-trail">
|
||||
{array.map((cur: TrailRecord, i: number) => {
|
||||
const prev = (array[i - 1] || { coord: undefined }).coord; // prev coord
|
||||
const opacity = _.round(Math.max(0.25, i / (array.length - 1)), 2);
|
||||
|
|
|
@ -7,6 +7,7 @@ import { history, getPathArray } from "../../history";
|
|||
import { svgToUrl } from "../../open_farm/icons";
|
||||
import { CropLiveSearchResult } from "../interfaces";
|
||||
import { findBySlug } from "../search_selectors";
|
||||
import { setDragIcon } from "../actions";
|
||||
|
||||
export function mapStateToProps(props: Everything): AddPlantProps {
|
||||
return {
|
||||
|
@ -48,11 +49,11 @@ export class AddPlant
|
|||
</p>
|
||||
<div className="panel-header-description">
|
||||
<img className="crop-drag-info-image"
|
||||
src={svgToUrl(result.crop.svg_icon)}
|
||||
alt={t("plant icon")}
|
||||
width={100}
|
||||
height={100}
|
||||
draggable={true}
|
||||
src={svgToUrl(result.crop.svg_icon)} />
|
||||
onDragStart={setDragIcon(result.crop.svg_icon)} />
|
||||
<b>{t("Drag and drop")}</b> {t("the icon onto the map or ")}
|
||||
<b>{t("CLICK anywhere within the grid")}</b> {t(`to add the plant
|
||||
to the map. You can add the plant as many times as you need to
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import * as React from "react";
|
||||
import { t } from "i18next";
|
||||
import * as _ from "lodash";
|
||||
import { DATA_URI, DEFAULT_ICON, svgToUrl } from "../../open_farm/icons";
|
||||
import { CropInfoProps, DraggableEvent } from "../interfaces";
|
||||
import { svgToUrl } from "../../open_farm/icons";
|
||||
import { CropInfoProps } from "../interfaces";
|
||||
import { history, getPathArray } from "../../history";
|
||||
import { connect } from "react-redux";
|
||||
import { findBySlug } from "../search_selectors";
|
||||
import { Everything } from "../../interfaces";
|
||||
import { OpenFarm } from "../openfarm";
|
||||
import { OFSearch } from "../util";
|
||||
import { unselectPlant } from "../actions";
|
||||
import { unselectPlant, setDragIcon } from "../actions";
|
||||
|
||||
interface InforFieldProps {
|
||||
title: string;
|
||||
|
@ -49,20 +49,6 @@ export class CropInfo extends React.Component<CropInfoProps, {}> {
|
|||
unselectPlant(this.props.dispatch)();
|
||||
}
|
||||
|
||||
handleDragStart = (e: DraggableEvent) => {
|
||||
const icon = e.currentTarget.getAttribute("data-icon-url");
|
||||
const img = document.createElement("img");
|
||||
icon ? img.src = DATA_URI + icon : DEFAULT_ICON;
|
||||
|
||||
// TODO: Setting these doesn't work by default, needs a fix
|
||||
// https://www.w3.org/TR/2011/WD-html5-20110405
|
||||
// /dnd.html#dom-datatransfer-setdragimage
|
||||
img.height = 50;
|
||||
img.width = 50;
|
||||
|
||||
e.dataTransfer.setDragImage && e.dataTransfer.setDragImage(img, 50, 50);
|
||||
}
|
||||
|
||||
render() {
|
||||
const crop = getPathArray()[5];
|
||||
const result =
|
||||
|
@ -93,10 +79,8 @@ export class CropInfo extends React.Component<CropInfoProps, {}> {
|
|||
<div className="panel-content">
|
||||
<div className="crop-drag-info-tile">
|
||||
<img className="crop-drag-info-image"
|
||||
onDragStart={this.handleDragStart}
|
||||
draggable={true}
|
||||
src={result.image}
|
||||
data-icon-url={result.crop.svg_icon} />
|
||||
onDragStart={setDragIcon(result.crop.svg_icon)} />
|
||||
<div className="crop-info-overlay">
|
||||
{t("Drag and drop into map")}
|
||||
</div>
|
||||
|
@ -137,7 +121,8 @@ export class CropInfo extends React.Component<CropInfoProps, {}> {
|
|||
<img
|
||||
src={svgToUrl(value)}
|
||||
width={100}
|
||||
height={100} />
|
||||
height={100}
|
||||
onDragStart={setDragIcon(value)} />
|
||||
</div>
|
||||
:
|
||||
<span>
|
||||
|
|
|
@ -9,10 +9,8 @@ import {
|
|||
selectAllPeripherals,
|
||||
getFirmwareConfig
|
||||
} from "../resources/selectors";
|
||||
import { StepsPerMmXY } from "../devices/interfaces";
|
||||
import { isNumber } from "lodash";
|
||||
import * as _ from "lodash";
|
||||
import { minFwVersionCheck, validBotLocationData, validFwConfig } from "../util";
|
||||
import { validBotLocationData, validFwConfig } from "../util";
|
||||
import { getWebAppConfigValue } from "../config_storage/actions";
|
||||
|
||||
export function mapStateToProps(props: Everything) {
|
||||
|
@ -26,27 +24,10 @@ export function mapStateToProps(props: Everything) {
|
|||
const hoveredPlant = plants.filter(x => x.uuid === plantUUID)[0];
|
||||
|
||||
const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index));
|
||||
const {
|
||||
mcu_params, configuration, informational_settings
|
||||
} = props.bot.hardware;
|
||||
const { mcu_params } = props.bot.hardware;
|
||||
const firmwareSettings = fwConfig || mcu_params;
|
||||
|
||||
function stepsPerMmXY(): StepsPerMmXY {
|
||||
const { steps_per_mm_x, steps_per_mm_y } = configuration;
|
||||
const { firmware_version } = informational_settings;
|
||||
const { movement_step_per_mm_x, movement_step_per_mm_y } = firmwareSettings;
|
||||
const stepsPerMm = () => {
|
||||
if (minFwVersionCheck(firmware_version, "5.0.5")) {
|
||||
return { x: movement_step_per_mm_x, y: movement_step_per_mm_y };
|
||||
} else {
|
||||
return { x: steps_per_mm_x, y: steps_per_mm_y };
|
||||
}
|
||||
};
|
||||
if (isNumber(stepsPerMm().x) && isNumber(stepsPerMm().y)) {
|
||||
return stepsPerMm();
|
||||
}
|
||||
return { x: undefined, y: undefined };
|
||||
}
|
||||
const { movement_step_per_mm_x, movement_step_per_mm_y } = firmwareSettings;
|
||||
|
||||
const peripherals = _.uniq(selectAllPeripherals(props.resources.index))
|
||||
.map(x => {
|
||||
|
@ -87,7 +68,7 @@ export function mapStateToProps(props: Everything) {
|
|||
plants,
|
||||
botLocationData: validBotLocationData(props.bot.hardware.location_data),
|
||||
botMcuParams: firmwareSettings,
|
||||
stepsPerMmXY: stepsPerMmXY(),
|
||||
stepsPerMmXY: { x: movement_step_per_mm_x, y: movement_step_per_mm_y },
|
||||
peripherals,
|
||||
eStopStatus: props.bot.hardware.informational_settings.locked,
|
||||
latestImages,
|
||||
|
|
|
@ -51,6 +51,7 @@ export const NavLinks = (props: NavLinksProps) => {
|
|||
to={fn(link.slug, childPath)}
|
||||
className={`${isActive}`}
|
||||
key={link.slug}
|
||||
draggable={false}
|
||||
onClick={props.close("mobileMenuOpen")}>
|
||||
<i className={`fa fa-${link.icon}`} />
|
||||
{t(link.name)}
|
||||
|
|
Loading…
Reference in New Issue