WIP
parent
1c932006d0
commit
b3deac0374
|
@ -51,15 +51,15 @@ export let bot: Everything["bot"] = {
|
|||
"farmwares": {}
|
||||
}
|
||||
},
|
||||
axis_inversion: {
|
||||
"x": false,
|
||||
"y": false,
|
||||
"z": false,
|
||||
},
|
||||
encoder_visibility: {
|
||||
"raw_encoders": false,
|
||||
"scaled_encoders": false,
|
||||
},
|
||||
// axis_inversion: {
|
||||
// "x": false,
|
||||
// "y": false,
|
||||
// "z": false,
|
||||
// },
|
||||
// encoder_visibility: {
|
||||
// "raw_encoders": false,
|
||||
// "scaled_encoders": false,
|
||||
// },
|
||||
"dirty": false,
|
||||
"currentOSVersion": "3.1.6",
|
||||
"connectivity": {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { init, error } from "farmbot-toastr";
|
|||
import { NavBar } from "./nav";
|
||||
import { Everything, Log } from "./interfaces";
|
||||
import { LoadingPlant } from "./loading_plant";
|
||||
import { BotState } from "./devices/interfaces";
|
||||
import { BotState, Xyz } from "./devices/interfaces";
|
||||
import { ResourceName, TaggedUser } from "./resources/tagged_resources";
|
||||
import {
|
||||
selectAllLogs,
|
||||
|
@ -18,6 +18,8 @@ import { HotKeys } from "./hotkeys";
|
|||
import { ControlsPopup } from "./controls_popup";
|
||||
import { Content } from "./constants";
|
||||
import { catchErrors } from "./util";
|
||||
import { Session } from "./session";
|
||||
import { BooleanSetting } from "./session_keys";
|
||||
|
||||
/** Remove 300ms delay on touch devices - https://github.com/ftlabs/fastclick */
|
||||
const fastClick = require("fastclick");
|
||||
|
@ -35,6 +37,7 @@ export interface AppProps {
|
|||
consistent: boolean;
|
||||
autoSyncEnabled: boolean;
|
||||
timeOffset: number;
|
||||
axisInversion: Record<Xyz, boolean>;
|
||||
}
|
||||
|
||||
function mapStateToProps(props: Everything): AppProps {
|
||||
|
@ -51,7 +54,12 @@ function mapStateToProps(props: Everything): AppProps {
|
|||
.value(),
|
||||
loaded: props.resources.loaded,
|
||||
consistent: !!(props.bot || {}).consistent,
|
||||
autoSyncEnabled: !!props.bot.hardware.configuration.auto_sync
|
||||
autoSyncEnabled: !!props.bot.hardware.configuration.auto_sync,
|
||||
axisInversion: {
|
||||
x: !!Session.deprecatedGetBool(BooleanSetting.x_axis_inverted),
|
||||
y: !!Session.deprecatedGetBool(BooleanSetting.y_axis_inverted),
|
||||
z: !!Session.deprecatedGetBool(BooleanSetting.z_axis_inverted),
|
||||
}
|
||||
};
|
||||
}
|
||||
/** Time at which the app gives up and asks the user to refresh */
|
||||
|
@ -110,7 +118,7 @@ export class App extends React.Component<AppProps, {}> {
|
|||
!currentPath.startsWith("/app/regimens") &&
|
||||
<ControlsPopup
|
||||
dispatch={this.props.dispatch}
|
||||
axisInversion={this.props.bot.axis_inversion} />}
|
||||
axisInversion={this.props.axisInversion} />}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,36 @@
|
|||
import { store } from "../redux/store";
|
||||
import { getWebAppConfig } from "../resources/selectors";
|
||||
import { BooleanConfigKey } from "../config_storage/web_app_configs";
|
||||
import { edit, save } from "../api/crud";
|
||||
|
||||
/**
|
||||
* HISTORICAL CONTEXT: We once stored user settings (like map zoom level) in localStorage and
|
||||
* would retrieve values via `Session.getBool("zoom_level")`
|
||||
*
|
||||
* PROBLEM: localStorage is no longer used. Many parts of the were accessing
|
||||
* values in places that did not have access to the Redux store.
|
||||
*
|
||||
* SOLUTION: Create a temporary shim that will "cheat" and directly call Redux
|
||||
* store without a lot of boilerplate props passing.
|
||||
*
|
||||
* WHY NOT JUST INLINE THESE FUNCTIONS?: It's easier to stub out calls in tests
|
||||
* that already exist.
|
||||
*/
|
||||
|
||||
/** Avoid using this function in new places. Pass props instead. */
|
||||
export function getBoolViaRedux(key: BooleanConfigKey): boolean | undefined {
|
||||
const conf = getWebAppConfig(store.getState().resources.index);
|
||||
return conf && conf.body[key];
|
||||
}
|
||||
|
||||
/** Avoid using this function in new places. Pass props instead. */
|
||||
export function setBoolViaRedux(key: BooleanConfigKey, val: boolean) {
|
||||
const conf = getWebAppConfig(store.getState().resources.index);
|
||||
if (conf) {
|
||||
store.dispatch(edit(conf, { [key]: val }));
|
||||
store.dispatch(save(conf.uuid));
|
||||
return val;
|
||||
} else {
|
||||
throw new Error("Impossible?");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -468,7 +468,6 @@ export enum Actions {
|
|||
BOT_CHANGE = "BOT_CHANGE",
|
||||
FETCH_OS_UPDATE_INFO_OK = "FETCH_OS_UPDATE_INFO_OK",
|
||||
FETCH_FW_UPDATE_INFO_OK = "FETCH_FW_UPDATE_INFO_OK",
|
||||
INVERT_JOG_BUTTON = "INVERT_JOG_BUTTON",
|
||||
DISPLAY_ENCODER_DATA = "DISPLAY_ENCODER_DATA",
|
||||
STASH_STATUS = "STASH_STATUS",
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ describe("<Move />", () => {
|
|||
|
||||
it("has only raw encoder data display", () => {
|
||||
const p = fakeProps();
|
||||
p.bot.encoder_visibility.raw_encoders = true;
|
||||
p.encoder_visibility.raw_encoders = true;
|
||||
const wrapper = mount(<Move {...p} />);
|
||||
const txt = wrapper.text().toLowerCase();
|
||||
expect(txt).toContain("raw");
|
||||
|
@ -61,10 +61,6 @@ describe("<Move />", () => {
|
|||
// tslint:disable-next-line:no-any
|
||||
const instance = wrapper.instance() as any;
|
||||
instance.toggle("x")();
|
||||
expect(p.dispatch).toHaveBeenCalledWith({
|
||||
type: Actions.INVERT_JOG_BUTTON,
|
||||
payload: "x"
|
||||
});
|
||||
expect(Session.invertBool).toHaveBeenCalledWith("x_axis_inverted");
|
||||
});
|
||||
|
||||
|
|
|
@ -20,22 +20,18 @@ export class Move extends React.Component<MoveProps, {}> {
|
|||
|
||||
toggle = (name: Xyz) => () => {
|
||||
Session.invertBool(INVERSION_MAPPING[name]);
|
||||
this.props.dispatch({ type: "INVERT_JOG_BUTTON", payload: name });
|
||||
};
|
||||
|
||||
toggle_encoder_data =
|
||||
(name: EncoderDisplay) => () => {
|
||||
Session.invertBool(ENCODER_MAPPING[name]);
|
||||
this.props.dispatch({ type: "DISPLAY_ENCODER_DATA", payload: name });
|
||||
}
|
||||
(name: EncoderDisplay) => () => Session.invertBool(ENCODER_MAPPING[name]);
|
||||
|
||||
render() {
|
||||
const {
|
||||
sync_status, firmware_version
|
||||
} = this.props.bot.hardware.informational_settings;
|
||||
const x_axis_inverted = this.props.bot.axis_inversion.x;
|
||||
const y_axis_inverted = this.props.bot.axis_inversion.y;
|
||||
const z_axis_inverted = this.props.bot.axis_inversion.z;
|
||||
const x_axis_inverted = this.props.x_axis_inversion;
|
||||
const y_axis_inverted = this.props.y_axis_inversion;
|
||||
const z_axis_inverted = this.props.z_axis_inversion;
|
||||
const { raw_encoders, scaled_encoders } = this.props.bot.encoder_visibility;
|
||||
const xBtnColor = x_axis_inverted ? "green" : "red";
|
||||
const yBtnColor = y_axis_inverted ? "green" : "red";
|
||||
|
|
|
@ -72,20 +72,21 @@ describe("botRedcuer", () => {
|
|||
});
|
||||
|
||||
it("inverts X/Y/Z", () => {
|
||||
const action = { type: Actions.INVERT_JOG_BUTTON, payload: "Q" };
|
||||
pending("These tests are no longer valid");
|
||||
// const action = { type: Actions.INVERT_JOG_BUTTON, payload: "Q" };
|
||||
|
||||
action.payload = "x";
|
||||
const result = botReducer(initialState(), action);
|
||||
expect(result.axis_inversion.x)
|
||||
.toBe(!initialState().axis_inversion.x);
|
||||
// action.payload = "x";
|
||||
// const result = botReducer(initialState(), action);
|
||||
// expect(result.axis_inversion.x)
|
||||
// .toBe(!initialState().axis_inversion.x);
|
||||
|
||||
action.payload = "y";
|
||||
expect(botReducer(initialState(), action).axis_inversion.y)
|
||||
.toBe(!initialState().axis_inversion.y);
|
||||
// action.payload = "y";
|
||||
// expect(botReducer(initialState(), action).axis_inversion.y)
|
||||
// .toBe(!initialState().axis_inversion.y);
|
||||
|
||||
action.payload = "z";
|
||||
expect(botReducer(initialState(), action).axis_inversion.z)
|
||||
.toBe(!initialState().axis_inversion.z);
|
||||
// action.payload = "z";
|
||||
// expect(botReducer(initialState(), action).axis_inversion.z)
|
||||
// .toBe(!initialState().axis_inversion.z);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
import { RestResources, ResourceIndex } from "../resources/interfaces";
|
||||
import { TaggedUser } from "../resources/tagged_resources";
|
||||
import { WD_ENV } from "../farmware/weed_detector/remote_env/interfaces";
|
||||
import { EncoderDisplay } from "../controls/interfaces";
|
||||
import { ConnectionStatus, ConnectionState } from "../connectivity/interfaces";
|
||||
import { IntegerSize } from "../util";
|
||||
|
||||
|
@ -64,19 +63,23 @@ export interface BotState {
|
|||
* spinner or not. */
|
||||
isUpdating?: boolean;
|
||||
controlPanelState: ControlPanelState;
|
||||
/** The inversions for the jog buttons on the controls page. */
|
||||
axis_inversion: Record<Xyz, boolean>;
|
||||
/** The display setting for encoder data on the controls page. */
|
||||
encoder_visibility: Record<EncoderDisplay, boolean>;
|
||||
/**
|
||||
*
|
||||
* THESE ARE THE RESPONSIBILITY OF THE API SETTINGS STORE NOW. REMOVING
|
||||
* - RC 10 JAN 18
|
||||
*
|
||||
*/
|
||||
// /** The inversions for the jog buttons on the controls page. */
|
||||
// axis_inversion: Record<Xyz, boolean>;
|
||||
// /** The display setting for encoder data on the controls page. */
|
||||
// encoder_visibility: Record<EncoderDisplay, boolean>;
|
||||
/** Have all API requests been acknowledged by external services? This flag
|
||||
* lets us know if it is safe to do data critical tasks with the bot */
|
||||
consistent: boolean;
|
||||
connectivity: ConnectionState;
|
||||
}
|
||||
|
||||
export interface BotProp {
|
||||
bot: BotState;
|
||||
}
|
||||
export interface BotProp { bot: BotState; }
|
||||
|
||||
/** Status registers for the bot's status */
|
||||
export type HardwareState = BotStateTree;
|
||||
|
|
|
@ -85,15 +85,6 @@ export let initialState = (): BotState => ({
|
|||
dirty: false,
|
||||
currentOSVersion: undefined,
|
||||
currentFWVersion: undefined,
|
||||
axis_inversion: {
|
||||
x: !!Session.deprecatedGetBool(BooleanSetting.x_axis_inverted),
|
||||
y: !!Session.deprecatedGetBool(BooleanSetting.y_axis_inverted),
|
||||
z: !!Session.deprecatedGetBool(BooleanSetting.z_axis_inverted),
|
||||
},
|
||||
encoder_visibility: {
|
||||
raw_encoders: !!Session.deprecatedGetBool(BooleanSetting.raw_encoders),
|
||||
scaled_encoders: !!Session.deprecatedGetBool(BooleanSetting.scaled_encoders),
|
||||
},
|
||||
connectivity: {
|
||||
"bot.mqtt": undefined,
|
||||
"user.mqtt": undefined,
|
||||
|
@ -188,10 +179,6 @@ export let botReducer = generateReducer<BotState>(initialState(), afterEach)
|
|||
s.currentFWVersion = payload;
|
||||
return s;
|
||||
})
|
||||
.add<Xyz>(Actions.INVERT_JOG_BUTTON, (s, { payload }) => {
|
||||
s.axis_inversion[payload] = !s.axis_inversion[payload];
|
||||
return s;
|
||||
})
|
||||
.add<EncoderDisplay>(Actions.DISPLAY_ENCODER_DATA, (s, { payload }) => {
|
||||
s.encoder_visibility[payload] = !s.encoder_visibility[payload];
|
||||
return s;
|
||||
|
|
|
@ -27,9 +27,11 @@ import {
|
|||
TaggedToolSlotPointer,
|
||||
TaggedUser,
|
||||
TaggedWebcamFeed,
|
||||
TaggedDevice
|
||||
TaggedDevice,
|
||||
TaggedWebAppConfig
|
||||
} from "./tagged_resources";
|
||||
import { CowardlyDictionary, betterCompact, sortResourcesById } from "../util";
|
||||
import { isNumber } from "util";
|
||||
type StringMap = CowardlyDictionary<string>;
|
||||
|
||||
/** Similar to findId(), but does not throw exceptions. Do NOT use this method
|
||||
|
@ -405,12 +407,12 @@ export let findSlotById = byId<TaggedToolSlotPointer>("Point");
|
|||
/** Find a Tool's corresponding Slot. */
|
||||
export let findSlotByToolId = (index: ResourceIndex, tool_id: number) => {
|
||||
const tool = findToolById(index, tool_id);
|
||||
const query: any = { body: { tool_id: tool.body.id } };
|
||||
const query = { body: { tool_id: tool.body.id } };
|
||||
const every = Object
|
||||
.keys(index.references)
|
||||
.map(x => index.references[x]);
|
||||
const tts = _.find(every, query);
|
||||
if (tts && isTaggedToolSlotPointer(tts) && sanityCheck(tts)) {
|
||||
if (tts && !isNumber(tts) && isTaggedToolSlotPointer(tts) && sanityCheck(tts)) {
|
||||
return tts;
|
||||
} else {
|
||||
return undefined;
|
||||
|
@ -522,6 +524,7 @@ export function mapToolIdToName(input: ResourceIndex) {
|
|||
.map(x => ({ key: "" + x.body.id, val: x.body.name }))
|
||||
.reduce((x, y) => ({ ...{ [y.key]: y.val, ...x } }), {} as StringMap);
|
||||
}
|
||||
|
||||
/** I dislike this method. */
|
||||
export function findToolBySlotId(input: ResourceIndex, tool_slot_id: number):
|
||||
TaggedTool | undefined {
|
||||
|
@ -546,3 +549,10 @@ export function findToolBySlotId(input: ResourceIndex, tool_slot_id: number):
|
|||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getWebAppConfig(i: ResourceIndex): TaggedWebAppConfig | undefined {
|
||||
const conf = i.references[i.byKind.WebAppConfig[0] || "NO"];
|
||||
if (conf && conf.kind === "WebAppConfig") {
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,8 @@ export type TaggedResource =
|
|||
| TaggedSequence
|
||||
| TaggedTool
|
||||
| TaggedUser
|
||||
| TaggedWebcamFeed;
|
||||
| TaggedWebcamFeed
|
||||
| TaggedWebAppConfig;
|
||||
|
||||
export type TaggedRegimen = Resource<"Regimen", Regimen>;
|
||||
export type TaggedTool = Resource<"Tool", Tool>;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { AuthState } from "./auth/interfaces";
|
||||
import { box } from "boxed_value";
|
||||
import { get, isNumber, isBoolean } from "lodash";
|
||||
import { get, isNumber } from "lodash";
|
||||
import { BooleanConfigKey, NumberConfigKey } from "./config_storage/web_app_configs";
|
||||
import { BooleanSetting, NumericSetting } from "./session_keys";
|
||||
import { getBoolViaRedux, setBoolViaRedux } from "./config/legacy_shims";
|
||||
|
||||
/** The `Session` namespace is a wrapper for `localStorage`.
|
||||
* Use this to avoid direct access of `localStorage` where possible.
|
||||
|
@ -44,17 +45,15 @@ export namespace Session {
|
|||
return undefined as never;
|
||||
}
|
||||
|
||||
/** Fetch a *boolean* value from localstorage. Returns `undefined` when
|
||||
* none are found.*/
|
||||
/** @deprecated Don't use this anymore. This is a legacy articfact of when we
|
||||
* used localStorage to store API settings. */
|
||||
export function deprecatedGetBool(key: BooleanConfigKey): boolean | undefined {
|
||||
const output = JSON.parse(localStorage.getItem(key) || "null");
|
||||
return (isBoolean(output)) ? output : undefined;
|
||||
return getBoolViaRedux(key);
|
||||
}
|
||||
|
||||
/** Store a boolean value in `localStorage` */
|
||||
export function setBool(key: BooleanConfigKey, val: boolean): boolean {
|
||||
localStorage.setItem(key, JSON.stringify(val));
|
||||
return val;
|
||||
return setBoolViaRedux(key, val);
|
||||
}
|
||||
|
||||
export function invertBool(key: BooleanConfigKey): boolean {
|
||||
|
|
Loading…
Reference in New Issue