Done: Start at midnight, disable dropdown if not auto-updating, move OTA selector to device pane. TODO: Use user-selected hour format (24 v. 12h format)

pull/1567/head
Rick Carlino 2019-11-10 18:49:37 -06:00
parent 19a6cabf70
commit de7af7c867
8 changed files with 79 additions and 60 deletions

View File

@ -52,7 +52,7 @@ describe("<FarmbotOsSettings />", () => {
it("renders settings", () => {
const osSettings = mount(<FarmbotOsSettings {...fakeProps()} />);
expect(osSettings.find("input").length).toBe(1);
expect(osSettings.find("button").length).toBe(6);
expect(osSettings.find("button").length).toBe(7);
["NAME", "TIME ZONE", "FARMBOT OS", "CAMERA", "FIRMWARE"]
.map(string => expect(osSettings.text()).toContain(string));
});

View File

@ -121,25 +121,6 @@ export class FarmbotOsSettings
networkState={this.props.botToMqttStatus}
lockOpen={process.env.NODE_ENV !== "production"
|| this.props.isValidFbosConfig}>
<FarmbotOsRow
bot={this.props.bot}
osReleaseNotesHeading={this.osReleaseNotes.heading}
osReleaseNotes={this.osReleaseNotes.notes}
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig}
shouldDisplay={this.props.shouldDisplay}
botOnline={botOnline}
botToMqttLastSeen={new Date(this.props.botToMqttLastSeen).getTime()}
timeSettings={this.props.timeSettings}
deviceAccount={this.props.deviceAccount} />
<AutoUpdateRow
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig} />
{(location.host.includes("localhost")
|| !isUndefined(sourceFbosConfig("auto_sync").value)) &&
<AutoSyncRow
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig} />}
<CameraSelection
env={this.props.env}
botOnline={botOnline}
@ -154,6 +135,26 @@ export class FarmbotOsSettings
shouldDisplay={this.props.shouldDisplay}
timeSettings={this.props.timeSettings}
sourceFbosConfig={sourceFbosConfig} />
<AutoUpdateRow
device={this.props.deviceAccount}
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig} />
<FarmbotOsRow
bot={this.props.bot}
osReleaseNotesHeading={this.osReleaseNotes.heading}
osReleaseNotes={this.osReleaseNotes.notes}
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig}
shouldDisplay={this.props.shouldDisplay}
botOnline={botOnline}
botToMqttLastSeen={new Date(this.props.botToMqttLastSeen).getTime()}
timeSettings={this.props.timeSettings}
deviceAccount={this.props.deviceAccount} />
{(location.host.includes("localhost")
|| !isUndefined(sourceFbosConfig("auto_sync").value)) &&
<AutoSyncRow
dispatch={this.props.dispatch}
sourceFbosConfig={sourceFbosConfig} />}
{this.props.shouldDisplay(Feature.boot_sequence) &&
<BootSequenceSelector />}
<PowerAndReset

View File

@ -11,7 +11,7 @@ import { fakeState } from "../../../../__test_support__/fake_state";
import { edit, save } from "../../../../api/crud";
import { fakeFbosConfig } from "../../../../__test_support__/fake_state/resources";
import {
buildResourceIndex
buildResourceIndex, fakeDevice
} from "../../../../__test_support__/resource_index_builder";
describe("<AutoUpdateRow/>", () => {
@ -20,6 +20,7 @@ describe("<AutoUpdateRow/>", () => {
state.resources = buildResourceIndex([fakeConfig]);
const fakeProps = (): AutoUpdateRowProps => ({
device: fakeDevice(),
dispatch: jest.fn(x => x(jest.fn(), () => state)),
sourceFbosConfig: () => ({ value: 1, consistent: true })
});
@ -33,7 +34,7 @@ describe("<AutoUpdateRow/>", () => {
const p = fakeProps();
p.sourceFbosConfig = () => ({ value: 0, consistent: true });
const wrapper = mount(<AutoUpdateRow {...p} />);
wrapper.find("button").first().simulate("click");
wrapper.find("button").at(1).simulate("click");
expect(edit).toHaveBeenCalledWith(fakeConfig, { os_auto_update: true });
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
});
@ -42,7 +43,7 @@ describe("<AutoUpdateRow/>", () => {
const p = fakeProps();
p.sourceFbosConfig = () => ({ value: 1, consistent: true });
const wrapper = mount(<AutoUpdateRow {...p} />);
wrapper.find("button").first().simulate("click");
wrapper.find("button").at(1).simulate("click");
expect(edit).toHaveBeenCalledWith(fakeConfig, { os_auto_update: false });
expect(save).toHaveBeenCalledWith(fakeConfig.uuid);
});

View File

@ -6,26 +6,33 @@ import { updateConfig } from "../../actions";
import { Content } from "../../../constants";
import { AutoUpdateRowProps } from "./interfaces";
import { t } from "../../../i18next_wrapper";
import { OtaTimeSelector, changeOtaHour } from "./ota_time_selector";
export function AutoUpdateRow(props: AutoUpdateRowProps) {
const osAutoUpdate = props.sourceFbosConfig("os_auto_update");
return <Row>
<Col xs={ColWidth.label}>
<label>
{t("FARMBOT OS AUTO UPDATE")}
</label>
</Col>
<Col xs={ColWidth.description}>
<p>
{t(Content.OS_AUTO_UPDATE)}
</p>
</Col>
<Col xs={ColWidth.button}>
<ToggleButton toggleValue={osAutoUpdate.value}
dim={!osAutoUpdate.consistent}
toggleAction={() => props.dispatch(updateConfig({
os_auto_update: !osAutoUpdate.value
}))} />
</Col>
</Row>;
return <div>
<OtaTimeSelector
disabled={!osAutoUpdate.value}
value={props.device.body.ota_hour}
onChange={changeOtaHour(props.dispatch, props.device)} />
<Row>
<Col xs={ColWidth.label}>
<label>
{t("FARMBOT OS AUTO UPDATE")}
</label>
</Col>
<Col xs={ColWidth.description}>
<p>
{t(Content.OS_AUTO_UPDATE)}
</p>
</Col>
<Col xs={ColWidth.button}>
<ToggleButton toggleValue={osAutoUpdate.value}
dim={!osAutoUpdate.consistent}
toggleAction={() => props.dispatch(updateConfig({
os_auto_update: !osAutoUpdate.value
}))} />
</Col>
</Row>
</div>;
}

View File

@ -13,7 +13,6 @@ import moment from "moment";
import { timeFormatString } from "../../../util";
import { TimeSettings } from "../../../interfaces";
import { StringConfigKey } from "farmbot/dist/resources/configs/fbos";
import { OtaTimeSelector, changeOtaHour } from "./ota_time_selector";
/** Return an indicator color for the given temperature (C). */
export const colorFromTemp = (temp: number | undefined): string => {
@ -280,9 +279,6 @@ export function FbosDetails(props: FbosDetailsProps) {
<VoltageDisplay chip={target} throttled={throttled} />
<BetaReleaseOptIn
dispatch={props.dispatch} sourceFbosConfig={props.sourceFbosConfig} />
<OtaTimeSelector
onChange={changeOtaHour(props.dispatch, props.deviceAccount)}
value={props.deviceAccount.body.ota_hour} />
{last_ota_checkup && <p><b>{t("Last checked for updates")}: </b>
{reformatDatetime(last_ota_checkup, props.timeSettings)}</p>}
{last_ota && <p><b>{t("Last updated")}: </b>

View File

@ -13,6 +13,7 @@ export interface AutoSyncRowProps {
export interface AutoUpdateRowProps {
dispatch: Function;
sourceFbosConfig: SourceFbosConfig;
device: TaggedDevice;
}
export interface CameraSelectionProps {

View File

@ -7,14 +7,20 @@ import { fakeDevice } from "../../../../../__test_support__/resource_index_build
describe("OTA time selector", () => {
it("selects an OTA update time", () => {
const onUpdate = jest.fn();
const el = shallow(<OtaTimeSelector onChange={onUpdate} value={3} />);
const el = shallow(<OtaTimeSelector
disabled={false}
onChange={onUpdate}
value={3} />);
el.find(FBSelect).simulate("change", { label: "at 5 PM", value: 17 });
expect(onUpdate).toHaveBeenCalledWith(17);
});
it("unselects an OTA update time", () => {
const onUpdate = jest.fn();
const el = shallow(<OtaTimeSelector onChange={onUpdate} value={3} />);
const el = shallow(<OtaTimeSelector
disabled={false}
onChange={onUpdate}
value={3} />);
el.find(FBSelect).simulate("change", { label: "no", value: -1 });
// tslint:disable-next-line:no-null-keyword
expect(onUpdate).toHaveBeenCalledWith(null);

View File

@ -1,8 +1,9 @@
import { DropDownItem, FBSelect } from "../../../../ui";
import { DropDownItem, FBSelect, Row, Col } from "../../../../ui";
import React from "react";
import { t } from "../../../../i18next_wrapper";
import { TaggedDevice } from "farmbot";
import { edit, save } from "../../../../api/crud";
import { ColWidth } from "../../farmbot_os_settings";
const IMMEDIATELY = -1;
@ -40,6 +41,7 @@ const OTA_TIMES: Record<number, DropDownItem> = {
const DEFAULT_HOUR = OTA_TIMES[IMMEDIATELY];
interface OtaTimeSelectorProps {
disabled: boolean;
onChange(hour24: number | undefined): void;
value: number | undefined;
}
@ -51,7 +53,8 @@ export const changeOtaHour =
dispatch(save(device.uuid));
};
/** Label and toggle button for opting in to FBOS beta releases. */
export const OtaTimeSelector = ({ onChange, value }: OtaTimeSelectorProps): JSX.Element => {
export const OtaTimeSelector = (props: OtaTimeSelectorProps): JSX.Element => {
const { onChange, value, disabled } = props;
const cb = (ddi: DropDownItem) => {
const v = parseInt("" + ddi.value, 10);
if ((v == IMMEDIATELY)) {
@ -63,16 +66,20 @@ export const OtaTimeSelector = ({ onChange, value }: OtaTimeSelectorProps): JSX.
const list = Object
.values(OTA_TIMES)
.reverse()
.map(x => ({ ...x, label: t(x.label) }));
return <fieldset className={"os-release-channel"}>
<label>
{t("Apply Software Updates ")}
</label>
<FBSelect
selectedItem={value ? OTA_TIMES[value] : DEFAULT_HOUR}
onChange={cb}
list={list} />
</fieldset>;
return <Row>
<Col xs={ColWidth.label}>
<label>
{t("Apply Software Updates ")}
</label>
</Col>
<Col xs={ColWidth.description}>
<FBSelect
selectedItem={value ? OTA_TIMES[value] : DEFAULT_HOUR}
onChange={cb}
list={list}
extraClass={disabled ? "disabled" : ""} />
</Col>
</Row>;
};