-
Notifications
You must be signed in to change notification settings - Fork 308
fix(web): batch of small bug fixes and UX polish #1412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,12 +27,17 @@ interface PositionConfigProps { | |
| onFormInit: DynamicFormFormInit<PositionValidation>; | ||
| } | ||
|
|
||
| // Firmware stores altitude in meters; form displays user's chosen unit. | ||
| const METERS_PER_FOOT = 0.3048; | ||
| const metersToFeet = (m: number) => m / METERS_PER_FOOT; | ||
| const feetToMeters = (ft: number) => ft * METERS_PER_FOOT; | ||
|
|
||
| /** | ||
| * Renders inside the Device GPS card. Pulls the browser's current location | ||
| * via navigator.geolocation and writes lat/lng/altitude into the form. | ||
| * No-op without a geolocation API (e.g. insecure context). | ||
| */ | ||
| function UseBrowserLocationButton() { | ||
| function UseBrowserLocationButton({ isImperial }: { isImperial: boolean }) { | ||
| const { setValue } = useFormContext<PositionValidation>(); | ||
| const { toast } = useToast(); | ||
| const { t } = useTranslation("config"); | ||
|
|
@@ -57,7 +62,10 @@ function UseBrowserLocationButton() { | |
| pos.coords.altitude !== null && | ||
| !Number.isNaN(pos.coords.altitude) | ||
| ) { | ||
| setValue("altitude", Math.round(pos.coords.altitude), { | ||
| const altitude = isImperial | ||
| ? metersToFeet(pos.coords.altitude) | ||
| : pos.coords.altitude; | ||
| setValue("altitude", Math.round(altitude), { | ||
| shouldDirty: true, | ||
| }); | ||
| } | ||
|
|
@@ -113,8 +121,11 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
|
|
||
| const currentPosition = myNode?.position; | ||
| const displayUnits = getEffectiveConfig("display")?.units; | ||
| const isImperial = | ||
| displayUnits === Protobuf.Config.Config_DisplayConfig_DisplayUnits.IMPERIAL; | ||
|
|
||
| const formValues = useMemo(() => { | ||
| const altitudeMeters = currentPosition?.altitude ?? 0; | ||
| return { | ||
| ...config.position, | ||
| ...effectivePosition, | ||
|
|
@@ -124,9 +135,11 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| longitude: currentPosition?.longitudeI | ||
| ? currentPosition.longitudeI / 1e7 | ||
| : undefined, | ||
| altitude: currentPosition?.altitude ?? 0, | ||
| altitude: isImperial | ||
| ? Math.round(metersToFeet(altitudeMeters)) | ||
| : altitudeMeters, | ||
| } as PositionValidation; | ||
| }, [config.position, effectivePosition, currentPosition]); | ||
| }, [config.position, effectivePosition, currentPosition, isImperial]); | ||
|
|
||
| const onSubmit = (data: PositionValidation) => { | ||
| const { | ||
|
|
@@ -149,13 +162,16 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| data.latitude !== undefined && | ||
| data.longitude !== undefined | ||
| ) { | ||
| const altitudeMeters = isImperial | ||
| ? Math.round(feetToMeters(data.altitude ?? 0)) | ||
| : Math.round(data.altitude ?? 0); | ||
| const message = create(Protobuf.Admin.AdminMessageSchema, { | ||
| payloadVariant: { | ||
| case: "setFixedPosition", | ||
| value: create(Protobuf.Mesh.PositionSchema, { | ||
| latitudeI: Math.round(data.latitude * 1e7), | ||
| longitudeI: Math.round(data.longitude * 1e7), | ||
| altitude: data.altitude || 0, | ||
| altitude: altitudeMeters, | ||
| time: Math.floor(Date.now() / 1000), | ||
| }), | ||
| }, | ||
|
|
@@ -220,7 +236,7 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| { | ||
| label: t("position.deviceGps.label"), | ||
| description: t("position.deviceGps.description"), | ||
| footer: <UseBrowserLocationButton />, | ||
| footer: <UseBrowserLocationButton isImperial={isImperial} />, | ||
| fields: [ | ||
| { | ||
| type: "toggle", | ||
|
|
@@ -236,7 +252,7 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| properties: { | ||
| step: 0.0000001, | ||
| suffix: "Degrees", | ||
| fieldLength: { max: 10 }, | ||
| fieldLength: { max: 12 }, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Enforce the seven-decimal limit in validation. The new length limits still allow excess fractional digits. For example, Add a fractional-digit refinement to the coordinate schema, or reject excess precision before submission. Also applies to: 267-267 🤖 Prompt for AI Agents |
||
| }, | ||
| disabledBy: [{ fieldName: "fixedPosition" }], | ||
| }, | ||
|
|
@@ -248,7 +264,7 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| properties: { | ||
| step: 0.0000001, | ||
| suffix: "Degrees", | ||
| fieldLength: { max: 10 }, | ||
| fieldLength: { max: 13 }, | ||
| }, | ||
| disabledBy: [{ fieldName: "fixedPosition" }], | ||
| }, | ||
|
|
@@ -257,19 +273,11 @@ export const Position = ({ onFormInit }: PositionConfigProps) => { | |
| name: "altitude", | ||
| label: t("position.fixedPosition.altitude.label"), | ||
| description: t("position.fixedPosition.altitude.description", { | ||
| unit: | ||
| displayUnits === | ||
| Protobuf.Config.Config_DisplayConfig_DisplayUnits.IMPERIAL | ||
| ? "Feet" | ||
| : "Meters", | ||
| unit: isImperial ? "Feet" : "Meters", | ||
| }), | ||
| properties: { | ||
| step: 0.0000001, | ||
| suffix: | ||
| displayUnits === | ||
| Protobuf.Config.Config_DisplayConfig_DisplayUnits.IMPERIAL | ||
| ? "Feet" | ||
| : "Meters", | ||
| step: 1, | ||
| suffix: isImperial ? "Feet" : "Meters", | ||
| }, | ||
| disabledBy: [{ fieldName: "fixedPosition" }], | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: meshtastic/web
Length of output: 44774
🏁 Script executed:
Repository: meshtastic/web
Length of output: 33007
🏁 Script executed:
Repository: meshtastic/web
Length of output: 297
🏁 Script executed:
Repository: meshtastic/web
Length of output: 14555
🏁 Script executed:
Repository: meshtastic/web
Length of output: 821
Treat empty optional coordinates as absent before coercion.
GenericInputforwards"", andDynamicFormsubmits on every change.z.coerce.number().optional()converts""to0, so the coordinate remains defined. WithfixedPositionenabled, clearing one or both fields can queuesetFixedPositionwith zeroed coordinates instead of treating incomplete input as absent. Normalize empty strings toundefinedbefore coercion, or reject them inPositionValidationSchema.🤖 Prompt for AI Agents
Source: MCP tools