Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ GEM
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
amq-protocol (2.3.3)
ast (2.4.2)
ast (2.4.3)
base64 (0.2.0)
bcrypt (3.1.20)
bigdecimal (3.1.9)
builder (3.3.0)
bunny (2.23.0)
amq-protocol (~> 2.3, >= 2.3.1)
bunny (2.24.0)
amq-protocol (~> 2.3)
sorted_set (~> 1, >= 1.0.2)
case_transform (0.2)
activesupport
Expand Down Expand Up @@ -104,7 +104,7 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
diff-lcs (1.6.0)
diff-lcs (1.6.1)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
discard (1.4.0)
Expand Down Expand Up @@ -144,7 +144,8 @@ GEM
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.2.1)
google-cloud-env (2.2.2)
base64 (~> 0.2)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.5.0)
google-cloud-storage (1.55.0)
Expand All @@ -157,7 +158,7 @@ GEM
googleauth (~> 1.9)
mini_mime (~> 1.0)
google-logging-utils (0.1.0)
googleauth (1.13.1)
googleauth (1.14.0)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.2)
google-logging-utils (~> 0.1)
Expand All @@ -171,7 +172,7 @@ GEM
mutex_m
i18n (1.14.7)
concurrent-ruby (~> 1.0)
json (2.10.1)
json (2.10.2)
jsonapi-renderer (0.2.2)
jwt (2.10.1)
base64
Expand Down Expand Up @@ -204,7 +205,7 @@ GEM
marcel (1.0.4)
method_source (1.1.0)
mini_mime (1.1.5)
minitest (5.25.4)
minitest (5.25.5)
multi_json (1.15.0)
mutations (0.9.1)
activesupport
Expand All @@ -221,13 +222,13 @@ GEM
net-smtp (0.5.1)
net-protocol
nio4r (2.7.4)
nokogiri (1.18.3-aarch64-linux-gnu)
nokogiri (1.18.6-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.3-x86_64-linux-gnu)
nokogiri (1.18.6-x86_64-linux-gnu)
racc (~> 1.4)
orm_adapter (0.5.0)
os (1.1.4)
parser (3.3.7.1)
parser (3.3.7.3)
ast (~> 2.4.1)
racc
passenger (6.0.23)
Expand Down Expand Up @@ -332,7 +333,7 @@ GEM
scenic (1.8.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
scout_apm (5.6.1)
scout_apm (5.6.2)
parser
secure_headers (7.1.0)
set (1.1.1)
Expand Down Expand Up @@ -367,7 +368,7 @@ GEM
trailblazer-option (0.1.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2025.1)
tzinfo-data (1.2025.2)
tzinfo (>= 1.0.0)
uber (0.1.0)
uri (1.0.3)
Expand Down
1 change: 1 addition & 0 deletions frontend/__test_support__/fake_designer_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const fakeDesignerState = (): DesignerState => ({
cropRadius: undefined,
distanceIndicator: "",
panelOpen: true,
threeDTopDownView: false,
});

export const fakeHelpState = (): HelpState => ({
Expand Down
2 changes: 2 additions & 0 deletions frontend/__test_support__/three_d_mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,8 @@ jest.mock("@react-three/drei", () => {
<div className={"cylinder"}>{name}</div>,
Cylinder: ({ name }: { name: string }) =>
<div className={"cylinder"}>{name}</div>,
Torus: ({ name }: { name: string }) =>
<div className={"torus"}>{name}</div>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Sphere: (props: any) =>
<div className={"sphere" + props.name} {...props}>{props.children}</div>,
Expand Down
14 changes: 13 additions & 1 deletion frontend/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,17 @@ export namespace Content {
trim(`Are you sure you want to delete all logs? A page refresh will be
required.`);

export const SHOW_3D_VIEW_DESCRIPTION_DESKTOP =
(`**3D Controls**
- Scroll to zoom
- Click and drag to rotate
- Right-click and drag to pan`);

export const SHOW_3D_VIEW_DESCRIPTION_MOBILE =
(`**3D Controls**
- Pinch to zoom and pan
- Touch and drag to rotate`);

// Front Page
export const TOS_UPDATE =
trim(`The terms of service have recently changed. You must accept the
Expand Down Expand Up @@ -2215,7 +2226,7 @@ export enum DeviceSetting {
showReadingsMapLayer = `Show Readings Map Layer`,
showMoisture = `Moisture`,
showMoistureInterpolationMapLayer = `Show Moisture Interpolation Map Layer`,
show3DMap = `3D Map`,
show3DMap = `3D Map beta`,

// Controls
invertJogButtonXAxis = `X Axis`,
Expand Down Expand Up @@ -2475,6 +2486,7 @@ export enum Actions {

// 3D
SET_DISTANCE_INDICATOR = "SET_DISTANCE_INDICATOR",
TOGGLE_3D_TOP_DOWN_VIEW = "TOGGLE_3D_TOP_DOWN_VIEW",

// Regimens
PUSH_WEEK = "PUSH_WEEK",
Expand Down
23 changes: 23 additions & 0 deletions frontend/css/farm_designer/farm_designer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -805,3 +805,26 @@
transform: translateX(-22.5rem);
}
}

.three-d-map-toggle-menu {
position: fixed;
bottom: 0;
right: 0;
padding: 1rem;
button {
height: 3.5rem;
width: 3.5rem;
i {
font-size: 1.5rem;
}
&.active {
background-color: $blue !important;
}
}
.three-d-map-toggle {
padding: 0 1.5rem;
background-color: var(--main-bg);
border-radius: 5px;
height: 3.5rem;
}
}
9 changes: 9 additions & 0 deletions frontend/farm_designer/__tests__/reducer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ describe("designer reducer", () => {
expect(newState.distanceIndicator).toEqual("setting");
});

it("sets top down view", () => {
const action: ReduxAction<boolean> = {
type: Actions.TOGGLE_3D_TOP_DOWN_VIEW,
payload: true,
};
const newState = designer(oldState(), action);
expect(newState.threeDTopDownView).toEqual(true);
});

it("sets panel open state", () => {
const action: ReduxAction<boolean> = {
type: Actions.SET_PANEL_OPEN,
Expand Down
18 changes: 16 additions & 2 deletions frontend/farm_designer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { Outlet } from "react-router";
import { ErrorBoundary } from "../error_boundary";
import { get3DConfigValueFunction } from "../settings/three_d_settings";
import { isDesktop, isMobile } from "../screen_size";
import { NavigationContext } from "../routes_helpers";
import { ThreeDGardenToggle } from "../three_d_garden";

export const getDefaultAxisLength =
(getConfigValue: GetWebAppConfigValue): Record<Xyz, number> => {
Expand Down Expand Up @@ -139,6 +141,10 @@ export class RawFarmDesigner

get mapPanelClassName() { return mapPanelClassName(this.props.designer); }

static contextType = NavigationContext;
context!: React.ContextType<typeof NavigationContext>;
navigate = this.context;

render() {
const {
legend_menu_open,
Expand All @@ -163,6 +169,8 @@ export class RawFarmDesigner
const mapPadding = getMapPadding(getPanelStatus(this.props.designer));
const padHeightOffset = mapPadding.top - mapPadding.top / zoom_level;

const threeDGarden = !!this.props.getConfigValue(BooleanSetting.three_d_garden);

return <div className="farm-designer">

<GardenMapLegend
Expand Down Expand Up @@ -206,7 +214,7 @@ export class RawFarmDesigner
</ErrorBoundary>
</div>

{this.props.getConfigValue(BooleanSetting.three_d_garden)
{threeDGarden
? <ThreeDGardenMap
designer={this.props.designer}
plants={this.props.plants}
Expand Down Expand Up @@ -278,7 +286,7 @@ export class RawFarmDesigner
&& (isDesktop() || !this.props.designer.panelOpen) &&
<SavedGardenHUD dispatch={this.props.dispatch} />}

{!this.props.getConfigValue(BooleanSetting.three_d_garden) &&
{!threeDGarden &&
<ProfileViewer
getConfigValue={this.props.getConfigValue}
dispatch={this.props.dispatch}
Expand All @@ -293,6 +301,12 @@ export class RawFarmDesigner
farmwareEnvs={this.props.farmwareEnvs}
mapTransformProps={this.mapTransformProps}
allPoints={this.props.allPoints} />}

<ThreeDGardenToggle
navigate={this.navigate}
dispatch={this.props.dispatch}
designer={this.props.designer}
threeDGarden={threeDGarden} />
</div>;
}
}
Expand Down
1 change: 1 addition & 0 deletions frontend/farm_designer/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export interface DesignerState {
cropRadius: number | undefined;
distanceIndicator: string;
panelOpen: boolean;
threeDTopDownView: boolean;
}

export type TaggedExecutable = TaggedSequence | TaggedRegimen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@ jest.mock("../../../../config_storage/actions", () => ({
setWebAppConfigValue: jest.fn(),
}));

let mockDev = false;
jest.mock("../../../../settings/dev/dev_support", () => ({
DevSettings: { futureFeaturesEnabled: () => mockDev }
}));

import React from "react";
import { shallow, mount } from "enzyme";
import {
GardenMapLegend, ZoomControls, PointsSubMenu, FarmbotSubMenu,
PlantsSubMenu, MapSettingsContent, SettingsSubMenuProps,
ZoomControlsProps,
} from "../garden_map_legend";
import { GardenMapLegendProps } from "../../interfaces";
import { BooleanSetting } from "../../../../session_keys";
Expand Down Expand Up @@ -83,23 +79,16 @@ describe("<GardenMapLegend />", () => {
wrapper.find(".fb-toggle-button").last().simulate("click");
expect(wrapper.html()).toContain("-100");
});

it("renders 3D map toggle", () => {
mockDev = true;
const p = fakeProps();
const wrapper = mount(<GardenMapLegend {...p} />);
expect(wrapper.text().toLowerCase()).toContain("3d map");
wrapper.find(".fb-layer-toggle").last().simulate("click");
expect(setWebAppConfigValue).toHaveBeenCalledWith(
BooleanSetting.three_d_garden, true);
});
});

describe("<ZoomControls />", () => {
const fakeProps = (): ZoomControlsProps => ({
zoom: jest.fn(),
getConfigValue: jest.fn(),
});

const expectDisabledBtnCountToEqual = (expected: number) => {
const wrapper = shallow(<ZoomControls
zoom={jest.fn()}
getConfigValue={jest.fn()} />);
const wrapper = shallow(<ZoomControls {...fakeProps()} />);
expect(wrapper.find(".disabled").length).toEqual(expected);
};

Expand Down
Loading