Skip to content

Conversation

@dzienisz
Copy link
Contributor

@dzienisz dzienisz commented Oct 26, 2025

Description

Screenshot 2025-10-26 at 12 23 44
  • Added new FixedPositionPicker component with interactive map and coordinate inputs
  • Enhanced position settings UI to allow manual coordinate entry and map-based selection
  • Added success/error toast notifications for position updates
  • Updated translations to include new fixed position coordinate picker labels and messages
  • Added support for requesting current position updates from device
  • Fixed queue status logging to properly stringify object output

Related Issues

Fixes: Users cannot set fixed position coordinates on web client

Changes Made

New Components:

  • FixedPositionPicker.tsx - Standalone component with interactive map, coordinate inputs, and position management buttons

Modified Files:

  • Position.tsx - Integrated FixedPositionPicker component into fixed position toggle
  • Map.tsx - Added initialViewState prop to support custom zoom levels and center coordinates
  • decodePacket.ts - Fixed Queue Status logging to use JSON.stringify() instead of string concatenation
  • config.json (en) - Added translation keys for fixed position UI elements

Key Features:

  • Interactive map with click-to-set coordinates (zoom level 13 for ~5km view)
  • Manual latitude/longitude/altitude input fields with 7 decimal place precision
  • "Set Fixed Position" button sends setFixedPosition admin message with coordinates
  • "Request Position Update" button triggers immediate position broadcast via getOwnerRequest
  • Validation ensures Fixed Position toggle is enabled before allowing coordinate updates
  • Smart initial view: centers on device's current position or world view if no position available

Testing Done

  • ✅ Toggled Fixed Position on/off - UI shows/hides correctly
  • ✅ Clicked map to set coordinates - marker appears and inputs update
  • ✅ Entered coordinates manually - values accepted and validated
  • ✅ Set fixed position with coordinates - admin message sent, console logs show correct latitudeI/longitudeI conversion
  • ✅ Clicked "Request Position Update" - device broadcasts POSITION_APP packet
  • ✅ Verified position updates on map/nodes page after setting fixed coordinates
  • ✅ Tested coordinate precision (7 decimal places = ~1cm accuracy)
  • ✅ Tested validation - error toast shown when Fixed Position toggle is off
  • ✅ Tested with GPS enabled and disabled modes

Screenshots (if applicable)

[Add screenshots showing the interactive map picker, coordinate inputs, and buttons]

Checklist

  • Code follows project style guidelines
  • Documentation has been updated or added (inline comments and component props)
  • Tests have been added or updated (manual testing completed, unit tests recommended for future)
  • All i18n translation labels have been added (English translations complete, Polish structure ready in pl-PL)

Additional Notes:

  • Coordinate format: UI uses decimal degrees, protocol uses integer format (multiply by 1e7)
  • Recommended position flags for fixed position: ALTITUDE + TIMESTAMP
  • Users should set GPS Mode to "DISABLED" or "NOT_PRESENT" when using fixed position for best results

@vercel
Copy link

vercel bot commented Oct 26, 2025

@dzienisz is attempting to deploy a commit to the Meshtastic Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Collaborator

@danditomaso danditomaso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall really glad to see this has been added to the code base, it has been needed for a while.

I will need to see some changes reverted otherwise it will cause all kinds of type issues around the code base. Additionally, the Fixed Position component must be added to the DynamicFormField and referenced using the type field by its string value (known as a factory function). Once you look at the DynamicFormField component you'll see the pattern. I'm available to help answer any questions on will be required to get this merged

device.log.trace(
Types.Emitter[Types.Emitter.HandleFromRadio],
`🚧 Received Queue Status: ${decodedMessage.payloadVariant.value}`,
`🚧 Received Queue Status: ${JSON.stringify(decodedMessage.payloadVariant.value)}`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure why this was added, it should be reverted as this would touch every piece of data received by the web app from a node.

},
"altitude": {
"label": "Altitude",
"description": "Meters above sea level (optional)"
Copy link
Collaborator

@danditomaso danditomaso Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should accept an interpolated string, as I believe if someone's localisation is set to US, it will be feet above sea level. It would look like {{ unitOfMeasure }} in other translations you can see examples of this usage.
So the string should look like {{ unit }} above sea level (optional) and pass in the Localization protobuf value.

disabledBy?: DisabledBy<T>[];
label: string;
description?: string;
description?: React.ReactNode;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be reverted. I think you modified the base field instead of adding your specific fields to the DynamicFormField.tsx file, and calling it from a string in the config.

label: string;
fieldName: string;
description?: string;
description?: React.ReactNode;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert same reason as above.

currentPosition?.altitude ? String(currentPosition.altitude) : ""
);

const handleMapClick = useCallback((event: any) => {
Copy link
Collaborator

@danditomaso danditomaso Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event should be typed correctly. Any cannot be used. This should be something like React.ChangeEvent<HTMLDivElement>>

<>
{t("position.fixedPosition.description")}
{formValues.fixedPosition && (
<FixedPositionPicker
Copy link
Collaborator

@danditomaso danditomaso Oct 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This need to be used in the same way as other Form fields in the code base. You'll need to add this as a field to DynamicFormField.tsx and call it using the correct string. There should be lots of examples of this in our code base to show you how its all connected.

@dzienisz
Copy link
Contributor Author

I am open to all suggestions. This is my first big PR. I can improve it next week.

@dzienisz dzienisz force-pushed the feat/fixed-position branch from f4ffb1b to d3777e6 Compare October 29, 2025 13:12
- Added new FixedPositionPicker component with interactive map and coordinate inputs
- Enhanced position settings UI to allow manual coordinate entry and map-based selection
- Added success/error toast notifications for position updates
- Updated translations to include new fixed position coordinate picker labels and messages
- Added support for requesting current position updates from device
- Fixed queue status logging to properly stringify
@dzienisz dzienisz force-pushed the feat/fixed-position branch from d3777e6 to 9b80d5e Compare October 29, 2025 13:48
@danditomaso
Copy link
Collaborator

@dzienisz Wanted to follow up and see if the fixes were going to be added to this PR?

@dzienisz
Copy link
Contributor Author

@dzienisz Wanted to follow up and see if the fixes were going to be added to this PR?

today, sorry for delay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants