remove deprecated code

pull/975/head
gabrielburnworth 2018-08-30 18:25:58 -07:00
parent 36eba926e3
commit fb14f89d6f
67 changed files with 357 additions and 420 deletions

View File

@ -30,6 +30,7 @@ const fakeProps = (): AppProps => {
firmwareConfig: undefined,
xySwap: false,
animate: false,
getConfigValue: jest.fn(),
};
};

View File

@ -8,14 +8,7 @@ jest.mock("axios", () => ({
}
}));
jest.mock("../session", () => {
return {
Session: {
clear: jest.fn(),
deprecatedGetBool: jest.fn(),
}
};
});
jest.mock("../session", () => ({ Session: { clear: jest.fn(), } }));
import { maybeRefreshToken } from "../refresh_token";
import { API } from "../api/index";

View File

@ -6,8 +6,6 @@ let mockAuth: AuthState | undefined = undefined;
jest.mock("../session", () => ({
Session: {
fetchStoredToken: jest.fn(() => mockAuth),
deprecatedGetNum: () => undefined,
deprecatedGetBool: () => undefined,
getAll: () => undefined,
clear: jest.fn()
}

View File

@ -1,24 +1,3 @@
import { fakeWebAppConfig } from "../__test_support__/fake_state/resources";
import { fakeState } from "../__test_support__/fake_state";
const mockConfig = fakeWebAppConfig();
jest.mock("../resources/selectors_by_kind", () => ({
getWebAppConfig: () => mockConfig
}));
jest.mock("../api/crud", () => ({
edit: jest.fn(),
save: jest.fn(),
}));
const mockState = fakeState();
jest.mock("../redux/store", () => ({
store: {
dispatch: jest.fn(),
getState: () => mockState,
}
}));
import {
isNumericSetting,
isBooleanSetting,
@ -27,7 +6,6 @@ import {
Session,
} from "../session";
import { auth } from "../__test_support__/fake_state/token";
import { edit, save } from "../api/crud";
describe("fetchStoredToken", () => {
it("can't fetch token", () => {
@ -61,41 +39,6 @@ describe("safeBooleanSetting", () => {
});
});
describe("deprecatedGetNum", () => {
it("gets number", () => {
const result = Session.deprecatedGetNum("success_log");
expect(result).toEqual(3);
});
});
describe("deprecatedSetNum", () => {
it("sets number", () => {
Session.deprecatedSetNum("success_log", 0);
expect(edit).toHaveBeenCalledWith(expect.any(Object), { success_log: 0 });
expect(save).toHaveBeenCalledWith(mockConfig.uuid);
});
});
describe("setBool", () => {
it("sets bool", () => {
Session.setBool("x_axis_inverted", false);
expect(edit).toHaveBeenCalledWith(expect.any(Object), {
x_axis_inverted: false
});
expect(save).toHaveBeenCalledWith(mockConfig.uuid);
});
});
describe("invertBool", () => {
it("inverts bool", () => {
Session.invertBool("x_axis_inverted");
expect(edit).toHaveBeenCalledWith(expect.any(Object), {
x_axis_inverted: true
});
expect(save).toHaveBeenCalledWith(mockConfig.uuid);
});
});
describe("safeNumericSetting", () => {
it("safely returns num", () => {
expect(() => safeNumericSetting("no")).toThrow();

View File

@ -79,7 +79,9 @@ export class Account extends React.Component<Props, State> {
<ChangePassword />
</Row>
<Row>
<LabsFeatures />
<LabsFeatures
dispatch={this.props.dispatch}
getConfigValue={this.props.getConfigValue} />
</Row>
<Row>
<DeleteAccount onClick={deleteAcct} />

View File

@ -1,9 +1,11 @@
import { User } from "../auth/interfaces";
import { TaggedUser } from "farmbot";
import { GetWebAppConfigValue } from "../config_storage/actions";
export interface Props {
user: TaggedUser;
dispatch: Function;
getConfigValue: GetWebAppConfigValue;
}
/** JSON form that gets POSTed to the API when user updates their info. */

View File

@ -3,7 +3,7 @@ import { fetchLabFeatures } from "../labs_features_list_data";
describe("fetchLabFeatures", () => {
window.location.reload = jest.fn();
it("basically just initializes stuff", () => {
const val = fetchLabFeatures();
const val = fetchLabFeatures(jest.fn());
expect(val.length).toBe(9);
expect(val[0].value).toBeFalsy();
const { callback } = val[0];

View File

@ -1,20 +1,5 @@
const mockStorj: Dictionary<boolean> = {};
jest.mock("../../../session", () => {
return {
Session: {
deprecatedGetBool: (k: string) => {
mockStorj[k] = !!mockStorj[k];
return mockStorj[k];
},
invertBool: (k: string) => {
mockStorj[k] = !mockStorj[k];
return mockStorj[k];
}
}
};
});
import { Dictionary } from "farmbot";
import { maybeToggleFeature, LabsFeature } from "../labs_features_list_data";
import { BooleanSetting } from "../../../session_keys";
@ -29,7 +14,7 @@ describe("maybeToggleFeature()", () => {
storageKey: BooleanSetting.stub_config,
confirmationMessage: "are you sure?"
};
const out = maybeToggleFeature(data);
const out = maybeToggleFeature(x => mockStorj[x], jest.fn())(data);
expect(data.value).toBeFalsy();
expect(out).toBeUndefined();
expect(window.confirm).toHaveBeenCalledWith(data.confirmationMessage);
@ -44,7 +29,7 @@ describe("maybeToggleFeature()", () => {
storageKey: BooleanSetting.stub_config,
confirmationMessage: "are you sure?"
};
const out = maybeToggleFeature(data);
const out = maybeToggleFeature(x => mockStorj[x], jest.fn())(data);
out ?
expect(out.value).toBeTruthy() : fail("out === undefined. Thats bad");
expect(out).toBeTruthy();
@ -52,7 +37,7 @@ describe("maybeToggleFeature()", () => {
it("Does not require consent when going from true to false", () => {
window.confirm = jest.fn(() => true);
const output = maybeToggleFeature({
const output = maybeToggleFeature(x => mockStorj[x], jest.fn())({
name: "Example",
value: (mockStorj[BooleanSetting.stub_config] = true),
description: "I stub this.",
@ -71,7 +56,7 @@ describe("maybeToggleFeature()", () => {
description: "I stub this.",
storageKey: BooleanSetting.stub_config
};
const out = maybeToggleFeature(data);
const out = maybeToggleFeature(x => mockStorj[x], jest.fn())(data);
out ?
expect(out.value).toBeTruthy() : fail("out === undefined. Thats bad");
expect(out).toBeTruthy();

View File

@ -9,7 +9,7 @@ const mockFeatures = [
];
const mocks = {
"maybeToggleFeature": jest.fn(),
"maybeToggleFeature": jest.fn(() => jest.fn()),
"fetchLabFeatures": jest.fn(() => mockFeatures)
};
@ -21,7 +21,9 @@ import { LabsFeatures } from "../labs_features";
describe("<LabsFeatures/>", () => {
it("triggers the correct callback on click", () => {
const el = mount(<LabsFeatures />);
const el = mount(<LabsFeatures
dispatch={jest.fn()}
getConfigValue={jest.fn()} />);
expect(mocks.fetchLabFeatures.mock.calls.length).toBeGreaterThan(0);
el.find("button").simulate("click");
expect(mockFeatures[0].callback).toHaveBeenCalled();

View File

@ -4,20 +4,29 @@ import { LabsFeaturesList } from "./labs_features_list_ui";
import { maybeToggleFeature } from "./labs_features_list_data";
import { t } from "i18next";
import { ToolTips } from "../../constants";
import { GetWebAppConfigValue } from "../../config_storage/actions";
export class LabsFeatures extends React.Component<{}, {}> {
interface LabsFeaturesProps {
getConfigValue: GetWebAppConfigValue;
dispatch: Function;
}
export class LabsFeatures extends React.Component<LabsFeaturesProps, {}> {
state = {};
render() {
const { getConfigValue, dispatch } = this.props;
return <Widget className="peripherals-widget">
<WidgetHeader title={t("App Settings")}
helpText={ToolTips.LABS}>
</WidgetHeader>
<WidgetBody>
<LabsFeaturesList onToggle={(x) => {
maybeToggleFeature(x);
this.forceUpdate();
}} />
<LabsFeaturesList
getConfigValue={getConfigValue}
onToggle={x => {
maybeToggleFeature(getConfigValue, dispatch)(x);
this.forceUpdate();
}} />
</WidgetBody>
</Widget>;
}

View File

@ -1,8 +1,9 @@
import { Session } from "../../session";
import { t } from "i18next";
import { BooleanConfigKey } from "../../config_storage/web_app_configs";
import { BooleanSetting } from "../../session_keys";
import { Content } from "../../constants";
import { VirtualTrail } from "../../farm_designer/map/virtual_farmbot/bot_trail";
import { GetWebAppConfigValue, setWebAppConfigValue } from "../../config_storage/actions";
export interface LabsFeature {
/** Toggle label. */
@ -21,88 +22,91 @@ export interface LabsFeature {
callback?(): void;
}
export const fetchLabFeatures = (): LabsFeature[] => ([
{
name: t("Internationalize Web App"),
description: t("Turn off to set Web App to English."),
storageKey: BooleanSetting.disable_i18n,
value: false,
displayInvert: true,
callback: () => window.location.reload()
},
{
name: t("Confirm Sequence step deletion"),
description: t(Content.CONFIRM_STEP_DELETION),
storageKey: BooleanSetting.confirm_step_deletion,
value: false
},
{
name: t("Hide Webcam widget"),
description: t(Content.HIDE_WEBCAM_WIDGET),
storageKey: BooleanSetting.hide_webcam_widget,
value: false
},
{
name: t("Dynamic map size"),
description: t(Content.DYNAMIC_MAP_SIZE),
storageKey: BooleanSetting.dynamic_map,
value: false
},
{
name: t("Double default map dimensions"),
description: t(Content.DOUBLE_MAP_DIMENSIONS),
storageKey: BooleanSetting.map_xl,
value: false
},
{
name: t("Display plant animations"),
description: t(Content.PLANT_ANIMATIONS),
storageKey: BooleanSetting.disable_animations,
value: false,
displayInvert: true
},
{
name: t("Read speak logs in browser"),
description: t(Content.BROWSER_SPEAK_LOGS),
storageKey: BooleanSetting.enable_browser_speak,
value: false
},
{
name: t("Discard Unsaved Changes"),
description: t(Content.DISCARD_UNSAVED_CHANGES),
storageKey: BooleanSetting.discard_unsaved,
value: false,
confirmationMessage: t(Content.DISCARD_UNSAVED_CHANGES_CONFIRM)
},
{
name: t("Display virtual FarmBot trail"),
description: t(Content.VIRTUAL_TRAIL),
storageKey: BooleanSetting.display_trail,
value: false,
callback: () => sessionStorage.setItem("virtualTrailRecords", "[]")
},
].map(fetchRealValue));
export const fetchLabFeatures =
(getConfigValue: GetWebAppConfigValue): LabsFeature[] => ([
{
name: t("Internationalize Web App"),
description: t("Turn off to set Web App to English."),
storageKey: BooleanSetting.disable_i18n,
value: false,
displayInvert: true,
callback: () => window.location.reload()
},
{
name: t("Confirm Sequence step deletion"),
description: t(Content.CONFIRM_STEP_DELETION),
storageKey: BooleanSetting.confirm_step_deletion,
value: false
},
{
name: t("Hide Webcam widget"),
description: t(Content.HIDE_WEBCAM_WIDGET),
storageKey: BooleanSetting.hide_webcam_widget,
value: false
},
{
name: t("Dynamic map size"),
description: t(Content.DYNAMIC_MAP_SIZE),
storageKey: BooleanSetting.dynamic_map,
value: false
},
{
name: t("Double default map dimensions"),
description: t(Content.DOUBLE_MAP_DIMENSIONS),
storageKey: BooleanSetting.map_xl,
value: false
},
{
name: t("Display plant animations"),
description: t(Content.PLANT_ANIMATIONS),
storageKey: BooleanSetting.disable_animations,
value: false,
displayInvert: true
},
{
name: t("Read speak logs in browser"),
description: t(Content.BROWSER_SPEAK_LOGS),
storageKey: BooleanSetting.enable_browser_speak,
value: false
},
{
name: t("Discard Unsaved Changes"),
description: t(Content.DISCARD_UNSAVED_CHANGES),
storageKey: BooleanSetting.discard_unsaved,
value: false,
confirmationMessage: t(Content.DISCARD_UNSAVED_CHANGES_CONFIRM)
},
{
name: t("Display virtual FarmBot trail"),
description: t(Content.VIRTUAL_TRAIL),
storageKey: BooleanSetting.display_trail,
value: false,
callback: () => sessionStorage.setItem(VirtualTrail.records, "[]")
},
].map(fetchSettingValue(getConfigValue)));
/** Always allow toggling from true => false (deactivate).
* Require a disclaimer when going from false => true (activate). */
export const maybeToggleFeature =
(x: LabsFeature): LabsFeature | undefined => {
return (x.value
|| !x.confirmationMessage
|| window.confirm(x.confirmationMessage)) ?
toggleFeatureValue(x) : undefined;
};
/** Stub this when testing if need be. */
const fetchVal = (k: BooleanConfigKey) => !!Session.deprecatedGetBool(k);
(getConfigValue: GetWebAppConfigValue, dispatch: Function) =>
(x: LabsFeature): LabsFeature | undefined =>
(x.value
|| !x.confirmationMessage
|| window.confirm(x.confirmationMessage)) ?
toggleFeatureValue(getConfigValue, dispatch)(x) : undefined;
/** Takes a `LabFeature` (probably one with an uninitialized fallback / default
* value) and sets it to the _real_ value that's in the API. */
const fetchRealValue = (x: LabsFeature): LabsFeature => {
return { ...x, value: fetchVal(x.storageKey) };
};
const fetchSettingValue = (getConfigValue: GetWebAppConfigValue) =>
(x: LabsFeature): LabsFeature => {
return { ...x, value: !!getConfigValue(x.storageKey) };
};
/** Toggle the `.value` of a `LabsToggle` object */
const toggleFeatureValue = (x: LabsFeature) => {
return { ...x, value: Session.invertBool(x.storageKey) };
};
const toggleFeatureValue =
(getConfigValue: GetWebAppConfigValue, dispatch: Function) =>
(x: LabsFeature) => {
const value = !getConfigValue(x.storageKey);
dispatch(setWebAppConfigValue(x.storageKey, value));
return { ...x, value };
};

View File

@ -1,14 +1,16 @@
import * as React from "react";
import { fetchLabFeatures, LabsFeature } from "./labs_features_list_data";
import { KeyValShowRow } from "../../controls/key_val_show_row";
import { GetWebAppConfigValue } from "../../config_storage/actions";
interface LabsFeaturesListProps {
onToggle(feature: LabsFeature): void;
getConfigValue: GetWebAppConfigValue;
}
export function LabsFeaturesList(props: LabsFeaturesListProps) {
return <div>
{fetchLabFeatures().map((p, i) => {
{fetchLabFeatures(props.getConfigValue).map((p, i) => {
const displayValue = p.displayInvert ? !p.value : p.value;
return <KeyValShowRow key={i}
label={p.name}

View File

@ -1,12 +1,14 @@
import { Everything } from "../interfaces";
import { Props } from "./interfaces";
import { getUserAccountSettings } from "../resources/selectors";
import { getWebAppConfigValue } from "../config_storage/actions";
export function mapStateToProps(props: Everything): Props {
const user = getUserAccountSettings(props.resources.index);
return {
user,
dispatch: () => { throw new Error("NEVER SHOULD HAPPEN"); }
dispatch: () => { throw new Error("NEVER SHOULD HAPPEN"); },
getConfigValue: getWebAppConfigValue(() => props),
};
}

View File

@ -22,7 +22,7 @@ import { validBotLocationData, validFwConfig } from "./util";
import { BooleanSetting } from "./session_keys";
import { getPathArray } from "./history";
import { FirmwareConfig } from "./config_storage/firmware_configs";
import { getWebAppConfigValue } from "./config_storage/actions";
import { getWebAppConfigValue, GetWebAppConfigValue } from "./config_storage/actions";
import { takeSortedLogs } from "./logs/state_to_props";
/** Remove 300ms delay on touch devices - https://github.com/ftlabs/fastclick */
@ -44,6 +44,7 @@ export interface AppProps {
xySwap: boolean;
firmwareConfig: FirmwareConfig | undefined;
animate: boolean;
getConfigValue: GetWebAppConfigValue;
}
function mapStateToProps(props: Everything): AppProps {
@ -64,6 +65,7 @@ function mapStateToProps(props: Everything): AppProps {
xySwap: !!webAppConfigValue(BooleanSetting.xy_swap),
firmwareConfig: validFwConfig(getFirmwareConfig(props.resources.index)),
animate: !webAppConfigValue(BooleanSetting.disable_animations),
getConfigValue: webAppConfigValue,
};
}
/** Time at which the app gives up and asks the user to refresh */
@ -111,7 +113,8 @@ export class App extends React.Component<AppProps, {}> {
user={this.props.user}
bot={this.props.bot}
dispatch={this.props.dispatch}
logs={this.props.logs} />
logs={this.props.logs}
getConfigValue={this.props.getConfigValue} />
{!syncLoaded && <LoadingPlant animate={this.props.animate} />}
{syncLoaded && this.props.children}
{!(["controls", "account", "regimens"].includes(currentPage)) &&

View File

@ -19,8 +19,6 @@ jest.mock("axios", () => ({
jest.mock("../../session", () => ({
Session: {
fetchStoredToken: jest.fn(),
deprecatedGetNum: () => undefined,
deprecatedGetBool: () => undefined,
getAll: () => undefined,
clear: jest.fn()
}

View File

@ -1,53 +0,0 @@
import { store } from "../redux/store";
import { BooleanConfigKey, NumberConfigKey } from "../config_storage/web_app_configs";
import { edit, save } from "../api/crud";
import { getWebAppConfig } from "../resources/selectors_by_kind";
/**
* 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 app 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);
// tslint:disable-next-line:no-any
return conf && (conf.body as any)[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 }));
// tslint:disable-next-line:no-any
store.dispatch(save(conf.uuid) as any);
}
return val;
}
/** Avoid using this function in new places. Pass props instead. */
export function getNumViaRedux(key: NumberConfigKey): number | 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 setNumViaRedux(key: NumberConfigKey, val: number): number {
const conf = getWebAppConfig(store.getState().resources.index);
if (conf) {
store.dispatch(edit(conf, { [key]: val }));
// tslint:disable-next-line:no-any
store.dispatch(save(conf.uuid) as any);
}
return val;
}

View File

@ -9,12 +9,6 @@ jest.mock("../../history", () => ({
},
}));
jest.mock("../../session", () => ({
Session: {
deprecatedGetBool: () => true // Simulate opt-in to beta features.
}
}));
const mockDevice = {
execScript: jest.fn(() => Promise.resolve({})),
};

View File

@ -4,30 +4,26 @@ import { shallow } from "enzyme";
import { NavBar } from "../index";
import { bot } from "../../__test_support__/fake_state/bot";
import { taggedUser } from "../../__test_support__/user";
import { NavBarProps } from "../interfaces";
describe("NavBar", () => {
const fakeProps = (): NavBarProps => ({
timeOffset: 0,
consistent: true,
logs: [],
bot,
user: taggedUser,
dispatch: jest.fn(),
getConfigValue: jest.fn(),
});
it("has correct parent classname", () => {
const wrapper = shallow(
<NavBar
timeOffset={0}
consistent={true}
logs={[]}
bot={bot}
user={taggedUser}
dispatch={jest.fn()} />
);
const wrapper = shallow(<NavBar {...fakeProps()} />);
expect(wrapper.find("div").first().hasClass("nav-wrapper")).toBeTruthy();
});
it("closes nav menu", () => {
const wrapper = shallow<NavBar>(<NavBar
timeOffset={0}
consistent={true}
logs={[]}
bot={bot}
user={taggedUser}
dispatch={jest.fn()} />);
const wrapper = shallow<NavBar>(<NavBar {...fakeProps()} />);
const link = wrapper.find("Link").first();
link.simulate("click");
expect(wrapper.instance().state.mobileMenuOpen).toBeFalsy();

View File

@ -1,25 +1,5 @@
const mockStorj: Dictionary<number | boolean> = {};
jest.mock("../../session", () => {
return {
Session: {
deprecatedGetNum: (k: string) => {
return mockStorj[k];
},
deprecatedSetNum: (k: string, v: number) => {
mockStorj[k] = v;
},
deprecatedGetBool: (k: string) => {
mockStorj[k] = !!mockStorj[k];
return mockStorj[k];
}
},
// tslint:disable-next-line:no-any
safeNumericSetting: (x: any) => x
};
});
import * as React from "react";
import { mount } from "enzyme";
import { TickerList } from "../ticker_list";
@ -41,6 +21,7 @@ describe("<TickerList />", () => {
logs: [fakeTaggedLog(), fakeTaggedLog()],
tickerListOpen: false,
toggle: jest.fn(),
getConfigValue: x => mockStorj[x],
};
};

View File

@ -55,15 +55,17 @@ export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
const { toggle, close } = this;
const { mobileMenuOpen, tickerListOpen, accountMenuOpen } = this.state;
const { logs, timeOffset } = this.props;
const { logs, timeOffset, getConfigValue } = this.props;
const tickerListProps = {
logs, tickerListOpen, toggle, timeOffset, getConfigValue
};
return <ErrorBoundary>
<div className="nav-wrapper">
<nav role="navigation">
<Row>
<Col xs={12}>
<div>
<TickerList {...{ logs, tickerListOpen, toggle, timeOffset }} />
<TickerList {...tickerListProps} />
<div className="nav-group">
<div className="nav-left">
<i

View File

@ -1,5 +1,6 @@
import { BotState } from "../devices/interfaces";
import { TaggedUser, TaggedLog } from "farmbot";
import { GetWebAppConfigValue } from "../config_storage/actions";
export interface NavButtonProps {
user: TaggedUser | undefined;
@ -16,6 +17,7 @@ export interface NavBarProps {
user: TaggedUser | undefined;
dispatch: Function;
timeOffset: number;
getConfigValue: GetWebAppConfigValue;
}
export interface NavBarState {
@ -36,6 +38,7 @@ export interface TickerListProps {
logs: TaggedLog[]
tickerListOpen: boolean;
timeOffset: number;
getConfigValue: GetWebAppConfigValue;
}
export interface NavLinksProps {

View File

@ -5,18 +5,20 @@ import { TickerListProps } from "./interfaces";
import { Link } from "react-router";
import { t } from "i18next";
import { formatLogTime } from "../logs/index";
import { Session, safeNumericSetting } from "../session";
import { safeNumericSetting } from "../session";
import { ErrorBoundary } from "../error_boundary";
import { ALLOWED_MESSAGE_TYPES, TaggedLog, SpecialStatus } from "farmbot";
import { filterByVerbosity } from "../logs/components/logs_table";
import { isNumber } from "lodash";
import { GetWebAppConfigValue } from "../config_storage/actions";
/** Get current verbosity filter level for a message type from WebAppConfig. */
const getFilterLevel = (type: ALLOWED_MESSAGE_TYPES): number => {
const filterLevel =
Session.deprecatedGetNum(safeNumericSetting(type + "_log"));
return isNumber(filterLevel) ? filterLevel : 1;
};
const getFilterLevel = (getConfigValue: GetWebAppConfigValue) =>
(type: ALLOWED_MESSAGE_TYPES): number => {
const filterLevel =
getConfigValue(safeNumericSetting(type + "_log"));
return isNumber(filterLevel) ? filterLevel : 1;
};
/** Generate a fallback TaggedLog to display in the first line of the ticker. */
const generateFallbackLog = (uuid: string, message: string): TaggedLog => {
@ -34,19 +36,21 @@ const generateFallbackLog = (uuid: string, message: string): TaggedLog => {
};
/** Choose the log to display in the first line of the ticker. */
const getfirstTickerLog = (logs: TaggedLog[]): TaggedLog => {
if (logs.length == 0) {
return generateFallbackLog("no_logs_yet", t("No logs yet."));
} else {
const filteredLogs = filterByVerbosity(getFilterLevel, logs);
if (filteredLogs.length > 0) {
return filteredLogs[0];
const getfirstTickerLog = (getConfigValue: GetWebAppConfigValue) =>
(logs: TaggedLog[]): TaggedLog => {
if (logs.length == 0) {
return generateFallbackLog("no_logs_yet", t("No logs yet."));
} else {
return generateFallbackLog("no_logs_to_display",
t("No logs to display. Visit Logs page to view filters."));
const filteredLogs =
filterByVerbosity(getFilterLevel(getConfigValue), logs);
if (filteredLogs.length > 0) {
return filteredLogs[0];
} else {
return generateFallbackLog("no_logs_to_display",
t("No logs to display. Visit Logs page to view filters."));
}
}
}
};
};
/** Format a single log for display in the ticker. */
const Ticker = (log: TaggedLog, timeOffset: number) => {
@ -70,10 +74,11 @@ export let TickerList = (props: TickerListProps) => {
return <ErrorBoundary>
<div className="ticker-list" onClick={props.toggle("tickerListOpen")} >
<div className="first-ticker">
{Ticker(getfirstTickerLog(props.logs), props.timeOffset)}
{Ticker(getfirstTickerLog(props.getConfigValue)(props.logs),
props.timeOffset)}
</div>
<Collapse isOpen={props.tickerListOpen}>
{filterByVerbosity(getFilterLevel, props.logs)
{filterByVerbosity(getFilterLevel(props.getConfigValue), props.logs)
// Don't use first log again since it's already displayed in first row
.filter((_, index) => index !== 0)
.map((log: TaggedLog) => Ticker(log, props.timeOffset))}

View File

@ -61,7 +61,8 @@ describe("<AllSteps/>", () => {
sequence={TEST_CASE}
onDrop={() => { }}
dispatch={jest.fn()}
resources={buildResourceIndex([]).index} />);
resources={buildResourceIndex([]).index}
confirmStepDeletion={false} />);
[TileMoveRelative, TileReadPin, TileWritePin]
.map(q => {
expect(el.find(q).length).toEqual(1);

View File

@ -55,6 +55,7 @@ describe("<SequenceEditorMiddleActive/>", () => {
farmwareConfigs: {},
},
shouldDisplay: jest.fn(),
confirmStepDeletion: false,
};
}

View File

@ -23,6 +23,7 @@ describe("<SequenceEditorMiddle/>", () => {
farmwareConfigs: {},
},
shouldDisplay: jest.fn(),
confirmStepDeletion: false,
};
}

View File

@ -29,6 +29,7 @@ describe("<Sequences/>", () => {
farmwareConfigs: {},
},
shouldDisplay: jest.fn(),
confirmStepDeletion: false,
};
}

View File

@ -17,6 +17,7 @@ describe("<StepIconGroup />", () => {
step: { kind: "wait", args: { milliseconds: 100 } },
sequence: fakeSequence(),
helpText: "helpful text",
confirmStepDeletion: false,
});
it("renders", () => {

View File

@ -17,6 +17,7 @@ interface AllStepsProps {
hardwareFlags?: HardwareFlags;
farmwareInfo?: FarmwareInfo;
shouldDisplay?: ShouldDisplay;
confirmStepDeletion: boolean;
}
export class AllSteps extends React.Component<AllStepsProps, {}> {
@ -50,6 +51,7 @@ export class AllSteps extends React.Component<AllStepsProps, {}> {
hardwareFlags,
farmwareInfo,
shouldDisplay,
confirmStepDeletion: this.props.confirmStepDeletion,
})}
</div>
</StepDragger>

View File

@ -30,6 +30,7 @@ export interface Props {
hardwareFlags: HardwareFlags;
farmwareInfo: FarmwareInfo;
shouldDisplay: ShouldDisplay;
confirmStepDeletion: boolean;
}
export interface SequenceEditorMiddleProps {
@ -40,11 +41,11 @@ export interface SequenceEditorMiddleProps {
hardwareFlags: HardwareFlags;
farmwareInfo: FarmwareInfo;
shouldDisplay: ShouldDisplay;
confirmStepDeletion: boolean;
}
export interface ActiveMiddleProps extends SequenceEditorMiddleProps {
sequence: TaggedSequence;
syncStatus: SyncStatus;
}
export type ChannelName = ALLOWED_CHANNEL_NAMES;
@ -156,4 +157,5 @@ export interface StepParams {
hardwareFlags?: HardwareFlags;
farmwareInfo?: FarmwareInfo;
shouldDisplay?: ShouldDisplay;
confirmStepDeletion: boolean;
}

View File

@ -7,24 +7,17 @@ import { SequenceEditorMiddleActive } from "./sequence_editor_middle_active";
export class SequenceEditorMiddle
extends React.Component<SequenceEditorMiddleProps, {}> {
render() {
const {
dispatch,
sequence,
resources,
syncStatus,
hardwareFlags,
farmwareInfo,
shouldDisplay,
} = this.props;
const { sequence } = this.props;
if (sequence && isTaggedSequence(sequence)) {
return <SequenceEditorMiddleActive
dispatch={dispatch}
dispatch={this.props.dispatch}
sequence={sequence}
resources={resources}
syncStatus={syncStatus}
hardwareFlags={hardwareFlags}
farmwareInfo={farmwareInfo}
shouldDisplay={shouldDisplay} />;
resources={this.props.resources}
syncStatus={this.props.syncStatus}
hardwareFlags={this.props.hardwareFlags}
farmwareInfo={this.props.farmwareInfo}
shouldDisplay={this.props.shouldDisplay}
confirmStepDeletion={this.props.confirmStepDeletion} />;
} else {
return <SequenceEditorMiddleInactive />;
}

View File

@ -43,7 +43,8 @@ export class Sequences extends React.Component<Props, {}> {
resources={this.props.resources}
hardwareFlags={this.props.hardwareFlags}
farmwareInfo={this.props.farmwareInfo}
shouldDisplay={this.props.shouldDisplay} />
shouldDisplay={this.props.shouldDisplay}
confirmStepDeletion={this.props.confirmStepDeletion} />
</CenterPanel>
<RightPanel
className="step-button-cluster-panel"

View File

@ -9,6 +9,8 @@ import {
betterCompact, shouldDisplay, determineInstalledOsVersion, validFwConfig
} from "../util";
import { getWebAppConfig } from "../resources/selectors";
import { BooleanSetting } from "../session_keys";
import { getWebAppConfigValue } from "../config_storage/actions";
export function mapStateToProps(props: Everything): Props {
const uuid = props.resources.consumers.sequences.current;
@ -63,6 +65,9 @@ export function mapStateToProps(props: Everything): Props {
const installedOsVersion = determineInstalledOsVersion(
props.bot, maybeGetDevice(props.resources.index));
const confirmStepDeletion =
!!getWebAppConfigValue(() => props)(BooleanSetting.confirm_step_deletion);
return {
dispatch: props.dispatch,
sequences: selectAllSequences(props.resources.index),
@ -81,5 +86,6 @@ export function mapStateToProps(props: Everything): Props {
farmwareConfigs,
},
shouldDisplay: shouldDisplay(installedOsVersion, props.bot.minOsFeatureData),
confirmStepDeletion,
};
}

View File

@ -10,6 +10,7 @@ export interface StepIconBarProps {
step: SequenceBodyItem;
sequence: TaggedSequence;
helpText: string;
confirmStepDeletion: boolean;
}
export function StepUpDownButtonPopover(
@ -24,10 +25,13 @@ export function StepUpDownButtonPopover(
}
export function StepIconGroup(props: StepIconBarProps) {
const { index, dispatch, step, sequence, helpText } = props;
const {
index, dispatch, step, sequence, helpText, confirmStepDeletion
} = props;
const onClone = () => dispatch(splice({ step, index, sequence }));
const onTrash = () => remove({ dispatch, index, sequence });
const onTrash = () =>
remove({ dispatch, index, sequence, confirmStepDeletion });
const onMove = (delta: number) => () => {
const to = Math.max(index + delta, 0);
dispatch(move({ step, sequence, from: index, to }));

View File

@ -1,39 +1,76 @@
const mockStorj: Dictionary<boolean> = {};
jest.mock("../../../api/crud", () => ({
overwrite: jest.fn(),
}));
jest.mock("../../../session", () => {
return {
Session: {
deprecatedGetBool: (k: string) => {
mockStorj[k] = !!mockStorj[k];
return mockStorj[k];
}
}
};
});
import { Dictionary } from "farmbot";
import { remove } from "../index";
import { remove, move, splice } from "../index";
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { BooleanSetting } from "../../../session_keys";
import { overwrite } from "../../../api/crud";
import { Wait } from "farmbot";
describe("remove()", () => {
const fakeProps = () => ({
index: 0,
dispatch: jest.fn(),
sequence: fakeSequence(),
confirmStepDeletion: false,
});
it("deletes step without confirmation", () => {
const dispatch = jest.fn();
mockStorj[BooleanSetting.confirm_step_deletion] = false;
remove({ index: 0, dispatch, sequence: fakeSequence() });
expect(dispatch).toHaveBeenCalled();
const p = fakeProps();
remove(p);
expect(p.dispatch).toHaveBeenCalled();
});
it("deletes step with confirmation", () => {
const dispatch = jest.fn();
mockStorj[BooleanSetting.confirm_step_deletion] = true;
const p = fakeProps();
p.confirmStepDeletion = true;
window.confirm = jest.fn();
remove({ index: 0, dispatch, sequence: fakeSequence() });
remove(p);
expect(window.confirm).toHaveBeenCalledWith(
expect.stringContaining("delete this step?"));
expect(dispatch).not.toHaveBeenCalled();
expect(p.dispatch).not.toHaveBeenCalled();
window.confirm = jest.fn(() => true);
remove({ index: 0, dispatch, sequence: fakeSequence() });
expect(dispatch).toHaveBeenCalled();
remove(p);
expect(p.dispatch).toHaveBeenCalled();
});
});
describe("move()", () => {
const sequence = fakeSequence();
const step1: Wait = { kind: "wait", args: { milliseconds: 100 } };
const step2: Wait = { kind: "wait", args: { milliseconds: 200 } };
sequence.body.body = [step1, step2];
const fakeProps = () => ({ step: step2, sequence, to: 0, from: 1, });
it("moves step backward", () => {
const p = fakeProps();
p.from = 1;
p.to = 0;
move(p);
expect(overwrite).toHaveBeenCalledWith(expect.any(Object),
expect.objectContaining({ body: [step2, step1] }));
});
it("moves step forward", () => {
const p = fakeProps();
p.sequence.body.body = [step2, step1];
p.from = 0;
p.to = 2;
move(p);
expect(overwrite).toHaveBeenCalledWith(expect.any(Object),
expect.objectContaining({ body: [step1, step2] }));
});
});
describe("splice()", () => {
const sequence = fakeSequence();
const step: Wait = { kind: "wait", args: { milliseconds: 100 } };
const fakeProps = () => ({ step, sequence, index: 1, });
it("adds step", () => {
const p = fakeProps();
splice(p);
expect(overwrite).toHaveBeenCalledWith(expect.any(Object),
expect.objectContaining({ body: [step] }));
});
});

View File

@ -289,7 +289,8 @@ describe("Pin and Peripheral support files", () => {
dispatch,
currentSequence,
currentStep,
index
index,
confirmStepDeletion: false,
};
const callback = setArgsDotPinNumber(stepParams);
const ddi: DropDownItem = { label: "hmm", value: 0 };

View File

@ -26,7 +26,8 @@ describe("<TileExecuteScript/>", () => {
firstPartyFarmwareNames: ["one"],
showFirstPartyFarmware: false,
farmwareConfigs: { "farmware-to-execute": [] },
}
},
confirmStepDeletion: false,
};
};

View File

@ -20,11 +20,12 @@ function fakeProps(): ExecBlockParams {
};
return {
currentSequence: fakeSequence(),
currentStep: currentStep,
currentStep,
dispatch: jest.fn(),
index: 0,
resources: emptyState().index,
shouldDisplay: () => false,
confirmStepDeletion: false,
};
}

View File

@ -1,5 +1,5 @@
import * as React from "react";
import { TileFindHome } from "../tile_find_home";
import { TileFindHome, FindHomeParams } from "../tile_find_home";
import { mount } from "enzyme";
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { FindHome } from "farmbot/dist";
@ -7,9 +7,10 @@ import { emptyState } from "../../../resources/reducer";
import {
fakeHardwareFlags
} from "../../../__test_support__/sequence_hardware_settings";
import { HardwareFlags } from "../../interfaces";
describe("<TileFindHome/>", () => {
const fakeProps = () => {
const fakeProps = (): FindHomeParams => {
const currentStep: FindHome = {
kind: "find_home",
args: {
@ -23,7 +24,8 @@ describe("<TileFindHome/>", () => {
dispatch: jest.fn(),
index: 0,
resources: emptyState().index,
hardwareFlags: fakeHardwareFlags()
hardwareFlags: fakeHardwareFlags(),
confirmStepDeletion: false,
};
};
@ -49,7 +51,7 @@ describe("<TileFindHome/>", () => {
it("doesn't render warning", () => {
const p = fakeProps();
p.currentStep.args.axis = "x";
p.hardwareFlags.findHomeEnabled.x = true;
(p.hardwareFlags as HardwareFlags).findHomeEnabled.x = true;
const wrapper = mount(<TileFindHome {...p} />);
expect(wrapper.text()).not.toContain(CONFLICT_TEXT_BASE);
});
@ -57,7 +59,7 @@ describe("<TileFindHome/>", () => {
it("renders warning: all axes", () => {
const p = fakeProps();
p.currentStep.args.axis = "all";
p.hardwareFlags.findHomeEnabled.x = false;
(p.hardwareFlags as HardwareFlags).findHomeEnabled.x = false;
const wrapper = mount(<TileFindHome {...p} />);
expect(wrapper.text()).toContain(CONFLICT_TEXT_BASE + ": x");
});
@ -65,7 +67,7 @@ describe("<TileFindHome/>", () => {
it("renders warning: one axis", () => {
const p = fakeProps();
p.currentStep.args.axis = "x";
p.hardwareFlags.findHomeEnabled.x = false;
(p.hardwareFlags as HardwareFlags).findHomeEnabled.x = false;
const wrapper = mount(<TileFindHome {...p} />);
expect(wrapper.text()).toContain(CONFLICT_TEXT_BASE + ": x");
});

View File

@ -23,7 +23,8 @@ describe("<TileIf/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={emptyState().index} />)
resources={emptyState().index}
confirmStepDeletion={false} />)
};
}

View File

@ -38,7 +38,8 @@ describe("<TileMoveAbsolute/>", () => {
dispatch: jest.fn(),
index: 0,
resources: emptyState().index,
hardwareFlags: fakeHardwareFlags()
hardwareFlags: fakeHardwareFlags(),
confirmStepDeletion: false,
};
};
@ -100,7 +101,8 @@ describe("<TileMoveAbsolute/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={index} />).instance() as TileMoveAbsolute;
resources={index}
confirmStepDeletion={false} />).instance() as TileMoveAbsolute;
expect(component.tool).toEqual(tool);
});

View File

@ -22,7 +22,8 @@ describe("<TileMoveRelative/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={emptyState().index} />)
resources={emptyState().index}
confirmStepDeletion={false} />)
};
}

View File

@ -34,7 +34,8 @@ describe("Pin tile support functions", () => {
currentStep,
dispatch,
index,
resources
resources,
confirmStepDeletion: false,
};
}

View File

@ -21,7 +21,8 @@ describe("<TileReadPin/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={emptyState().index} />)
resources={emptyState().index}
confirmStepDeletion={false} />)
};
}

View File

@ -28,6 +28,7 @@ describe("<TileSendMessage/>", () => {
dispatch: jest.fn(),
index: 0,
resources: emptyState().index,
confirmStepDeletion: false,
};
}

View File

@ -17,7 +17,8 @@ describe("<TileTakePhoto/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={emptyState().index} />)
resources={emptyState().index}
confirmStepDeletion={false} />)
};
}

View File

@ -19,7 +19,8 @@ describe("<TileWait/>", () => {
currentStep={currentStep}
dispatch={jest.fn()}
index={0}
resources={emptyState().index} />)
resources={emptyState().index}
confirmStepDeletion={false} />)
};
}

View File

@ -20,7 +20,8 @@ describe("<TileWritePin/>", () => {
currentStep: currentStep,
dispatch: jest.fn(),
index: 0,
resources: emptyState().index
resources: emptyState().index,
confirmStepDeletion: false,
};
}

View File

@ -20,8 +20,6 @@ import * as _ from "lodash";
import { overwrite } from "../../api/crud";
import { TileFindHome } from "./tile_find_home";
import { t } from "i18next";
import { Session } from "../../session";
import { BooleanSetting } from "../../session_keys";
interface MoveParams {
step: Step;
@ -64,10 +62,12 @@ interface RemoveParams {
index: number;
dispatch: Function;
sequence: TaggedSequence;
confirmStepDeletion: boolean;
}
export function remove({ dispatch, index, sequence }: RemoveParams) {
if (!Session.deprecatedGetBool(BooleanSetting.confirm_step_deletion) ||
export function remove(props: RemoveParams) {
const { dispatch, index, sequence, confirmStepDeletion } = props;
if (!confirmStepDeletion ||
confirm(t("Are you sure you want to delete this step?"))) {
const original = sequence;
const update = defensiveClone(original);

View File

@ -21,7 +21,8 @@ export function ExecuteBlock(p: StepParams) {
index={p.index}
dispatch={p.dispatch}
resources={p.resources}
shouldDisplay={p.shouldDisplay || (() => false)} />;
shouldDisplay={p.shouldDisplay || (() => false)}
confirmStepDeletion={p.confirmStepDeletion} />;
} else {
throw new Error("Thats not an execute block!");
}
@ -34,6 +35,7 @@ export interface ExecBlockParams {
index: number;
resources: ResourceIndex;
shouldDisplay: ShouldDisplay;
confirmStepDeletion: boolean;
}
export class RefactoredExecuteBlock extends React.Component<ExecBlockParams, {}> {
changeSelection = (input: DropDownItem) => {
@ -99,7 +101,8 @@ export class RefactoredExecuteBlock extends React.Component<ExecBlockParams, {}>
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={this.props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={12}>

View File

@ -9,8 +9,8 @@ import { editStep } from "../../api/crud";
import { ExecuteScript, FarmwareConfig } from "farmbot";
import { FarmwareInputs, farmwareList } from "./tile_execute_script_support";
export function TileExecuteScript({
dispatch, currentStep, index, currentSequence, farmwareInfo }: StepParams) {
export function TileExecuteScript(props: StepParams) {
const { dispatch, currentStep, index, currentSequence, farmwareInfo } = props;
if (currentStep.kind === "execute_script") {
const farmwareName = currentStep.args.label;
@ -63,7 +63,8 @@ export function TileExecuteScript({
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={12}>

View File

@ -20,18 +20,20 @@ export function TileFindHome(props: StepParams) {
dispatch={props.dispatch}
index={props.index}
resources={props.resources}
hardwareFlags={props.hardwareFlags} />;
hardwareFlags={props.hardwareFlags}
confirmStepDeletion={props.confirmStepDeletion} />;
} else {
throw new Error("TileFindHome expects find_home");
}
}
interface FindHomeParams {
export interface FindHomeParams {
currentStep: FindHome;
currentSequence: TaggedSequence;
dispatch: Function;
index: number;
resources: ResourceIndex;
hardwareFlags: HardwareFlags | undefined;
confirmStepDeletion: boolean;
}
const AXIS_CHOICES: ALLOWED_AXIS[] = ["x", "y", "z", "all"];
@ -87,7 +89,8 @@ class InnerFindHome extends React.Component<FindHomeParams, {}> {
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index}>
index={index}
confirmStepDeletion={this.props.confirmStepDeletion}>
{some(this.settingConflicts) &&
<StepWarning
warning={this.settingConflictWarning}

View File

@ -10,7 +10,8 @@ export function TileIf(props: StepParams) {
dispatch={props.dispatch}
index={props.index}
resources={props.resources}
shouldDisplay={props.shouldDisplay} />;
shouldDisplay={props.shouldDisplay}
confirmStepDeletion={props.confirmStepDeletion} />;
} else {
return <p> Expected "_if" node</p>;
}

View File

@ -23,7 +23,8 @@ describe("<Else/>", () => {
currentStep,
dispatch: jest.fn(),
index: 0,
resources: emptyState().index
resources: emptyState().index,
confirmStepDeletion: false,
};
}

View File

@ -25,6 +25,7 @@ describe("<If_/>", () => {
index: 0,
resources: emptyState().index,
shouldDisplay: jest.fn(),
confirmStepDeletion: false,
};
}

View File

@ -46,6 +46,7 @@ function fakeProps(): IfParams {
index: 0,
resources: fakeResourceIndex,
shouldDisplay: jest.fn(),
confirmStepDeletion: false,
};
}

View File

@ -23,7 +23,8 @@ describe("<Then/>", () => {
currentStep,
dispatch: jest.fn(),
index: 0,
resources: emptyState().index
resources: emptyState().index,
confirmStepDeletion: false,
};
}

View File

@ -26,6 +26,7 @@ export interface IfParams {
index: number;
resources: ResourceIndex;
shouldDisplay?: ShouldDisplay;
confirmStepDeletion: boolean;
}
export type Operator = "lhs"
@ -88,7 +89,8 @@ export function InnerIf(props: IfParams) {
index,
dispatch,
currentStep,
currentSequence
currentSequence,
confirmStepDeletion,
} = props;
const recursive = isRecursive(currentStep, currentSequence);
const className = "if-step";
@ -99,7 +101,8 @@ export function InnerIf(props: IfParams) {
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index}>
index={index}
confirmStepDeletion={confirmStepDeletion}>
{recursive && (
<span>
<i className="fa fa-exclamation-triangle"></i>

View File

@ -169,7 +169,8 @@ export class TileMoveAbsolute extends Component<StepParams, MoveAbsState> {
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index}>
index={index}
confirmStepDeletion={this.props.confirmStepDeletion}>
{_.some(this.settingConflicts) &&
<StepWarning
warning={this.settingConflictWarning}

View File

@ -6,8 +6,8 @@ import { ToolTips } from "../../constants";
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
import { Row, Col } from "../../ui/index";
export function TileMoveRelative({
dispatch, currentStep, index, currentSequence }: StepParams) {
export function TileMoveRelative(props: StepParams) {
const { dispatch, currentStep, index, currentSequence } = props;
const className = "move-relative-step";
return <StepWrapper>
<StepHeader
@ -16,7 +16,8 @@ export function TileMoveRelative({
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={6} md={3}>

View File

@ -35,7 +35,8 @@ export function TileReadPin(props: StepParams) {
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={6} md={6}>

View File

@ -23,7 +23,8 @@ export function TileSendMessage(props: StepParams) {
currentSequence={props.currentSequence}
dispatch={props.dispatch}
index={props.index}
resources={props.resources} />;
resources={props.resources}
confirmStepDeletion={props.confirmStepDeletion} />;
} else {
throw new Error("TileSendMessage expects send_message");
}
@ -35,6 +36,7 @@ interface SendMessageParams {
dispatch: Function;
index: number;
resources: ResourceIndex;
confirmStepDeletion: boolean;
}
export class RefactoredSendMessage
@ -102,7 +104,8 @@ export class RefactoredSendMessage
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={this.props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={12}>

View File

@ -6,8 +6,8 @@ import { StepWrapper, StepHeader, StepContent } from "../step_ui";
import { Col, Row } from "../../ui/index";
import { t } from "i18next";
export function TileTakePhoto({
dispatch, currentStep, index, currentSequence }: StepParams) {
export function TileTakePhoto(props: StepParams) {
const { dispatch, currentStep, index, currentSequence } = props;
const className = "take-photo-step";
return <StepWrapper>
<StepHeader
@ -16,7 +16,8 @@ export function TileTakePhoto({
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={12}>

View File

@ -6,8 +6,8 @@ import { ToolTips } from "../../constants";
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
import { Row, Col } from "../../ui/index";
export function TileWait({
dispatch, currentStep, index, currentSequence }: StepParams) {
export function TileWait(props: StepParams) {
const { dispatch, currentStep, index, currentSequence } = props;
const className = "wait-step";
return <StepWrapper>
<StepHeader
@ -16,7 +16,8 @@ export function TileWait({
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={6} md={3}>

View File

@ -46,7 +46,8 @@ export function TileWritePin(props: StepParams) {
currentSequence={currentSequence}
currentStep={currentStep}
dispatch={dispatch}
index={index} />
index={index}
confirmStepDeletion={props.confirmStepDeletion} />
<StepContent className={className}>
<Row>
<Col xs={6} md={6}>

View File

@ -30,7 +30,8 @@ describe("<StepHeader />", () => {
currentStep: { kind: "take_photo", args: {} },
dispatch: jest.fn(),
index: 0,
children: "child"
children: "child",
confirmStepDeletion: false,
};
it("renders", () => {

View File

@ -13,6 +13,7 @@ export interface StepHeaderProps {
currentStep: SequenceBodyItem;
dispatch: Function;
index: number;
confirmStepDeletion: boolean;
}
export function StepHeader(props: StepHeaderProps) {
@ -22,7 +23,8 @@ export function StepHeader(props: StepHeaderProps) {
currentSequence,
currentStep,
dispatch,
index
index,
confirmStepDeletion,
} = props;
return <Row>
<Col sm={12}>
@ -37,7 +39,8 @@ export function StepHeader(props: StepHeaderProps) {
dispatch={dispatch}
step={currentStep}
sequence={currentSequence}
helpText={t(helpText)} />
helpText={t(helpText)}
confirmStepDeletion={confirmStepDeletion} />
{props.children}
</div>
</Col>

View File

@ -2,7 +2,6 @@ import { AuthState } from "./auth/interfaces";
import { box } from "boxed_value";
import { BooleanConfigKey, NumberConfigKey } from "./config_storage/web_app_configs";
import { BooleanSetting, NumericSetting } from "./session_keys";
import * as LegacyShim from "./config/legacy_shims";
/** The `Session` namespace is a wrapper for `localStorage`.
* Use this to avoid direct access of `localStorage` where possible.
@ -43,33 +42,6 @@ export namespace Session {
window.location.assign(window.location.origin || "/");
return undefined as never;
}
/** @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 {
return LegacyShim.getBoolViaRedux(key);
}
/** @deprecated Store a boolean value in `localStorage` */
export function setBool(key: BooleanConfigKey, val: boolean): boolean {
return LegacyShim.setBoolViaRedux(key, val);
}
/** @deprecated */
export function invertBool(key: BooleanConfigKey): boolean {
return Session.setBool(key, !Session.deprecatedGetBool(key));
}
/** @deprecated Extract numeric settings from `localStorage`. Returns `undefined` when
* none are found. */
export function deprecatedGetNum(key: NumberConfigKey): number | undefined {
return LegacyShim.getNumViaRedux(key);
}
/** @deprecated Set a numeric value in `localStorage`. */
export function deprecatedSetNum(key: NumberConfigKey, val: number): void {
LegacyShim.setNumViaRedux(key, val);
}
}
export const isBooleanSetting =