Skip to content

Commit 5562fca

Browse files
committed
fix: various optimizations
1 parent 31590f7 commit 5562fca

28 files changed

+110
-103
lines changed

src/components/dashboard-page/DashboardFeatureWrapper.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import type { FeatureWrapperProps } from "../features/FeatureWrapper.js";
77
import { getFeatureIcon } from "../features/index.js";
88

99
const DashboardFeatureWrapper = memo((props: PropsWithChildren<FeatureWrapperProps>) => {
10-
const { children, feature, deviceState = {} } = props;
11-
const fi = useMemo(
12-
() => getFeatureIcon(feature.name, deviceState[feature.property!], "unit" in feature ? feature.unit : undefined),
13-
[feature, deviceState],
14-
);
10+
const { children, feature, deviceValue } = props;
11+
// @ts-expect-error `undefined` is fine
12+
const unit = feature.unit as string | undefined;
13+
const fi = useMemo(() => getFeatureIcon(feature.name, deviceValue, unit), [unit, feature.name, deviceValue]);
1514
const { t } = useTranslation(["featureNames", "zigbee"]);
1615
const featureName = feature.name === "state" ? feature.property : feature.name;
1716
const fallbackFeatureName = startCase(camelCase(featureName));

src/components/device-page/tabs/DeviceInfo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export default function DeviceInfo(props: DeviceInfoProps) {
9292
return (
9393
<div className="card lg:card-side bg-base-100">
9494
<figure className="w-64 h-64" style={{ overflow: "visible" }}>
95-
<DeviceImage device={device} deviceState={deviceState} disabled={device.disabled} className="" />
95+
<DeviceImage device={device} otaState={deviceState.update} disabled={device.disabled} className="" />
9696
</figure>
9797
<div className="card-body">
9898
<h2 className="card-title">
@@ -134,7 +134,7 @@ export default function DeviceInfo(props: DeviceInfoProps) {
134134
<div className="stat">
135135
<div className="stat-title">{t("last_seen")}</div>
136136
<div className="stat-value">
137-
<LastSeen config={bridgeConfig.advanced.last_seen} state={deviceState} />
137+
<LastSeen config={bridgeConfig.advanced.last_seen} lastSeen={deviceState.last_seen} />
138138
</div>
139139
<div className="stat-desc">
140140
{t("availability:availability")}

src/components/device/DeviceCard.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PropsWithChildren } from "react";
2-
import type { FeatureWithAnySubFeatures, LastSeenConfig } from "../../types.js";
3-
import { type BaseFeatureProps, getFeatureKey } from "../features/index.js";
2+
import type { DeviceState, FeatureWithAnySubFeatures, LastSeenConfig } from "../../types.js";
3+
import { type BaseWithSubFeaturesProps, getFeatureKey } from "../features/index.js";
44

55
import { Link } from "react-router";
66

@@ -13,8 +13,9 @@ import { Lqi } from "../value-decorators/Lqi.js";
1313
import PowerSource from "../value-decorators/PowerSource.js";
1414
import { DeviceImage } from "./DeviceImage.js";
1515

16-
type Props = Omit<BaseFeatureProps<FeatureWithAnySubFeatures>, "feature"> &
16+
type Props = Omit<BaseWithSubFeaturesProps<FeatureWithAnySubFeatures>, "feature" | "deviceState"> &
1717
PropsWithChildren<{
18+
deviceState: DeviceState;
1819
features: FeatureWithAnySubFeatures[];
1920
lastSeenConfig: LastSeenConfig;
2021
endpoint?: string | number;
@@ -38,7 +39,7 @@ export default function DeviceCard({
3839
<li className="list-row flex-grow">
3940
<div className="h-12 w-12" style={{ overflow: "visible" }}>
4041
{/* disabled always false because dashboard does not contain disabled devices */}
41-
<DeviceImage disabled={false} device={device} deviceState={deviceState} />
42+
<DeviceImage disabled={false} device={device} otaState={deviceState.update} />
4243
</div>
4344
<div>
4445
<Link to={`/device/${device.ieee_address}`} className="link link-hover">
@@ -74,7 +75,7 @@ export default function DeviceCard({
7475
</li>
7576
<li className="flex flex-row flex-wrap gap-1 m-4 justify-around items-center">
7677
<span className="badge badge-soft badge-ghost cursor-default" title={t("last_seen")}>
77-
<LastSeen state={deviceState} config={lastSeenConfig} />
78+
<LastSeen lastSeen={deviceState.last_seen} config={lastSeenConfig} />
7879
</span>
7980
<span className="badge badge-soft badge-ghost cursor-default" title={t("lqi")}>
8081
<Lqi value={deviceState.linkquality as number | undefined} />

src/components/device/DeviceImage.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import { useTranslation } from "react-i18next";
55
import { InterviewState } from "../../consts.js";
66
import genericDevice from "../../images/generic-zigbee-device.png";
77
import type { Device, DeviceState } from "../../types.js";
8-
import { ErrorBoundary } from "./ErrorBoundary.js";
9-
import { LazyImage } from "./LazyImage.js";
8+
import ErrorBoundary from "./ErrorBoundary.js";
9+
import LazyImage from "./LazyImage.js";
1010

1111
type DeviceImageProps = {
1212
device: Device;
13-
deviceState?: DeviceState;
13+
otaState?: DeviceState["update"];
1414
disabled: boolean;
1515
className?: string;
1616
noIndicator?: boolean;
1717
};
1818

1919
export function DeviceImage(props: Readonly<DeviceImageProps>) {
2020
const { t } = useTranslation("zigbee");
21-
const { device = {} as Device, disabled, deviceState, className, noIndicator } = props;
21+
const { device = {} as Device, disabled, otaState, className, noIndicator } = props;
2222

2323
const interviewState = useMemo(
2424
() =>
@@ -54,7 +54,7 @@ export function DeviceImage(props: Readonly<DeviceImageProps>) {
5454
<LazyImage device={device} className={`grid place-items-center${className ? ` ${className}` : ""}`} />
5555
) : (
5656
<div className="indicator w-full">
57-
{deviceState?.update?.state === "updating" && (
57+
{otaState?.state === "updating" && (
5858
<FontAwesomeIcon
5959
icon={faSync}
6060
spin

src/components/device/ErrorBoundary.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ type ErrorBoundaryProps = {
77
children: JSX.Element | JSX.Element[];
88
};
99

10-
export function ErrorBoundary({ error, children }: ErrorBoundaryProps) {
10+
export default function ErrorBoundary({ error, children }: ErrorBoundaryProps) {
1111
return error ? <FontAwesomeIcon icon={faExclamationTriangle} size="3x" className="text-error" title="Missing image" /> : children;
1212
}

src/components/device/LazyImage.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo } from "react";
12
import { useImage } from "react-image";
23
import genericDevice from "../../images/generic-zigbee-device.png";
34
import type { Device } from "../../types.js";
@@ -8,8 +9,7 @@ type LazyImageProps = {
89
className?: string;
910
};
1011

11-
export function LazyImage(props: Readonly<LazyImageProps>) {
12-
const { device, ...rest } = props;
12+
const LazyImage = memo(({ device, className }: Readonly<LazyImageProps>) => {
1313
const fromDefinition = device.definition?.icon;
1414
const fromZ2MDocs = getZ2MDeviceImage(device);
1515
const srcList: string[] = [];
@@ -28,6 +28,7 @@ export function LazyImage(props: Readonly<LazyImageProps>) {
2828

2929
const { src } = useImage({ srcList });
3030

31-
// biome-ignore lint/a11y/useAltText: bad detection
32-
return <img alt={device.ieee_address} crossOrigin={"anonymous"} src={src} {...rest} />;
33-
}
31+
return <img alt={device.ieee_address} crossOrigin={"anonymous"} src={src} className={className} />;
32+
});
33+
34+
export default LazyImage;

src/components/features/BaseViewer.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import type { BasicFeature } from "../../types.js";
33
import { DisplayValue } from "../value-decorators/DisplayValue.js";
44
import type { BaseFeatureProps } from "./index.js";
55

6-
const BaseViewer = memo((props: BaseFeatureProps<BasicFeature>) => {
6+
const BaseViewer = memo(({ feature, deviceValue }: BaseFeatureProps<BasicFeature>) => {
77
return (
88
<div>
9-
{props.feature.property && (
9+
{feature.property && (
1010
<span className="font-bold">
11-
<DisplayValue value={props.deviceState[props.feature.property]} name={props.feature.name} />
11+
<DisplayValue value={deviceValue} name={feature.name} />
1212
</span>
1313
)}
14-
{"unit" in props.feature && <span className="text-xs ms-1">{props.feature.unit}</span>}
14+
{"unit" in feature && <span className="text-xs ms-1">{feature.unit}</span>}
1515
</div>
1616
);
1717
});

src/components/features/Binary.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type BinaryProps = BaseFeatureProps<BinaryFeature>;
1414
const Binary = memo((props: BinaryProps) => {
1515
const {
1616
feature: { access = FeatureAccessMode.SET, name, property, value_off: valueOff, value_on: valueOn },
17-
deviceState,
17+
deviceValue,
1818
onChange,
1919
minimal,
2020
} = props;
@@ -26,8 +26,7 @@ const Binary = memo((props: BinaryProps) => {
2626
);
2727

2828
if (access & FeatureAccessMode.SET) {
29-
const value = deviceState[property];
30-
const valueExists = value != null;
29+
const valueExists = deviceValue != null;
3130
const showOnOffButtons = !minimal || (minimal && !valueExists);
3231

3332
return (
@@ -38,7 +37,7 @@ const Binary = memo((props: BinaryProps) => {
3837
</Button>
3938
)}
4039
{valueExists ? (
41-
<input className="toggle" type="checkbox" checked={value === valueOn} onChange={onCheckboxChange} />
40+
<input className="toggle" type="checkbox" checked={deviceValue === valueOn} onChange={onCheckboxChange} />
4241
) : (
4342
<FontAwesomeIcon icon={faQuestion} title={t("unknown")} />
4443
)}

src/components/features/Climate.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { memo } from "react";
22
import type { ClimateFeature, WithAnySubFeatures } from "../../types.js";
33
import FeatureSubFeatures from "./FeatureSubFeatures.js";
4-
import type { BaseFeatureProps } from "./index.js";
4+
import type { BaseWithSubFeaturesProps } from "./index.js";
55

6-
type ClimateProps = BaseFeatureProps<WithAnySubFeatures<ClimateFeature>>;
6+
type ClimateProps = BaseWithSubFeaturesProps<WithAnySubFeatures<ClimateFeature>>;
77

88
const Climate = memo((props: ClimateProps) => {
99
return <FeatureSubFeatures {...props} />;

src/components/features/Color.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import type { BaseFeatureProps } from "./index.js";
66
type ColorProps = BaseFeatureProps<ColorFeature>;
77

88
const Color = memo((props: ColorProps) => {
9-
const { deviceState, feature, onChange, minimal } = props;
9+
const { deviceValue, feature, onChange, minimal } = props;
1010

1111
const value = useMemo(() => {
1212
const val = {} as AnyColor;
1313

1414
for (const innerFeature of feature.features) {
15-
val[innerFeature.name] = deviceState[feature.property]?.[innerFeature.property] ?? 0;
15+
val[innerFeature.name] = deviceValue?.[innerFeature.property] ?? 0;
1616
}
1717

1818
return val;
19-
}, [deviceState[feature.property], feature]);
19+
}, [deviceValue, feature]);
2020

2121
const onEditorChange = useCallback((color: AnyColor | { hex: string }) => onChange({ color }), [onChange]);
2222

0 commit comments

Comments
 (0)