Skip to content

Commit

Permalink
add seconds to time display options
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielburnworth committed Jun 29, 2020
1 parent 9cfa333 commit 6552baf
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 39 deletions.
@@ -0,0 +1,8 @@
class AddTimeFormatSecondsToWebAppConfig < ActiveRecord::Migration[6.0]
def change
add_column :web_app_configs,
:time_format_seconds,
:boolean,
default: false
end
end
1 change: 1 addition & 0 deletions frontend/__test_support__/fake_time_settings.ts
Expand Up @@ -3,4 +3,5 @@ import { TimeSettings } from "../interfaces";
export const fakeTimeSettings = (): TimeSettings => ({
utcOffset: 0,
hour24: false,
seconds: false,
});
13 changes: 13 additions & 0 deletions frontend/__tests__/session_keys_test.ts
@@ -0,0 +1,13 @@
import { BooleanSetting, NumericSetting } from "../session_keys";

describe("BooleanSetting", () => {
it("verifies key integrity", () => {
Object.entries(BooleanSetting).map(([k, v]) => expect(k).toEqual(v));
});
});

describe("NumericSetting", () => {
it("verifies key integrity", () => {
Object.entries(NumericSetting).map(([k, v]) => expect(k).toEqual(v));
});
});
2 changes: 1 addition & 1 deletion frontend/account/labs/__tests__/fetch_lab_features_test.ts
Expand Up @@ -4,7 +4,7 @@ describe("fetchLabFeatures", () => {
Object.defineProperty(window.location, "reload", { value: jest.fn() });
it("basically just initializes stuff", () => {
const val = fetchLabFeatures(jest.fn());
expect(val.length).toBe(8);
expect(val.length).toBe(9);
expect(val[0].value).toBeFalsy();
const { callback } = val[0];
if (callback) {
Expand Down
6 changes: 6 additions & 0 deletions frontend/account/labs/labs_features_list_data.ts
Expand Up @@ -39,6 +39,12 @@ export const fetchLabFeatures =
storageKey: BooleanSetting.time_format_24_hour,
value: false,
},
{
name: DeviceSetting.showSecondsInTime,
description: Content.TIME_FORMAT_SECONDS,
storageKey: BooleanSetting.time_format_seconds,
value: false,
},
{
name: DeviceSetting.hideWebcamWidget,
description: Content.HIDE_WEBCAM_WIDGET,
Expand Down
4 changes: 4 additions & 0 deletions frontend/constants.ts
Expand Up @@ -617,6 +617,9 @@ export namespace Content {
trim(`Display time using the 24-hour notation,
i.e., 23:00 instead of 11:00pm`);

export const TIME_FORMAT_SECONDS =
trim(`Display seconds in time, i.e., 10:00:00am instead of 10:00am`);

export const HIDE_WEBCAM_WIDGET =
trim(`If not using a webcam, use this setting to remove the
widget from the Controls panel.`);
Expand Down Expand Up @@ -1251,6 +1254,7 @@ export enum DeviceSetting {
// App
internationalizeWebApp = `Internationalize Web App`,
use24hourTimeFormat = `Use 24-hour time format`,
showSecondsInTime = `Show seconds in time`,
hideWebcamWidget = `Hide Webcam widget`,
hideSensorsPanel = `Hide Sensors panel`,
readSpeakLogsInBrowser = `Read speak logs in browser`,
Expand Down
4 changes: 3 additions & 1 deletion frontend/css/status_ticker.scss
Expand Up @@ -85,14 +85,16 @@
}

.status-ticker-created-at {
white-space: nowrap;
float: right;
margin-top: 0.2rem;
font-weight: 400;
}

// background: linear-gradient(to top, black 0%, transparent 30%);
.status-ticker-message {
display: inline;
display: inline-block;
min-width: 90%;
letter-spacing: 0.1rem;
span {
-webkit-animation: flash 1.5s ease-in;
Expand Down
1 change: 1 addition & 0 deletions frontend/devices/components/maybe_highlight.tsx
Expand Up @@ -162,6 +162,7 @@ const LOG_SETTINGS = [
const APP_SETTINGS = [
DeviceSetting.internationalizeWebApp,
DeviceSetting.use24hourTimeFormat,
DeviceSetting.showSecondsInTime,
DeviceSetting.hideWebcamWidget,
DeviceSetting.hideSensorsPanel,
DeviceSetting.readSpeakLogsInBrowser,
Expand Down
5 changes: 5 additions & 0 deletions frontend/farm_designer/settings/other_settings.tsx
Expand Up @@ -218,6 +218,11 @@ const OTHER_SETTINGS = (): SettingDescriptionProps[] => ([
description: Content.TIME_FORMAT_24_HOUR,
setting: BooleanSetting.time_format_24_hour,
},
{
title: DeviceSetting.showSecondsInTime,
description: Content.TIME_FORMAT_SECONDS,
setting: BooleanSetting.time_format_seconds,
},
{
title: DeviceSetting.hideWebcamWidget,
description: Content.HIDE_WEBCAM_WIDGET,
Expand Down
1 change: 1 addition & 0 deletions frontend/interfaces.ts
Expand Up @@ -28,4 +28,5 @@ export type UnsafeError = any;
export interface TimeSettings {
utcOffset: number;
hour24: boolean;
seconds: boolean;
}
50 changes: 31 additions & 19 deletions frontend/resources/__tests__/selectors_test.ts
Expand Up @@ -11,7 +11,8 @@ import { hasId, arrayUnwrap } from "../util";
import {
fakeWebcamFeed,
fakeSequence,
fakePlant,
fakeToolSlot,
fakeUser,
} from "../../__test_support__/fake_state/resources";
import { resourceReducer } from "../reducer";
import { emptyState } from "../reducer";
Expand Down Expand Up @@ -122,24 +123,14 @@ describe("findPointerByTypeAndId()", () => {
});
});

describe("findPlant()", () => {
it("throws error", () => {
console.warn = jest.fn();
const find = () => Selector.findPlant(fakeIndex, "bad");
expect(find).toThrowError();
expect(console.warn).toBeCalled();
});

it("finds a plant", () => {
const plant = fakePlant();
plant.body.id = 333;
const result = Selector
.findPlant(buildResourceIndex([plant]).index, plant.uuid);
expect(result.uuid).toBe(plant.uuid);
describe("selectCurrentToolSlot()", () => {
it("returns tool slot", () => {
const toolSlot = fakeToolSlot();
const resourceIndex = buildResourceIndex([toolSlot]).index;
const result = Selector.selectCurrentToolSlot(resourceIndex, toolSlot.uuid);
expect(result.uuid).toBe(toolSlot.uuid);
});
});

describe("selectCurrentToolSlot()", () => {
it("throws error", () => {
const find = () => Selector.selectCurrentToolSlot(fakeIndex, "bad");
expect(find).toThrowError();
Expand Down Expand Up @@ -260,13 +251,13 @@ describe("getDeviceAccountSettings", () => {
it("crashes if < 1", () => {
const { index } = buildResourceIndex([]);
const kaboom = () => Selector.getDeviceAccountSettings(index);
expect(kaboom).toThrowError();
expect(kaboom).toThrowError(/before it was loaded/);
});

it("crashes if > 1", () => {
const { index } = buildResourceIndex([DEV1, DEV2]);
const kaboom = () => Selector.getDeviceAccountSettings(index);
expect(kaboom).toThrowError();
expect(kaboom).toThrowError(/more than 1/);
});

it("returns exactly one device", () => {
Expand All @@ -275,3 +266,24 @@ describe("getDeviceAccountSettings", () => {
expect(result.kind).toBe("Device");
});
});

describe("getUserAccountSettings()", () => {
it("fetches user", () => {
const user = fakeUser();
const { index } = buildResourceIndex([user]);
const result = Selector.getUserAccountSettings(index);
expect(result?.uuid).toEqual(user.uuid);
});

it("errors while fetching user: no user", () => {
const { index } = buildResourceIndex([]);
expect(() => Selector.getUserAccountSettings(index)).toThrowError(
/before it was available/);
});

it("errors while fetching user: more than one user", () => {
const { index } = buildResourceIndex([fakeUser(), fakeUser()]);
expect(() => Selector.getUserAccountSettings(index)).toThrowError(
/Expected 1 user. Got: 2/);
});
});
20 changes: 8 additions & 12 deletions frontend/resources/selectors.ts
Expand Up @@ -23,9 +23,7 @@ import {
} from "./tagged_resources";
import { betterCompact, bail } from "../util";
import { findAllById } from "./selectors_by_id";
import {
findPoints, selectAllPoints, selectAllActivePoints,
} from "./selectors_by_kind";
import { selectAllPoints, selectAllActivePoints } from "./selectors_by_kind";
import { assertUuid } from "./util";
import { joinKindAndId } from "./reducer_support";
import { chain } from "lodash";
Expand Down Expand Up @@ -115,15 +113,6 @@ export function selectAllToolSlotPointers(index: ResourceIndex):
return betterCompact(toolSlotPointers);
}

export function findPlant(i: ResourceIndex, uuid: string):
TaggedPlantPointer {
const point = findPoints(i, uuid);
if (point && sanityCheck(point) && point.body.pointer_type === "Plant") {
return point as TaggedPlantPointer;
} else {
throw new Error("That is not a true plant pointer");
}
}
export function selectCurrentToolSlot(index: ResourceIndex, uuid: string) {
const x = index.references[uuid];
if (x && isTaggedToolSlotPointer(x)) {
Expand Down Expand Up @@ -188,10 +177,17 @@ export function maybeGet24HourTimeSetting(index: ResourceIndex): boolean {
return conf ? conf.body[BooleanSetting.time_format_24_hour] : false;
}

/** Return seconds time format preference if possible. */
export function maybeGetSecondsTimeSetting(index: ResourceIndex): boolean {
const conf = getWebAppConfig(index);
return conf ? conf.body[BooleanSetting.time_format_seconds] : false;
}

export function maybeGetTimeSettings(index: ResourceIndex): TimeSettings {
return {
utcOffset: maybeGetTimeOffset(index),
hour24: maybeGet24HourTimeSetting(index),
seconds: maybeGetSecondsTimeSetting(index),
};
}

Expand Down
14 changes: 10 additions & 4 deletions frontend/session_keys.ts
@@ -1,9 +1,13 @@
import {
BooleanConfigKey,
NumberConfigKey,
BooleanConfigKey as WebAppBooleanConfigKey,
NumberConfigKey as WebAppNumberConfigKey,
} from "farmbot/dist/resources/configs/web_app";

export const BooleanSetting: Record<BooleanConfigKey, BooleanConfigKey> = {
type WebAppBooleanConfigKeyAll = WebAppBooleanConfigKey | "time_format_seconds";
type WebAppNumberConfigKeyAll = WebAppNumberConfigKey;

export const BooleanSetting:
Record<WebAppBooleanConfigKeyAll, WebAppBooleanConfigKey> = {
/** Move settings */
x_axis_inverted: "x_axis_inverted",
y_axis_inverted: "y_axis_inverted",
Expand Down Expand Up @@ -46,6 +50,7 @@ export const BooleanSetting: Record<BooleanConfigKey, BooleanConfigKey> = {
enable_browser_speak: "enable_browser_speak",
discard_unsaved: "discard_unsaved",
time_format_24_hour: "time_format_24_hour",
time_format_seconds: "time_format_seconds" as WebAppBooleanConfigKey,
disable_emergency_unlock_confirmation: "disable_emergency_unlock_confirmation",
user_interface_read_only_mode: "user_interface_read_only_mode",

Expand All @@ -56,7 +61,8 @@ export const BooleanSetting: Record<BooleanConfigKey, BooleanConfigKey> = {
stub_config: "stub_config",
};

export const NumericSetting: Record<NumberConfigKey, NumberConfigKey> = {
export const NumericSetting:
Record<WebAppNumberConfigKeyAll, WebAppNumberConfigKey> = {
/** Logs settings */
assertion_log: "assertion_log",
success_log: "success_log",
Expand Down
4 changes: 4 additions & 0 deletions frontend/util/__tests__/util_test.ts
Expand Up @@ -190,11 +190,15 @@ describe("timeFormatString()", () => {
const timeSettings = fakeTimeSettings();
timeSettings.hour24 = false;
expect(Util.timeFormatString(timeSettings)).toEqual("h:mma");
timeSettings.seconds = true;
expect(Util.timeFormatString(timeSettings)).toEqual("h:mm:ssa");
});

it("returns 24hr time format", () => {
const timeSettings = fakeTimeSettings();
timeSettings.hour24 = true;
expect(Util.timeFormatString(timeSettings)).toEqual("H:mm");
timeSettings.seconds = true;
expect(Util.timeFormatString(timeSettings)).toEqual("H:mm:ss");
});
});
6 changes: 4 additions & 2 deletions frontend/util/util.ts
Expand Up @@ -220,5 +220,7 @@ export const parseIntInput = (input: string): number => {
};

export const timeFormatString =
(timeSettings: TimeSettings | undefined): string =>
(timeSettings?.hour24) ? "H:mm" : "h:mma";
(timeSettings: TimeSettings | undefined): string => {
const subHour = timeSettings?.seconds ? "mm:ss" : "mm";
return timeSettings?.hour24 ? `H:${subHour}` : `h:${subHour}a`;
};

0 comments on commit 6552baf

Please sign in to comment.