Added weed_detector to WebAppConfigs table. 50% done converting `keyof`s

pull/608/head
Rick Carlino 2018-01-09 01:24:59 -06:00
parent 584ef721e2
commit 9d41909632
28 changed files with 176 additions and 157 deletions

View File

@ -0,0 +1,8 @@
class AddWeedDetectorToWebAppConfig < ActiveRecord::Migration[5.1]
def change
add_column :web_app_configs,
:weed_detector,
:boolean,
default: false
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180105175215) do
ActiveRecord::Schema.define(version: 20180109070610) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -354,6 +354,7 @@ ActiveRecord::Schema.define(version: 20180105175215) do
t.integer "fun_log", default: 1
t.integer "debug_log", default: 1
t.integer "successs_log", default: 1
t.boolean "weed_detector", default: false
t.index ["device_id"], name: "index_web_app_configs_on_device_id"
end

View File

@ -19,7 +19,7 @@ import { BooleanSetting } from "../session_keys";
describe("<LoadingPlant/>", () => {
it("renders loading text", () => {
mockStorj[BooleanSetting.disableAnimations] = true;
mockStorj[BooleanSetting.disable_animations] = true;
const wrapper = shallow(<LoadingPlant />);
expect(wrapper.find(".loading-plant").length).toEqual(0);
expect(wrapper.find(".loading-plant-text").props().y).toEqual(150);
@ -28,7 +28,7 @@ describe("<LoadingPlant/>", () => {
});
it("renders loading animation", () => {
mockStorj[BooleanSetting.disableAnimations] = false;
mockStorj[BooleanSetting.disable_animations] = false;
const wrapper = shallow(<LoadingPlant />);
expect(wrapper.find(".loading-plant")).toBeTruthy();
const circleProps = wrapper.find(".loading-plant-circle").props();

View File

@ -26,7 +26,7 @@ describe("maybeToggleFeature()", () => {
name: "Example",
value: false,
description: "I stub this.",
storageKey: BooleanSetting.weedDetector,
storageKey: BooleanSetting.weed_detector,
experimental: true
};
const out = maybeToggleFeature(data);
@ -39,9 +39,9 @@ describe("maybeToggleFeature()", () => {
(global as any).confirm = () => true;
const data: LabsFeature = {
name: "Example1",
value: (mockStorj[BooleanSetting.weedDetector] = false),
value: (mockStorj[BooleanSetting.weed_detector] = false),
description: "I stub this.",
storageKey: BooleanSetting.weedDetector,
storageKey: BooleanSetting.weed_detector,
experimental: true
};
const out = maybeToggleFeature(data);
@ -55,9 +55,9 @@ describe("maybeToggleFeature()", () => {
Object.defineProperty(global, "confirm", conf);
const output = maybeToggleFeature({
name: "Example",
value: (mockStorj[BooleanSetting.weedDetector] = true),
value: (mockStorj[BooleanSetting.weed_detector] = true),
description: "I stub this.",
storageKey: BooleanSetting.weedDetector,
storageKey: BooleanSetting.weed_detector,
experimental: true
});
expect(conf).not.toHaveBeenCalled();
@ -68,9 +68,9 @@ describe("maybeToggleFeature()", () => {
it("updates a `LabsFeature` when consent is not required", () => {
const data: LabsFeature = {
name: "Example1",
value: (mockStorj[BooleanSetting.weedDetector] = false),
value: (mockStorj[BooleanSetting.weed_detector] = false),
description: "I stub this.",
storageKey: BooleanSetting.weedDetector
storageKey: BooleanSetting.weed_detector
};
const out = maybeToggleFeature(data);
out ?

View File

@ -1,14 +1,15 @@
import { Content } from "../../constants";
import { Session } from "../../session";
import { BooleanSetting } from "../../session_keys";
import { trim } from "../../util";
import { t } from "i18next";
import { BooleanConfigKey } from "../../config_storage/web_app_configs";
import { BooleanSetting } from "../../session_keys";
export interface LabsFeature {
name: string;
description: string;
/** Entry for localStorage. Must be unique. */
storageKey: BooleanSetting;
storageKey: BooleanConfigKey;
value: boolean;
experimental?: boolean;
displayInvert?: boolean;
@ -21,7 +22,7 @@ export const fetchLabFeatures = (): LabsFeature[] => ([
{
name: t("Internationalize Web App"),
description: t("Turn off to set Web App to English."),
storageKey: BooleanSetting.disableI18n,
storageKey: BooleanSetting.disable_i18n,
value: false,
displayInvert: true,
callback: () => window.location.reload()
@ -30,14 +31,14 @@ export const fetchLabFeatures = (): LabsFeature[] => ([
name: t("Confirm Sequence step deletion"),
description: trim(t(`Show a confirmation dialog when the sequence delete step
icon is pressed.`)),
storageKey: BooleanSetting.confirmStepDeletion,
storageKey: BooleanSetting.confirm_step_deletion,
value: false
},
{
name: t("Hide Webcam widget"),
description: trim(t(`If not using a webcam, use this setting to remove the
widget from the Controls page.`)),
storageKey: BooleanSetting.hideWebcamWidget,
storageKey: BooleanSetting.hide_webcam_widget,
value: false
},
{
@ -45,20 +46,20 @@ export const fetchLabFeatures = (): LabsFeature[] => ([
description: trim(t(`Change the Farm Designer map size based on axis length.
A value must be input in AXIS LENGTH and STOP AT MAX must be enabled in
the HARDWARE widget.`)),
storageKey: BooleanSetting.dynamicMap,
storageKey: BooleanSetting.dynamic_map,
value: false
},
{
name: t("Double default map dimensions"),
description: trim(t(`Double the default dimensions of the Farm Designer map
for a map with four times the area.`)),
storageKey: BooleanSetting.mapXL,
storageKey: BooleanSetting.map_xl,
value: false
},
{
name: t("Display plant animations"),
description: trim(t(`Enable plant animations in the Farm Designer.`)),
storageKey: BooleanSetting.disableAnimations,
storageKey: BooleanSetting.disable_animations,
value: false,
displayInvert: true
}
@ -75,7 +76,7 @@ export const maybeToggleFeature =
};
/** Stub this when testing if need be. */
const fetchVal = (k: BooleanSetting) => !!Session.getBool(k);
const fetchVal = (k: BooleanConfigKey) => !!Session.getBool(k);
/** Takes a `LabFeature` (probably one with an uninitialized fallback / default
* value) and sets it to the _real_ value that's in localStorage. */

View File

@ -38,6 +38,7 @@ export interface WebAppConfig {
fun_log: number;
debug_log: number;
successs_log: number;
weed_detector: boolean;
}
export type NumberConfigKey = "id"
@ -73,4 +74,5 @@ export type BooleanConfigKey = "confirm_step_deletion"
|"show_points"
|"x_axis_inverted"
|"y_axis_inverted"
|"z_axis_inverted";
|"z_axis_inverted"
|"weed_detector";

View File

@ -38,7 +38,7 @@ describe("<Controls />", () => {
}
it("shows webcam widget", () => {
mockStorj[BooleanSetting.hideWebcamWidget] = false;
mockStorj[BooleanSetting.hide_webcam_widget] = false;
const wrapper = mount(<Controls {...fakeProps() } />);
const txt = wrapper.text().toLowerCase();
["webcam", "move", "peripherals"]
@ -46,7 +46,7 @@ describe("<Controls />", () => {
});
it("hides webcam widget", () => {
mockStorj[BooleanSetting.hideWebcamWidget] = true;
mockStorj[BooleanSetting.hide_webcam_widget] = true;
const wrapper = mount(<Controls {...fakeProps() } />);
const txt = wrapper.text().toLowerCase();
["move", "peripherals"].map(string => expect(txt).toContain(string));

View File

@ -20,7 +20,7 @@ export class Controls extends React.Component<Props, {}> {
.hardware
.informational_settings
.busy;
const showWebcamWidget = !Session.getBool(BooleanSetting.hideWebcamWidget);
const showWebcamWidget = !Session.getBool(BooleanSetting.hide_webcam_widget);
return <Page className="controls">
{showWebcamWidget
?

View File

@ -9,6 +9,7 @@ import { maybeNegateStatus, maybeNegateConsistency } from "../connectivity/maybe
import { EdgeStatus } from "../connectivity/interfaces";
import { ReduxAction } from "../redux/interfaces";
import { connectivityReducer } from "../connectivity/reducer";
import { BooleanConfigKey } from "../config_storage/web_app_configs";
const afterEach = (state: BotState, a: ReduxAction<{}>) => {
state.connectivity = connectivityReducer(state.connectivity, a);
@ -85,13 +86,13 @@ export let initialState = (): BotState => ({
currentOSVersion: undefined,
currentFWVersion: undefined,
axis_inversion: {
x: !!Session.getBool(BooleanSetting.xAxisInverted),
y: !!Session.getBool(BooleanSetting.yAxisInverted),
z: !!Session.getBool(BooleanSetting.zAxisInverted),
x: !!Session.getBool(BooleanSetting.x_axis_inverted),
y: !!Session.getBool(BooleanSetting.y_axis_inverted),
z: !!Session.getBool(BooleanSetting.z_axis_inverted),
},
encoder_visibility: {
raw_encoders: !!Session.getBool(BooleanSetting.rawEncoders),
scaled_encoders: !!Session.getBool(BooleanSetting.scaledEncoders),
raw_encoders: !!Session.getBool(BooleanSetting.raw_encoders),
scaled_encoders: !!Session.getBool(BooleanSetting.scaled_encoders),
},
connectivity: {
"bot.mqtt": undefined,
@ -101,17 +102,17 @@ export let initialState = (): BotState => ({
});
/** Translate X/Y/Z to the name that is used in `localStorage` */
export const INVERSION_MAPPING: Record<Xyz, BooleanSetting> = {
x: BooleanSetting.xAxisInverted,
y: BooleanSetting.yAxisInverted,
z: BooleanSetting.zAxisInverted,
export const INVERSION_MAPPING: Record<Xyz, BooleanConfigKey> = {
x: BooleanSetting.x_axis_inverted,
y: BooleanSetting.y_axis_inverted,
z: BooleanSetting.z_axis_inverted,
};
/** Translate `encode_visibility` key name to the name that is
* used in `localStorage` */
export const ENCODER_MAPPING: Record<EncoderDisplay, BooleanSetting> = {
raw_encoders: BooleanSetting.rawEncoders,
scaled_encoders: BooleanSetting.scaledEncoders,
export const ENCODER_MAPPING: Record<EncoderDisplay, BooleanConfigKey> = {
raw_encoders: BooleanSetting.raw_encoders,
scaled_encoders: BooleanSetting.scaled_encoders,
};
export let botReducer = generateReducer<BotState>(initialState(), afterEach)

View File

@ -22,7 +22,7 @@ describe("getDefaultAxisLength()", () => {
});
it("returns XL axis lengths", () => {
mockStorj[BooleanSetting.mapXL] = true;
mockStorj[BooleanSetting.map_xl] = true;
const axes = getDefaultAxisLength();
expect(axes).toEqual({ x: 5900, y: 2900 });
});
@ -30,7 +30,7 @@ describe("getDefaultAxisLength()", () => {
describe("getGridSize()", () => {
it("returns default grid size", () => {
mockStorj[BooleanSetting.mapXL] = false;
mockStorj[BooleanSetting.map_xl] = false;
const grid = getGridSize({
x: { value: 100, isDefault: false },
y: { value: 200, isDefault: false }
@ -39,7 +39,7 @@ describe("getGridSize()", () => {
});
it("returns XL grid size", () => {
mockStorj[BooleanSetting.mapXL] = true;
mockStorj[BooleanSetting.map_xl] = true;
const grid = getGridSize({
x: { value: 100, isDefault: false },
y: { value: 200, isDefault: false }
@ -48,7 +48,7 @@ describe("getGridSize()", () => {
});
it("returns grid size using bot size", () => {
mockStorj[BooleanSetting.dynamicMap] = true;
mockStorj[BooleanSetting.dynamic_map] = true;
const grid = getGridSize({
x: { value: 100, isDefault: false },
y: { value: 200, isDefault: false }

View File

@ -16,7 +16,7 @@ import { getBotSize } from "./map/util";
import { catchErrors } from "../util";
export const getDefaultAxisLength = (): AxisNumberProperty => {
if (Session.getBool(BooleanSetting.mapXL)) {
if (Session.getBool(BooleanSetting.map_xl)) {
return { x: 5900, y: 2900 };
} else {
return { x: 2900, y: 1400 };
@ -24,7 +24,7 @@ export const getDefaultAxisLength = (): AxisNumberProperty => {
};
export const getGridSize = (botSize: BotSize) => {
if (Session.getBool(BooleanSetting.dynamicMap)) {
if (Session.getBool(BooleanSetting.dynamic_map)) {
// Render the map size according to device axis length.
return { x: botSize.x.value, y: botSize.y.value };
}
@ -38,7 +38,7 @@ export const gridOffset: AxisNumberProperty = { x: 50, y: 50 };
export class FarmDesigner extends React.Component<Props, Partial<State>> {
componentDidCatch(x: Error, y: React.ErrorInfo) { catchErrors(x, y); }
initializeSetting = (name: keyof State, defaultValue: boolean): boolean => {
initializeSetting = (name: (keyof State), defaultValue: boolean): boolean => {
const currentValue = Session.getBool(safeBooleanSettting(name));
if (isUndefined(currentValue)) {
Session.setBool(safeBooleanSettting(name), defaultValue);
@ -49,26 +49,26 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
}
getBotOriginQuadrant = (): BotOriginQuadrant => {
const value = Session.getNum(NumericSetting.botOriginQuadrant);
const value = Session.getNum(NumericSetting.bot_origin_quadrant);
return isBotOriginQuadrant(value) ? value : 2;
}
getZoomLevel = (): number => {
return Session.getNum(NumericSetting.zoomLevel) || 1;
return Session.getNum(NumericSetting.zoom_level) || 1;
}
state: State = {
legendMenuOpen: this.initializeSetting(BooleanSetting.legendMenuOpen, false),
showPlants: this.initializeSetting(BooleanSetting.showPlants, true),
showPoints: this.initializeSetting(BooleanSetting.showPoints, true),
showSpread: this.initializeSetting(BooleanSetting.showSpread, false),
showFarmbot: this.initializeSetting(BooleanSetting.showFarmbot, true),
botOriginQuadrant: this.getBotOriginQuadrant(),
zoomLevel: this.getZoomLevel()
legend_menu_open: this.initializeSetting(BooleanSetting.legend_menu_open, false),
show_plants: this.initializeSetting(BooleanSetting.show_plants, true),
show_points: this.initializeSetting(BooleanSetting.show_points, true),
show_spread: this.initializeSetting(BooleanSetting.show_spread, false),
show_farmbot: this.initializeSetting(BooleanSetting.show_farmbot, true),
bot_origin_quadrant: this.getBotOriginQuadrant(),
zoom_level: this.getZoomLevel()
};
componentDidMount() {
this.updateBotOriginQuadrant(this.state.botOriginQuadrant)();
this.updateBotOriginQuadrant(this.state.bot_origin_quadrant)();
this.updateZoomLevel(0)();
}
@ -78,14 +78,14 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
}
updateBotOriginQuadrant = (payload: BotOriginQuadrant) => () => {
this.setState({ botOriginQuadrant: payload });
Session.setNum(NumericSetting.botOriginQuadrant, payload);
this.setState({ bot_origin_quadrant: payload });
Session.setNum(NumericSetting.bot_origin_quadrant, payload);
}
updateZoomLevel = (zoomIncrement: number) => () => {
const payload = Math.round((this.getZoomLevel() + zoomIncrement) * 10) / 10;
this.setState({ zoomLevel: payload });
Session.setNum(NumericSetting.zoomLevel, payload);
this.setState({ zoom_level: payload });
Session.setNum(NumericSetting.zoom_level, payload);
}
childComponent(props: Props) {
@ -106,13 +106,13 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
}
const {
legendMenuOpen,
showPlants,
showPoints,
showSpread,
showFarmbot,
botOriginQuadrant,
zoomLevel
legend_menu_open,
show_plants,
show_points,
show_spread,
show_farmbot,
bot_origin_quadrant,
zoom_level
} = this.state;
const designerTabClasses: string[] = ["active", "visible-xs"];
@ -131,13 +131,13 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
zoom={this.updateZoomLevel}
toggle={this.toggle}
updateBotOriginQuadrant={this.updateBotOriginQuadrant}
botOriginQuadrant={botOriginQuadrant}
zoomLvl={zoomLevel}
legendMenuOpen={legendMenuOpen}
showPlants={showPlants}
showPoints={showPoints}
showSpread={showSpread}
showFarmbot={showFarmbot} />
bot_origin_quadrant={bot_origin_quadrant}
zoomLvl={zoom_level}
legendMenuOpen={legend_menu_open}
showPlants={show_plants}
showPoints={show_points}
showSpread={show_spread}
showFarmbot={show_farmbot} />
<div className="panel-header gray-panel designer-nav">
<div className="panel-tabs">
@ -158,12 +158,12 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
<div
className="farm-designer-map"
style={{ zoom: zoomLevel }}>
style={{ zoom: zoom_level }}>
<GardenMap
showPoints={showPoints}
showPlants={showPlants}
showSpread={showSpread}
showFarmbot={showFarmbot}
showPoints={show_points}
showPlants={show_plants}
showSpread={show_spread}
showFarmbot={show_farmbot}
selectedPlant={this.props.selectedPlant}
crops={this.props.crops}
dispatch={this.props.dispatch}
@ -175,8 +175,8 @@ export class FarmDesigner extends React.Component<Props, Partial<State>> {
botSize={botSize}
stopAtHome={stopAtHome}
hoveredPlant={this.props.hoveredPlant}
zoomLvl={zoomLevel}
botOriginQuadrant={botOriginQuadrant}
zoomLvl={zoom_level}
bot_origin_quadrant={bot_origin_quadrant}
gridSize={getGridSize(botSize)}
gridOffset={gridOffset}
peripherals={this.props.peripherals}

View File

@ -28,13 +28,13 @@ export function isBotOriginQuadrant(mystery: Mystery):
}
export interface State {
legendMenuOpen: boolean;
showPlants: boolean;
showPoints: boolean;
showSpread: boolean;
showFarmbot: boolean;
botOriginQuadrant: BotOriginQuadrant;
zoomLevel: number;
legend_menu_open: boolean;
show_plants: boolean;
show_points: boolean;
show_spread: boolean;
show_farmbot: boolean;
bot_origin_quadrant: BotOriginQuadrant;
zoom_level: number;
}
export interface Props {

View File

@ -36,7 +36,7 @@ describe("<GardenPlant/>", () => {
}
it("renders plant", () => {
mockStorj[BooleanSetting.disableAnimations] = true;
mockStorj[BooleanSetting.disable_animations] = true;
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
expect(wrapper.find("image").length).toEqual(1);
expect(wrapper.find("image").props().opacity).toEqual(1);
@ -47,7 +47,7 @@ describe("<GardenPlant/>", () => {
});
it("renders plant animations", () => {
mockStorj[BooleanSetting.disableAnimations] = false;
mockStorj[BooleanSetting.disable_animations] = false;
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
expect(wrapper.find(".soil-cloud").length).toEqual(1);
expect(wrapper.find(".animate").length).toEqual(1);

View File

@ -38,7 +38,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.disableAnimations);
const animate = !Session.getBool(BooleanSetting.disable_animations);
return <g id={"plant-" + id}>

View File

@ -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.disableAnimations);
const animate = !Session.getBool(BooleanSetting.disable_animations);
return <g id="hovered-plant-layer">
{this.props.visible && hovered &&

View File

@ -84,7 +84,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.disableAnimations);
const animate = !Session.getBool(BooleanSetting.disable_animations);
return <g id={"spread-" + id}>
{!selected &&

View File

@ -41,11 +41,11 @@ describe("<BotPeripherals/>", () => {
function animationToggle(
props: BotPeripheralsProps, enabled: number, disabled: number) {
mockStorj[BooleanSetting.disableAnimations] = false;
mockStorj[BooleanSetting.disable_animations] = false;
const wrapperEnabled = shallow(<BotPeripherals {...props } />);
expect(wrapperEnabled.find("use").length).toEqual(enabled);
mockStorj[BooleanSetting.disableAnimations] = true;
mockStorj[BooleanSetting.disable_animations] = true;
const wrapperDisabled = shallow(<BotPeripherals {...props } />);
expect(wrapperDisabled.find("use").length).toEqual(disabled);
}

View File

@ -44,7 +44,7 @@ function waterFigure(
props: { i: number, cx: number, cy: number }) {
const { i, cx, cy } = props;
const color = "rgb(11, 83, 148)";
const animate = !Session.getBool(BooleanSetting.disableAnimations);
const animate = !Session.getBool(BooleanSetting.disable_animations);
const copies = animate ? 3 : 1;
const animateClass = animate ? "animate" : "";
@ -85,7 +85,7 @@ function vacuumFigure(
props: { i: number, cx: number, cy: number }) {
const { i, cx, cy } = props;
const color = "black";
const animate = !Session.getBool(BooleanSetting.disableAnimations);
const animate = !Session.getBool(BooleanSetting.disable_animations);
const copies = animate ? 3 : 1;
const animateClass = animate ? "animate" : "";

View File

@ -10,8 +10,8 @@ export function VirtualFarmBot(props: VirtualFarmBotProps) {
const {
mapTransformProps, plantAreaOffset, peripherals, eStopStatus
} = props;
const displayTrail = Session.getBool(BooleanSetting.displayTrail);
const encoderFigure = Session.getBool(BooleanSetting.encoderFigure);
const displayTrail = Session.getBool(BooleanSetting.display_trail);
const encoderFigure = Session.getBool(BooleanSetting.encoder_figure);
return <g id="virtual-farmbot">
<BotPeripherals

View File

@ -18,7 +18,7 @@ export function getUserLang(langCode = "en_us") {
export function generateI18nConfig(lang: string): InitOptions {
// NOTE: Some users prefer English over i18nized version.
const choice = Session.getBool(BooleanSetting.disableI18n) ? "en" : lang;
const choice = Session.getBool(BooleanSetting.disable_i18n) ? "en" : lang;
const langi = require("../public/app-resources/languages/" + choice + ".js");
return {

View File

@ -3,7 +3,7 @@ import { Session } from "./session";
import { BooleanSetting } from "./session_keys";
export function LoadingPlant() {
const animations = !Session.getBool(BooleanSetting.disableAnimations);
const animations = !Session.getBool(BooleanSetting.disable_animations);
return <div className="loading-plant-div-container">
<svg width="300px" height="500px">
{animations &&

View File

@ -104,7 +104,7 @@ describe("<Logs />", () => {
});
it("loads filter setting", () => {
mockStorj[NumericSetting.warnLog] = 3;
mockStorj[NumericSetting.warn_log] = 3;
const wrapper = mount(<Logs logs={fakeLogs()} bot={bot} timeOffset={0} />);
expect(wrapper.state().warn).toEqual(3);
});
@ -120,7 +120,7 @@ describe("<Logs />", () => {
});
it("toggles filter", () => {
mockStorj[NumericSetting.warnLog] = 3;
mockStorj[NumericSetting.warn_log] = 3;
const wrapper = mount(<Logs logs={fakeLogs()} bot={bot} timeOffset={0} />);
// tslint:disable-next-line:no-any
const instance = wrapper.instance() as any;
@ -132,7 +132,7 @@ describe("<Logs />", () => {
});
it("sets filter", () => {
mockStorj[NumericSetting.warnLog] = 3;
mockStorj[NumericSetting.warn_log] = 3;
const wrapper = mount(<Logs logs={fakeLogs()} bot={bot} timeOffset={0} />);
// tslint:disable-next-line:no-any
const instance = wrapper.instance() as any;

View File

@ -67,11 +67,11 @@ describe("<LogsSettingsMenu />", () => {
const setFilterLevel = jest.fn();
const wrapper = mount(<LogsSettingsMenu
bot={bot} setFilterLevel={() => setFilterLevel} />);
mockStorj[NumericSetting.busyLog] = 0;
mockStorj[NumericSetting.busy_log] = 0;
wrapper.find("button").at(0).simulate("click");
expect(setFilterLevel).toHaveBeenCalledWith(2);
jest.clearAllMocks();
mockStorj[NumericSetting.busyLog] = 3;
mockStorj[NumericSetting.busy_log] = 3;
wrapper.find("button").at(0).simulate("click");
expect(setFilterLevel).not.toHaveBeenCalled();
});

View File

@ -14,6 +14,7 @@ import { Session, safeNumericSetting } from "../session";
import { isUndefined } from "lodash";
import { NumericSetting } from "../session_keys";
import { catchErrors } from "../util";
import { NumberConfigKey } from "../config_storage/web_app_configs";
export const formatLogTime = (created_at: number, timeoffset: number) =>
moment.unix(created_at).utcOffset(timeoffset).format("MMM D, h:mma");
@ -22,7 +23,7 @@ export const formatLogTime = (created_at: number, timeoffset: number) =>
export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
componentDidCatch(x: Error, y: React.ErrorInfo) { catchErrors(x, y); }
initialize = (name: NumericSetting, defaultValue: number): number => {
initialize = (name: NumberConfigKey, defaultValue: number): number => {
const currentValue = Session.getNum(safeNumericSetting(name));
if (isUndefined(currentValue)) {
Session.setNum(safeNumericSetting(name), defaultValue);
@ -34,13 +35,13 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
state: LogsState = {
autoscroll: false,
success: this.initialize(NumericSetting.successLog, 1),
busy: this.initialize(NumericSetting.busyLog, 1),
warn: this.initialize(NumericSetting.warnLog, 1),
error: this.initialize(NumericSetting.errorLog, 1),
info: this.initialize(NumericSetting.infoLog, 1),
fun: this.initialize(NumericSetting.funLog, 1),
debug: this.initialize(NumericSetting.debugLog, 1),
success: this.initialize(NumericSetting.success_log, 1),
busy: this.initialize(NumericSetting.busy_log, 1),
warn: this.initialize(NumericSetting.warn_log, 1),
error: this.initialize(NumericSetting.error_log, 1),
info: this.initialize(NumericSetting.info_log, 1),
fun: this.initialize(NumericSetting.fun_log, 1),
debug: this.initialize(NumericSetting.debug_log, 1),
};
toggle = (name: keyof LogsState) => {

View File

@ -23,14 +23,14 @@ describe("remove()", () => {
it("deletes step without confirmation", () => {
const dispatch = jest.fn();
mockStorj[BooleanSetting.confirmStepDeletion] = false;
mockStorj[BooleanSetting.confirm_step_deletion] = false;
remove({ index: 0, dispatch, sequence: fakeSequence() });
expect(dispatch).toHaveBeenCalled();
});
it("deletes step with confirmation", () => {
const dispatch = jest.fn();
mockStorj[BooleanSetting.confirmStepDeletion] = true;
mockStorj[BooleanSetting.confirm_step_deletion] = true;
remove({ index: 0, dispatch, sequence: fakeSequence() });
expect(dispatch).not.toHaveBeenCalled();
// tslint:disable-next-line:no-any

View File

@ -66,7 +66,7 @@ interface RemoveParams {
}
export function remove({ dispatch, index, sequence }: RemoveParams) {
if (!Session.getBool(BooleanSetting.confirmStepDeletion) ||
if (!Session.getBool(BooleanSetting.confirm_step_deletion) ||
confirm(t("Are you sure you want to delete this step?"))) {
const original = sequence;
const update = defensiveClone(original);

View File

@ -1,6 +1,7 @@
import { AuthState } from "./auth/interfaces";
import { box } from "boxed_value";
import { get, isNumber, isBoolean } from "lodash";
import { BooleanConfigKey, NumberConfigKey } from "./config_storage/web_app_configs";
import { BooleanSetting, NumericSetting } from "./session_keys";
/** The `Session` namespace is a wrapper for `localStorage`.
@ -45,54 +46,54 @@ export namespace Session {
/** Fetch a *boolean* value from localstorage. Returns `undefined` when
* none are found.*/
export function getBool(key: BooleanSetting): boolean | undefined {
export function getBool(key: BooleanConfigKey): boolean | undefined {
const output = JSON.parse(localStorage.getItem(key) || "null");
return (isBoolean(output)) ? output : undefined;
}
/** Store a boolean value in `localStorage` */
export function setBool(key: BooleanSetting, val: boolean): boolean {
export function setBool(key: BooleanConfigKey, val: boolean): boolean {
localStorage.setItem(key, JSON.stringify(val));
return val;
}
export function invertBool(key: BooleanSetting): boolean {
export function invertBool(key: BooleanConfigKey): boolean {
return Session.setBool(key, !Session.getBool(key));
}
/** Extract numeric settings from `localStorage`. Returns `undefined` when
* none are found. */
export function getNum(key: NumericSetting): number | undefined {
export function getNum(key: NumberConfigKey): number | undefined {
const output = JSON.parse(get(localStorage, key, "null"));
return (isNumber(output)) ? output : undefined;
}
/** Set a numeric value in `localStorage`. */
export function setNum(key: NumericSetting, val: number): void {
export function setNum(key: NumberConfigKey, val: number): void {
localStorage.setItem(key, JSON.stringify(val));
}
}
export const isBooleanSetting =
// tslint:disable-next-line:no-any
(x: any): x is BooleanSetting => !!BooleanSetting[x];
(x: any): x is BooleanConfigKey => !!BooleanSetting[x as BooleanConfigKey];
export function safeBooleanSettting(name: string): BooleanSetting {
export function safeBooleanSettting(name: string): BooleanConfigKey {
if (isBooleanSetting(name)) {
return name;
} else {
throw new Error(`Expected BooleanSetting but got '${name}'`);
throw new Error(`Expected BooleanConfigKey but got '${name}'`);
}
}
export const isNumericSetting =
// tslint:disable-next-line:no-any
(x: any): x is NumericSetting => !!NumericSetting[x];
(x: any): x is NumberConfigKey => !!NumericSetting[x as NumberConfigKey];
export function safeNumericSetting(name: string): NumericSetting {
export function safeNumericSetting(name: string): NumberConfigKey {
if (isNumericSetting(name)) {
return name;
} else {
throw new Error(`Expected NumericSetting but got '${name}'`);
throw new Error(`Expected NumberConfigKey but got '${name}'`);
}
}

View File

@ -1,36 +1,40 @@
export enum BooleanSetting {
xAxisInverted = "xAxisInverted",
yAxisInverted = "yAxisInverted",
zAxisInverted = "zAxisInverted",
rawEncoders = "rawEncoders",
scaledEncoders = "scaledEncoders",
legendMenuOpen = "legendMenuOpen",
showPlants = "showPlants",
showPoints = "showPoints",
showSpread = "showSpread",
showFarmbot = "showFarmbot",
import { BooleanConfigKey, NumberConfigKey } from "./config_storage/web_app_configs";
export const BooleanSetting: Record<BooleanConfigKey, BooleanConfigKey> = {
x_axis_inverted: "x_axis_inverted",
y_axis_inverted: "y_axis_inverted",
z_axis_inverted: "z_axis_inverted",
raw_encoders: "raw_encoders",
scaled_encoders: "scaled_encoders",
legend_menu_open: "legend_menu_open",
show_plants: "show_plants",
show_points: "show_points",
show_spread: "show_spread",
show_farmbot: "show_farmbot",
/** "Labs" feature names. */
weedDetector = "weedDetector",
disableI18n = "disableI18n",
confirmStepDeletion = "confirmStepDeletion",
hideWebcamWidget = "hideWebcamWidget",
dynamicMap = "dynamicMap",
mapXL = "mapXL",
disableAnimations = "disableAnimations",
displayTrail = "displayTrail",
encoderFigure = "encoderFigure",
}
weed_detector: "weed_detector",
disable_i18n: "disable_i18n",
confirm_step_deletion: "confirm_step_deletion",
hide_webcam_widget: "hide_webcam_widget",
dynamic_map: "dynamic_map",
map_xl: "map_xl",
disable_animations: "disable_animations",
display_trail: "display_trail",
encoder_figure: "encoder_figure",
};
export enum NumericSetting {
botOriginQuadrant = "botOriginQuadrant",
zoomLevel = "zoomLevel",
successLog = "successLog",
busyLog = "busyLog",
warnLog = "warnLog",
errorLog = "errorLog",
infoLog = "infoLog",
funLog = "funLog",
debugLog = "debugLog",
successsLog = "successsLog"
}
export const NumericSetting: Record<NumberConfigKey, NumberConfigKey> = {
bot_origin_quadrant: "bot_origin_quadrant",
busy_log: "busy_log",
debug_log: "debug_log",
device_id: "device_id",
error_log: "error_log",
fun_log: "fun_log",
id: "id",
info_log: "info_log",
success_log: "success_log",
successs_log: "successs_log",
warn_log: "warn_log",
zoom_level: "zoom_level",
};