feat: add fixed position coordinate picker (#909)
* chore: remove unused logo SVG files * feat: add interactive fixed position picker with map interface - Created new FixedPositionPicker component with clickable map for setting device coordinates - Added form field type for fixed position picker that appears when fixedPosition toggle is enabled - Implemented position request functionality to retrieve current device location * feat: display altitude unit based on user's display settings - Added dynamic altitude unit (Meters/Feet) that respects the user's imperial/metric display preference - Updated altitude field description to show the appropriate unit instead of hardcoded "Meters" * refactor: replace any type with MapLayerMouseEvent in map click handler * refactor: improve accessibility and code quality in FixedPositionPicker - Replace hardcoded IDs with useId() hook for proper accessibility - Use Number.isNaN() instead of isNaN() for more reliable type checking - Add radix parameter to parseInt() and remove unnecessary fragment wrapper * refactor: simplify fixed position picker integration - Removed dedicated FixedPositionPicker form field type in favor of toggle's additionalContent prop - Moved FixedPositionPicker to render conditionally within toggle field instead of as separate dynamic field - Streamlined form field types by eliminating FixedPositionPickerFieldProps * style: format code with consistent line breaks and import ordering * refactor: simplify fixed position picker container styling * feat: disable fixed position toggle when GPS is enabled * refactor: use ComponentRef instead of ElementRef in Switch component * refactor: replace interactive map picker with inline coordinate fields for fixed position - Removed FixedPositionPicker component with map interface - Added latitude, longitude, and altitude fields directly to position form - Moved coordinate validation into PositionValidationSchema with proper min/max bounds - Updated translation strings to include coordinate ranges and improved altitude description - Coordinates now sent via setFixedPosition admin message on form submit when fixedPosition is enabled * refactor: simplify toggle field by removing additionalContent prop and unused field spreading - Removed additionalContent prop and its JSDoc documentation from ToggleFieldProps - Removed rendering of additionalContent below toggle switch - Cleaned up Controller render function by removing unused rest spread operator - Renamed field destructuring to controllerField for clarity * refactor: improve fixed position handling and add position broadcast request - Restructure onSubmit to save config before sending admin message - Add position broadcast request after setting fixed position to immediately update display - Add comprehensive debug logging throughout submission flow - Extract coordinate exclusion logic earlier in submission process for clarity - Add 1 second delay before requesting position broadcast to allow fixed position processing * feat: add max length constraint to latitude and longitude fields - Set fieldLength.max to 10 for both latitude and longitude inputs - Prevents excessive decimal precision while maintaining 7 decimal places (±1.1cm accuracy)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { deviceRoute, moduleRoute, radioRoute } from "@app/routes";
|
||||
import { toBinary } from "@bufbuild/protobuf";
|
||||
import { PageLayout } from "@components/PageLayout.tsx";
|
||||
import { Sidebar } from "@components/Sidebar.tsx";
|
||||
import { SidebarButton } from "@components/UI/Sidebar/SidebarButton.tsx";
|
||||
@@ -6,6 +7,7 @@ import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
|
||||
import { useToast } from "@core/hooks/useToast.ts";
|
||||
import { useDevice } from "@core/stores";
|
||||
import { cn } from "@core/utils/cn.ts";
|
||||
import { Protobuf } from "@meshtastic/core";
|
||||
import { DeviceConfig } from "@pages/Settings/DeviceConfig.tsx";
|
||||
import { ModuleConfig } from "@pages/Settings/ModuleConfig.tsx";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
@@ -27,6 +29,7 @@ const ConfigPage = () => {
|
||||
getAllConfigChanges,
|
||||
getAllModuleConfigChanges,
|
||||
getAllChannelChanges,
|
||||
getAllQueuedAdminMessages,
|
||||
connection,
|
||||
clearAllChanges,
|
||||
setConfig,
|
||||
@@ -35,6 +38,7 @@ const ConfigPage = () => {
|
||||
getConfigChangeCount,
|
||||
getModuleConfigChangeCount,
|
||||
getChannelChangeCount,
|
||||
getAdminMessageChangeCount,
|
||||
} = useDevice();
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@@ -49,6 +53,7 @@ const ConfigPage = () => {
|
||||
const configChangeCount = getConfigChangeCount();
|
||||
const moduleConfigChangeCount = getModuleConfigChangeCount();
|
||||
const channelChangeCount = getChannelChangeCount();
|
||||
const adminMessageChangeCount = getAdminMessageChangeCount();
|
||||
|
||||
const sections = useMemo(
|
||||
() => [
|
||||
@@ -121,6 +126,7 @@ const ConfigPage = () => {
|
||||
const channelChanges = getAllChannelChanges();
|
||||
const configChanges = getAllConfigChanges();
|
||||
const moduleConfigChanges = getAllModuleConfigChanges();
|
||||
const adminMessages = getAllQueuedAdminMessages();
|
||||
|
||||
await Promise.all(
|
||||
channelChanges.map((channel) =>
|
||||
@@ -165,6 +171,19 @@ const ConfigPage = () => {
|
||||
await connection?.commitEditSettings();
|
||||
}
|
||||
|
||||
// Send queued admin messages after configs are committed
|
||||
if (adminMessages.length > 0) {
|
||||
await Promise.all(
|
||||
adminMessages.map((message) =>
|
||||
connection?.sendPacket(
|
||||
toBinary(Protobuf.Admin.AdminMessageSchema, message),
|
||||
Protobuf.Portnums.PortNum.ADMIN_APP,
|
||||
"self",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
channelChanges.forEach((newChannel) => {
|
||||
addChannel(newChannel);
|
||||
});
|
||||
@@ -206,6 +225,7 @@ const ConfigPage = () => {
|
||||
connection,
|
||||
getAllModuleConfigChanges,
|
||||
getAllChannelChanges,
|
||||
getAllQueuedAdminMessages,
|
||||
formMethods,
|
||||
addChannel,
|
||||
setConfig,
|
||||
@@ -244,7 +264,8 @@ const ConfigPage = () => {
|
||||
const hasDrafts =
|
||||
getConfigChangeCount() > 0 ||
|
||||
getModuleConfigChangeCount() > 0 ||
|
||||
getChannelChangeCount() > 0;
|
||||
getChannelChangeCount() > 0 ||
|
||||
adminMessageChangeCount > 0;
|
||||
const hasPending = hasDrafts || rhfState.isDirty;
|
||||
const buttonOpacity = hasPending ? "opacity-100" : "opacity-0";
|
||||
const saveDisabled = isSaving || !rhfState.isValid || !hasPending;
|
||||
@@ -312,7 +333,7 @@ const ConfigPage = () => {
|
||||
label={activeSection?.label ?? ""}
|
||||
actions={actions}
|
||||
>
|
||||
<ActiveComponent onFormInit={onFormInit} />
|
||||
{ActiveComponent && <ActiveComponent onFormInit={onFormInit} />}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user