From 86d1cabf49774099b6c07b7609fb28364bea6f3c Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Tue, 10 Oct 2017 21:29:12 -0700 Subject: [PATCH 1/5] farmware forms --- webpack/devices/interfaces.ts | 1 + .../__tests__/farmware_forms_test.tsx | 29 +++++++-- webpack/farmware/__tests__/farmware_test.tsx | 1 + webpack/farmware/farmware_forms.tsx | 62 ++++++++++++++----- webpack/farmware/index.tsx | 5 +- webpack/farmware/state_to_props.ts | 1 + .../__tests__/weed_detector_test.tsx | 1 + 7 files changed, 77 insertions(+), 23 deletions(-) diff --git a/webpack/devices/interfaces.ts b/webpack/devices/interfaces.ts index bbdd70ca6..c81b36cd4 100644 --- a/webpack/devices/interfaces.ts +++ b/webpack/devices/interfaces.ts @@ -136,6 +136,7 @@ export interface PeripheralsProps { export interface FarmwareProps { dispatch: Function; env: Partial; + user_env: Record; images: TaggedImage[]; currentImage: TaggedImage | undefined; syncStatus: SyncStatus; diff --git a/webpack/farmware/__tests__/farmware_forms_test.tsx b/webpack/farmware/__tests__/farmware_forms_test.tsx index 1b2a0ba05..99ed646f1 100644 --- a/webpack/farmware/__tests__/farmware_forms_test.tsx +++ b/webpack/farmware/__tests__/farmware_forms_test.tsx @@ -21,7 +21,7 @@ function fakeFarmwares(): Dictionary { args: ["my_farmware.fth"], url: "https://", path: "my_farmware", - config: [], + config: [{ name: "config_1", label: "Config 1", value: "4" }], meta: { min_os_version_major: "3", description: "Does things.", @@ -35,19 +35,40 @@ function fakeFarmwares(): Dictionary { } describe("", () => { + it("doesn't render", () => { + const farmwares = fakeFarmwares(); + const farmware = farmwares.farmware_0; + if (farmware) { farmware.config = []; } + const wrapper = mount(); + expect(wrapper.text()).toEqual(""); + }); + it("renders", () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.text()).toContain("My Farmware"); expect(wrapper.text()).toContain("version: 0.0.0"); expect(wrapper.text()).toContain("Does things."); + expect(wrapper.find("label").last().text()).toContain("Config 1"); + expect(wrapper.find("input").props().value).toEqual("4"); }); it("runs", () => { const runFarmware = getDevice().execScript as jest.Mock<{}>; - const wrapper = mount(); + const wrapper = mount(); const run = wrapper.find("button").first(); run.simulate("click"); expect(run.text()).toEqual("Run"); - expect(runFarmware).toHaveBeenCalledWith("My Farmware"); + const argsList = runFarmware.mock.calls[0]; + expect(argsList[0]).toEqual("My Farmware"); + const pairs = argsList[1][0]; + expect(pairs.kind).toEqual("pair"); + expect(pairs.args) + .toEqual({ "label": "my_farmware_config_1", "value": "4" }); }); }); diff --git a/webpack/farmware/__tests__/farmware_test.tsx b/webpack/farmware/__tests__/farmware_test.tsx index 66f1e1c86..0e50ace86 100644 --- a/webpack/farmware/__tests__/farmware_test.tsx +++ b/webpack/farmware/__tests__/farmware_test.tsx @@ -19,6 +19,7 @@ describe("", () => { farmwares: {}, syncStatus: "unknown", env: {}, + user_env: {}, dispatch: jest.fn(), currentImage: undefined, images: [] diff --git a/webpack/farmware/farmware_forms.tsx b/webpack/farmware/farmware_forms.tsx index fe6b4349b..b1ad2c4aa 100644 --- a/webpack/farmware/farmware_forms.tsx +++ b/webpack/farmware/farmware_forms.tsx @@ -1,44 +1,63 @@ import * as React from "react"; -import { Widget, WidgetHeader, WidgetBody, Col } from "../ui/index"; +import { + Widget, WidgetHeader, WidgetBody, Col, BlurableInput +} from "../ui/index"; import { t } from "i18next"; -import { FarmwareManifest, Dictionary } from "farmbot"; +import { FarmwareManifest, Dictionary, Pair, FarmwareConfig } from "farmbot"; import { betterCompact } from "../util"; import { getDevice } from "../device"; +import * as _ from "lodash"; interface FarmwareFormsProps { farmwares: Dictionary; + user_env: Record; } -// TODO: download and parse the "manifest.json" file instead. -const firstParty = [ - "camera-calibration", - "historical-camera-calibration", - "take-photo", - "plant-detection", - "historical-plant-detection"]; - export function FarmwareForms(props: FarmwareFormsProps): JSX.Element { - const { farmwares } = props; + + function inputChange(key: string, e: React.SyntheticEvent) { + const value = e.currentTarget.value; + getDevice().setUserEnv({ [key]: value }); + } + + function getEnvName(farmwareName: string, configName: string) { + return `${_.snakeCase(farmwareName)}_${configName}`; + } + + function getValue(farmwareName: string, currentConfig: FarmwareConfig) { + return (user_env[getEnvName(farmwareName, currentConfig.name)] + || _.toString(currentConfig.value)); + } + + function run(farmwareName: string, config: FarmwareConfig[]) { + const pairs = config.map((x) => { + const label = getEnvName(farmwareName, x.name); + const value = getValue(farmwareName, x); + return { kind: "pair", args: { value, label } }; + }); + getDevice().execScript(farmwareName, pairs); + } + + const { farmwares, user_env } = props; const farmwareData = betterCompact(Object .keys(farmwares) .map(x => farmwares[x])) .map((fw) => { - // TODO: Add optional "config" field to farmbot-js and check for it here. - const needsWidget = !firstParty.includes(fw.name); + const needsWidget = fw.config && fw.config.length > 0; return needsWidget ? fw : undefined; }); return
{farmwareData.map((farmware, i) => { return farmware ? - + @@ -47,8 +66,17 @@ export function FarmwareForms(props: FarmwareFormsProps): JSX.Element {

{farmware.meta.description}

+
} - {/* TODO: Render inputs described in "farmware.config". */} + {farmware.config.map((config) => { + return
+ + + inputChange(getEnvName(farmware.name, config.name), e)} + value={getValue(farmware.name, config)} /> +
; + })}
:
; diff --git a/webpack/farmware/index.tsx b/webpack/farmware/index.tsx index 14427bfca..3ae9ce954 100644 --- a/webpack/farmware/index.tsx +++ b/webpack/farmware/index.tsx @@ -8,7 +8,7 @@ import { CameraCalibration } from "./camera_calibration/camera_calibration"; import { FarmwareProps } from "../devices/interfaces"; import { WeedDetector } from "./weed_detector/index"; import { envGet } from "./weed_detector/remote_env/selectors"; -// import { FarmwareForms } from "./farmware_forms"; +import { FarmwareForms } from "./farmware_forms"; @connect(mapStateToProps) export class FarmwarePage extends React.Component { @@ -48,7 +48,8 @@ export class FarmwarePage extends React.Component { - {/* */} + ; } } diff --git a/webpack/farmware/state_to_props.ts b/webpack/farmware/state_to_props.ts index 6660ebf4e..2f6eee107 100644 --- a/webpack/farmware/state_to_props.ts +++ b/webpack/farmware/state_to_props.ts @@ -24,6 +24,7 @@ export function mapStateToProps(props: Everything): FarmwareProps { farmwares, syncStatus, env: prepopulateEnv(props.bot.hardware.user_env), + user_env: props.bot.hardware.user_env, dispatch: props.dispatch, currentImage, images diff --git a/webpack/farmware/weed_detector/__tests__/weed_detector_test.tsx b/webpack/farmware/weed_detector/__tests__/weed_detector_test.tsx index 13da7ad62..40d814715 100644 --- a/webpack/farmware/weed_detector/__tests__/weed_detector_test.tsx +++ b/webpack/farmware/weed_detector/__tests__/weed_detector_test.tsx @@ -13,6 +13,7 @@ describe("", () => { farmwares: {}, syncStatus: "unknown", env: {}, + user_env: {}, dispatch: jest.fn(), currentImage: undefined, images: [] From cdff9a8f89ae4727399aec7c20c83fe12326d016 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Tue, 10 Oct 2017 21:29:55 -0700 Subject: [PATCH 2/5] update ruby version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4fc61a98..c0a096f57 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You will need the following: 1. A Linux or Mac based machine. We do not support windows at this time. 0. [Docker 17.06.0-ce or greater](https://docs.docker.com/engine/installation/) - 0. [Ruby 2.4.1](http://rvm.io/rvm/install) + 0. [Ruby 2.4.2](http://rvm.io/rvm/install) 0. [ImageMagick](https://www.imagemagick.org/script/index.php) (`brew install imagemagick` (Mac) or `sudo apt-get install imagemagick` (Ubuntu)) 0. [Node JS > v6](https://nodejs.org/en/download/) 0. [`libpq-dev` and `postgresql`](http://stackoverflow.com/questions/6040583/cant-find-the-libpq-fe-h-header-when-trying-to-install-pg-gem/6040822#6040822) and `postgresql-contrib` From dacd3a5a672e416ac5315bce54817e6dd294758b Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Tue, 10 Oct 2017 21:43:31 -0700 Subject: [PATCH 3/5] fix default lab settings values --- webpack/__tests__/loading_plant_test.tsx | 3 ++- webpack/account/labs/labs_features_list_data.ts | 15 +++++++++------ webpack/account/labs/labs_features_list_ui.tsx | 3 ++- .../map/__tests__/garden_plant_test.tsx | 3 ++- webpack/farm_designer/map/garden_plant.tsx | 2 +- .../map/layers/__tests__/plant_layer_test.tsx | 2 +- .../map/layers/hovered_plant_layer.tsx | 2 +- webpack/farm_designer/map/layers/spread_layer.tsx | 2 +- webpack/loading_plant.tsx | 2 +- webpack/session_keys.ts | 2 +- 10 files changed, 21 insertions(+), 15 deletions(-) diff --git a/webpack/__tests__/loading_plant_test.tsx b/webpack/__tests__/loading_plant_test.tsx index ff4295360..da9fc76a7 100644 --- a/webpack/__tests__/loading_plant_test.tsx +++ b/webpack/__tests__/loading_plant_test.tsx @@ -19,6 +19,7 @@ import { BooleanSetting } from "../session_keys"; describe("", () => { it("renders loading text", () => { + mockStorj[BooleanSetting.disableAnimations] = true; const wrapper = shallow(); expect(wrapper.find(".loading-plant").length).toEqual(0); expect(wrapper.find(".loading-plant-text").props().y).toEqual(150); @@ -27,7 +28,7 @@ describe("", () => { }); it("renders loading animation", () => { - mockStorj[BooleanSetting.plantAnimations] = true; + mockStorj[BooleanSetting.disableAnimations] = false; const wrapper = shallow(); expect(wrapper.find(".loading-plant")).toBeTruthy(); const circleProps = wrapper.find(".loading-plant-circle").props(); diff --git a/webpack/account/labs/labs_features_list_data.ts b/webpack/account/labs/labs_features_list_data.ts index 42b90d2c9..fceb9480e 100644 --- a/webpack/account/labs/labs_features_list_data.ts +++ b/webpack/account/labs/labs_features_list_data.ts @@ -11,14 +11,16 @@ export interface LabsFeature { storageKey: BooleanSetting; value: boolean; experimental?: boolean; + displayInvert?: boolean; } export const fetchLabFeatures = (): LabsFeature[] => ([ { - name: t("Disable Web App internationalization"), - description: t("Set Web App to English."), + name: t("Internationalize Web App"), + description: t("Turn off to set Web App to English."), storageKey: BooleanSetting.disableI18n, - value: false + value: false, + displayInvert: true }, { name: t("Confirm Sequence step deletion"), @@ -51,9 +53,10 @@ export const fetchLabFeatures = (): LabsFeature[] => ([ }, { name: t("Display plant animations"), - description: trim(t(`Turn on plant animations in the Farm Designer.`)), - storageKey: BooleanSetting.plantAnimations, - value: true + description: trim(t(`Enable plant animations in the Farm Designer.`)), + storageKey: BooleanSetting.disableAnimations, + value: false, + displayInvert: true } ].map(fetchRealValue)); diff --git a/webpack/account/labs/labs_features_list_ui.tsx b/webpack/account/labs/labs_features_list_ui.tsx index dce8acea0..b2e5a4856 100644 --- a/webpack/account/labs/labs_features_list_ui.tsx +++ b/webpack/account/labs/labs_features_list_ui.tsx @@ -9,11 +9,12 @@ interface LabsFeaturesListProps { export function LabsFeaturesList(props: LabsFeaturesListProps) { return
{fetchLabFeatures().map((p, i) => { + const displayValue = p.displayInvert ? !p.value : p.value; return props.onToggle(p)} disabled={false} />; diff --git a/webpack/farm_designer/map/__tests__/garden_plant_test.tsx b/webpack/farm_designer/map/__tests__/garden_plant_test.tsx index 750d49294..5139b7a1c 100644 --- a/webpack/farm_designer/map/__tests__/garden_plant_test.tsx +++ b/webpack/farm_designer/map/__tests__/garden_plant_test.tsx @@ -36,6 +36,7 @@ describe("", () => { } it("renders plant", () => { + mockStorj[BooleanSetting.disableAnimations] = true; const wrapper = shallow(); expect(wrapper.find("image").length).toEqual(1); expect(wrapper.find("image").props().opacity).toEqual(1); @@ -46,7 +47,7 @@ describe("", () => { }); it("renders plant animations", () => { - mockStorj[BooleanSetting.plantAnimations] = true; + mockStorj[BooleanSetting.disableAnimations] = false; const wrapper = shallow(); expect(wrapper.find(".soil-cloud").length).toEqual(1); expect(wrapper.find(".animate").length).toEqual(1); diff --git a/webpack/farm_designer/map/garden_plant.tsx b/webpack/farm_designer/map/garden_plant.tsx index 2298d2071..9929ef0c6 100644 --- a/webpack/farm_designer/map/garden_plant.tsx +++ b/webpack/farm_designer/map/garden_plant.tsx @@ -37,7 +37,7 @@ export class GardenPlant extends const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize); const alpha = dragging ? 0.4 : 1.0; - const animate = Session.getBool(BooleanSetting.plantAnimations); + const animate = !Session.getBool(BooleanSetting.disableAnimations); return diff --git a/webpack/farm_designer/map/layers/__tests__/plant_layer_test.tsx b/webpack/farm_designer/map/layers/__tests__/plant_layer_test.tsx index fae1eb579..af0e8dacd 100644 --- a/webpack/farm_designer/map/layers/__tests__/plant_layer_test.tsx +++ b/webpack/farm_designer/map/layers/__tests__/plant_layer_test.tsx @@ -1,7 +1,7 @@ jest.mock("../../../../session", () => { return { Session: { - getBool: () => { return true; } + getBool: () => { return false; } } }; }); diff --git a/webpack/farm_designer/map/layers/hovered_plant_layer.tsx b/webpack/farm_designer/map/layers/hovered_plant_layer.tsx index efe6ad937..ec2f6bf83 100644 --- a/webpack/farm_designer/map/layers/hovered_plant_layer.tsx +++ b/webpack/farm_designer/map/layers/hovered_plant_layer.tsx @@ -47,7 +47,7 @@ export class HoveredPlantLayer extends const hovered = !!this.props.designer.hoveredPlant.icon; const scaledRadius = currentPlant ? radius : radius * 1.2; const alpha = dragging ? 0.4 : 1.0; - const animate = Session.getBool(BooleanSetting.plantAnimations); + const animate = !Session.getBool(BooleanSetting.disableAnimations); return {this.props.visible && hovered && diff --git a/webpack/farm_designer/map/layers/spread_layer.tsx b/webpack/farm_designer/map/layers/spread_layer.tsx index 870e01bd8..d105bf67b 100644 --- a/webpack/farm_designer/map/layers/spread_layer.tsx +++ b/webpack/farm_designer/map/layers/spread_layer.tsx @@ -83,7 +83,7 @@ export class SpreadCircle extends const { selected, mapTransformProps } = this.props; const { quadrant, gridSize } = mapTransformProps; const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize); - const animate = Session.getBool(BooleanSetting.plantAnimations); + const animate = !Session.getBool(BooleanSetting.disableAnimations); return {!selected && diff --git a/webpack/loading_plant.tsx b/webpack/loading_plant.tsx index f58977f01..cf98a6cc5 100644 --- a/webpack/loading_plant.tsx +++ b/webpack/loading_plant.tsx @@ -3,7 +3,7 @@ import { Session } from "./session"; import { BooleanSetting } from "./session_keys"; export function LoadingPlant() { - const animations = Session.getBool(BooleanSetting.plantAnimations); + const animations = !Session.getBool(BooleanSetting.disableAnimations); return
{animations && diff --git a/webpack/session_keys.ts b/webpack/session_keys.ts index cc64e09f7..ecf89bbe8 100644 --- a/webpack/session_keys.ts +++ b/webpack/session_keys.ts @@ -17,7 +17,7 @@ export enum BooleanSetting { hideWebcamWidget = "hideWebcamWidget", dynamicMap = "dynamicMap", mapXL = "mapXL", - plantAnimations = "plantAnimations", + disableAnimations = "disableAnimations", } export enum NumericSetting { From 91aecc3c6eac9ed3f3ff1db6b26fcc4806533dd7 Mon Sep 17 00:00:00 2001 From: gabrielburnworth Date: Tue, 10 Oct 2017 21:49:06 -0700 Subject: [PATCH 4/5] ensure correct hue slider value order --- webpack/farmware/weed_detector/image_workspace.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack/farmware/weed_detector/image_workspace.tsx b/webpack/farmware/weed_detector/image_workspace.tsx index 716862411..c26d0de8e 100644 --- a/webpack/farmware/weed_detector/image_workspace.tsx +++ b/webpack/farmware/weed_detector/image_workspace.tsx @@ -84,8 +84,8 @@ export class ImageWorkspace extends React.Component { onRelease={this.onHslChange("H")} lowest={RANGES.H.LOWEST} highest={RANGES.H.HIGHEST} - lowValue={H_LO} - highValue={H_HI} /> + lowValue={Math.min(H_LO, H_HI)} + highValue={Math.max(H_LO, H_HI)} /> Date: Tue, 10 Oct 2017 22:12:57 -0700 Subject: [PATCH 5/5] improve small window sequence/regimen page views --- webpack/css/regimens.scss | 4 ++++ webpack/css/sequences.scss | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/webpack/css/regimens.scss b/webpack/css/regimens.scss index 00b9c9d8a..00767954a 100644 --- a/webpack/css/regimens.scss +++ b/webpack/css/regimens.scss @@ -10,6 +10,10 @@ margin-bottom: 1rem; } } + @media screen and (max-width: 974px) { + margin-left: 15px; + margin-right: 15px; + } } // Regimen Editor diff --git a/webpack/css/sequences.scss b/webpack/css/sequences.scss index a44162322..b5ed297cb 100644 --- a/webpack/css/sequences.scss +++ b/webpack/css/sequences.scss @@ -13,20 +13,34 @@ margin-top: 0.4rem; } @media screen and (max-width: 974px) { + h3, p { + margin-left: 15px; + margin-right: 15px; + } h3 { margin-bottom: 2.5rem; } + .button-group { + margin-right: 15px; + } } } .sequence-editor-content, .regimen-editor-content { margin-right: -15px; + @media screen and (max-width: 974px) { + margin-left: 15px; + margin-right: 0; + } } .sequence-editor-tools, .regimen-editor-tools { margin-right: 15px; + @media screen and (max-width: 974px) { + margin-right: 10px; + } } .sequence, @@ -47,7 +61,7 @@ .regimen-list { overflow-y: auto; overflow-x: hidden; - height: calc(100vh - 21rem); + max-height: calc(100vh - 21rem); } .step-button-cluster, @@ -55,6 +69,20 @@ margin-right: -15px; } +.step-button-cluster-panel { + @media screen and (max-width: 974px) { + margin-left: 15px; + margin-right: 15px; + } +} + +.step-button-cluster { + @media screen and (max-width: 974px) { + margin-left: 0; + margin-right: 0; + } +} + .sequence-list-items { margin-right: 15px; } @@ -64,6 +92,10 @@ padding-top: 0.4rem; margin-bottom: 3rem; margin-right: 5px; + @media screen and (max-width: 974px) { + margin-left: 15px; + margin-right: 15px; + } } .sequence-list-panel input,