mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-07-05 17:32:10 +02:00
Fix clamping on value inputs to allow empty while focused
This commit is contained in:
@@ -316,6 +316,80 @@ const CREATE_INTEGRATION_DEFINITIONS_BY_VALUE = Object.fromEntries(
|
||||
CREATE_INTEGRATION_DEFINITIONS.map((definition) => [definition.value, definition])
|
||||
) as Record<DraftType, CreateIntegrationDefinition>;
|
||||
|
||||
function getNumberInputValue(value: unknown, fallback: number): string | number {
|
||||
if (value === '') return '';
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function getOptionalNumberInputValue(value: unknown): string | number {
|
||||
if (value === '') return '';
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
||||
return '';
|
||||
}
|
||||
|
||||
function parseIntegerInputValue(value: string): number | string {
|
||||
if (value === '') return '';
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
return Number.isNaN(parsed) ? value : parsed;
|
||||
}
|
||||
|
||||
function parseFloatInputValue(value: string): number | string {
|
||||
if (value === '') return '';
|
||||
const parsed = Number.parseFloat(value);
|
||||
return Number.isNaN(parsed) ? value : parsed;
|
||||
}
|
||||
|
||||
function normalizeIntegrationConfigForSave(
|
||||
configType: string,
|
||||
config: Record<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
const normalized = { ...config };
|
||||
|
||||
if (configType === 'mqtt_private') {
|
||||
const port = normalized.broker_port;
|
||||
if (port === '' || port === undefined || port === null) {
|
||||
normalized.broker_port = 1883;
|
||||
} else if (typeof port === 'string') {
|
||||
const parsed = Number.parseInt(port, 10);
|
||||
normalized.broker_port = Number.isNaN(parsed) ? 1883 : parsed;
|
||||
}
|
||||
|
||||
const topicPrefix = String(normalized.topic_prefix ?? '').trim();
|
||||
normalized.topic_prefix = topicPrefix || 'meshcore';
|
||||
}
|
||||
|
||||
if (configType === 'mqtt_community') {
|
||||
const brokerHost = String(normalized.broker_host ?? '').trim();
|
||||
normalized.broker_host = brokerHost || DEFAULT_COMMUNITY_BROKER_HOST;
|
||||
|
||||
const port = normalized.broker_port;
|
||||
if (port === '' || port === undefined || port === null) {
|
||||
normalized.broker_port = DEFAULT_COMMUNITY_BROKER_PORT;
|
||||
} else if (typeof port === 'string') {
|
||||
const parsed = Number.parseInt(port, 10);
|
||||
normalized.broker_port = Number.isNaN(parsed) ? DEFAULT_COMMUNITY_BROKER_PORT : parsed;
|
||||
}
|
||||
|
||||
const topicTemplate = String(normalized.topic_template ?? '').trim();
|
||||
normalized.topic_template = topicTemplate || DEFAULT_COMMUNITY_PACKET_TOPIC_TEMPLATE;
|
||||
}
|
||||
|
||||
if (configType === 'map_upload') {
|
||||
const radius = normalized.geofence_radius_km;
|
||||
if (radius === '' || radius === undefined || radius === null) {
|
||||
normalized.geofence_radius_km = 0;
|
||||
} else if (typeof radius === 'string') {
|
||||
const parsed = Number.parseFloat(radius);
|
||||
normalized.geofence_radius_km = Number.isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function isDraftType(value: string): value is DraftType {
|
||||
return value in CREATE_INTEGRATION_DEFINITIONS_BY_VALUE;
|
||||
}
|
||||
@@ -338,7 +412,7 @@ function normalizeDraftConfig(draftType: DraftType, config: Record<string, unkno
|
||||
throw new Error('MeshRank packet topic is required');
|
||||
}
|
||||
|
||||
return {
|
||||
return normalizeIntegrationConfigForSave('mqtt_community', {
|
||||
...config,
|
||||
broker_host: DEFAULT_MESHRANK_BROKER_HOST,
|
||||
broker_port: DEFAULT_MESHRANK_BROKER_PORT,
|
||||
@@ -352,7 +426,7 @@ function normalizeDraftConfig(draftType: DraftType, config: Record<string, unkno
|
||||
topic_template: topicTemplate,
|
||||
username: '',
|
||||
password: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (draftType === 'mqtt_community_letsmesh_us' || draftType === 'mqtt_community_letsmesh_eu') {
|
||||
@@ -360,7 +434,7 @@ function normalizeDraftConfig(draftType: DraftType, config: Record<string, unkno
|
||||
draftType === 'mqtt_community_letsmesh_eu'
|
||||
? DEFAULT_COMMUNITY_BROKER_HOST_EU
|
||||
: DEFAULT_COMMUNITY_BROKER_HOST;
|
||||
return {
|
||||
return normalizeIntegrationConfigForSave('mqtt_community', {
|
||||
...config,
|
||||
broker_host: brokerHost,
|
||||
broker_port: DEFAULT_COMMUNITY_BROKER_PORT,
|
||||
@@ -372,10 +446,13 @@ function normalizeDraftConfig(draftType: DraftType, config: Record<string, unkno
|
||||
topic_template: (config.topic_template as string) || DEFAULT_COMMUNITY_PACKET_TOPIC_TEMPLATE,
|
||||
username: '',
|
||||
password: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return config;
|
||||
return normalizeIntegrationConfigForSave(
|
||||
getCreateIntegrationDefinition(draftType).savedType,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeDraftScope(draftType: DraftType, scope: Record<string, unknown>) {
|
||||
@@ -649,9 +726,9 @@ function MqttPrivateConfigEditor({
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
value={(config.broker_port as number) || 1883}
|
||||
value={getNumberInputValue(config.broker_port, 1883)}
|
||||
onChange={(e) =>
|
||||
onChange({ ...config, broker_port: parseInt(e.target.value, 10) || 1883 })
|
||||
onChange({ ...config, broker_port: parseIntegerInputValue(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -709,7 +786,8 @@ function MqttPrivateConfigEditor({
|
||||
<Input
|
||||
id="fanout-mqtt-prefix"
|
||||
type="text"
|
||||
value={(config.topic_prefix as string) || 'meshcore'}
|
||||
placeholder="meshcore"
|
||||
value={(config.topic_prefix as string | undefined) ?? ''}
|
||||
onChange={(e) => onChange({ ...config, topic_prefix: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
@@ -745,7 +823,7 @@ function MqttCommunityConfigEditor({
|
||||
id="fanout-comm-host"
|
||||
type="text"
|
||||
placeholder={DEFAULT_COMMUNITY_BROKER_HOST}
|
||||
value={(config.broker_host as string) || DEFAULT_COMMUNITY_BROKER_HOST}
|
||||
value={(config.broker_host as string | undefined) ?? ''}
|
||||
onChange={(e) => onChange({ ...config, broker_host: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
@@ -756,11 +834,11 @@ function MqttCommunityConfigEditor({
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
value={(config.broker_port as number) || DEFAULT_COMMUNITY_BROKER_PORT}
|
||||
value={getNumberInputValue(config.broker_port, DEFAULT_COMMUNITY_BROKER_PORT)}
|
||||
onChange={(e) =>
|
||||
onChange({
|
||||
...config,
|
||||
broker_port: parseInt(e.target.value, 10) || DEFAULT_COMMUNITY_BROKER_PORT,
|
||||
broker_port: parseIntegerInputValue(e.target.value),
|
||||
})
|
||||
}
|
||||
/>
|
||||
@@ -895,7 +973,8 @@ function MqttCommunityConfigEditor({
|
||||
<Input
|
||||
id="fanout-comm-topic-template"
|
||||
type="text"
|
||||
value={(config.topic_template as string) || DEFAULT_COMMUNITY_PACKET_TOPIC_TEMPLATE}
|
||||
placeholder={DEFAULT_COMMUNITY_PACKET_TOPIC_TEMPLATE}
|
||||
value={(config.topic_template as string | undefined) ?? ''}
|
||||
onChange={(e) => onChange({ ...config, topic_template: e.target.value })}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
@@ -1215,11 +1294,11 @@ function MapUploadConfigEditor({
|
||||
min="0"
|
||||
step="any"
|
||||
placeholder="e.g. 100"
|
||||
value={(config.geofence_radius_km as number | undefined) ?? ''}
|
||||
value={getOptionalNumberInputValue(config.geofence_radius_km)}
|
||||
onChange={(e) =>
|
||||
onChange({
|
||||
...config,
|
||||
geofence_radius_km: e.target.value === '' ? 0 : parseFloat(e.target.value),
|
||||
geofence_radius_km: parseFloatInputValue(e.target.value),
|
||||
})
|
||||
}
|
||||
/>
|
||||
@@ -1997,9 +2076,10 @@ export function SettingsFanoutSection({
|
||||
if (!currentEditingId) {
|
||||
throw new Error('Missing fanout config id for update');
|
||||
}
|
||||
const editingType = configs.find((cfg) => cfg.id === currentEditingId)?.type ?? '';
|
||||
const update: Record<string, unknown> = {
|
||||
name: editName,
|
||||
config: editConfig,
|
||||
config: normalizeIntegrationConfigForSave(editingType, editConfig),
|
||||
scope: editScope,
|
||||
};
|
||||
if (enabled !== undefined) update.enabled = enabled;
|
||||
|
||||
Reference in New Issue
Block a user