mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-07 02:01:36 +00:00
205 lines
4.8 KiB
TypeScript
205 lines
4.8 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
|
|
import { notification, Input, Select, Button, Space, Form, Dropdown, Menu } from "antd";
|
|
import { ReloadOutlined, CopyOutlined } from "@ant-design/icons";
|
|
|
|
import type { GetRandomDevAddrResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
|
import { GetRandomDevAddrRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device_pb";
|
|
|
|
import DeviceStore from "../stores/DeviceStore";
|
|
|
|
interface IProps {
|
|
label: string;
|
|
name: string;
|
|
devEui: string;
|
|
required?: boolean;
|
|
value?: string;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
function DevAddrInput(props: IProps) {
|
|
const form = Form.useFormInstance();
|
|
const [byteOrder, setByteOrder] = useState<string>("msb");
|
|
const [value, setValue] = useState<string>("");
|
|
|
|
useEffect(() => {
|
|
if (props.value) {
|
|
setValue(props.value);
|
|
}
|
|
}, [props]);
|
|
|
|
const updateField = (v: string) => {
|
|
if (byteOrder === "lsb") {
|
|
const bytes = v.match(/[A-Fa-f0-9]{2}/g) || [];
|
|
v = bytes.reverse().join("");
|
|
}
|
|
|
|
form.setFieldsValue({
|
|
[props.name]: v,
|
|
});
|
|
};
|
|
|
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const v = e.target.value;
|
|
const match = v.match(/[A-Fa-f0-9]/g);
|
|
|
|
let value = "";
|
|
if (match) {
|
|
if (match.length > 8) {
|
|
value = match.slice(0, 8).join("");
|
|
} else {
|
|
value = match.join("");
|
|
}
|
|
}
|
|
|
|
setValue(value);
|
|
updateField(value);
|
|
};
|
|
|
|
const onByteOrderSelect = (v: string) => {
|
|
if (v === byteOrder) {
|
|
return;
|
|
}
|
|
|
|
setByteOrder(v);
|
|
|
|
const current = value;
|
|
const bytes = current.match(/[A-Fa-f0-9]{2}/g) || [];
|
|
const vv = bytes.reverse().join("");
|
|
|
|
setValue(vv);
|
|
updateField(vv);
|
|
};
|
|
|
|
const generateRandom = () => {
|
|
const req = new GetRandomDevAddrRequest();
|
|
req.setDevEui(props.devEui);
|
|
|
|
DeviceStore.getRandomDevAddr(req, (resp: GetRandomDevAddrResponse) => {
|
|
setValue(resp.getDevAddr());
|
|
updateField(resp.getDevAddr());
|
|
});
|
|
};
|
|
|
|
const copyToClipboard = () => {
|
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
|
|
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
|
navigator.clipboard
|
|
.writeText(bytes.join("").toUpperCase())
|
|
.then(() => {
|
|
notification.success({
|
|
message: "Copied to clipboard",
|
|
duration: 3,
|
|
});
|
|
})
|
|
.catch(e => {
|
|
notification.error({
|
|
message: "Error",
|
|
description: e,
|
|
duration: 3,
|
|
});
|
|
});
|
|
} else {
|
|
notification.error({
|
|
message: "Error",
|
|
description: "Clipboard functionality is not available.",
|
|
duration: 3,
|
|
});
|
|
}
|
|
};
|
|
|
|
const copyToClipboardHexArray = () => {
|
|
const bytes = value.match(/[A-Fa-f0-9]{2}/g);
|
|
|
|
if (bytes !== null && navigator.clipboard !== undefined) {
|
|
navigator.clipboard
|
|
.writeText(
|
|
bytes
|
|
.join(", ")
|
|
.toUpperCase()
|
|
.replace(/[A-Fa-f0-9]{2}/g, "0x$&"),
|
|
)
|
|
.then(() => {
|
|
notification.success({
|
|
message: "Copied to clipboard",
|
|
duration: 3,
|
|
});
|
|
})
|
|
.catch(e => {
|
|
notification.error({
|
|
message: "Error",
|
|
description: e,
|
|
duration: 3,
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
const copyMenu = (
|
|
<Menu
|
|
items={[
|
|
{
|
|
key: "1",
|
|
label: (
|
|
<Button type="text" onClick={copyToClipboard}>
|
|
HEX string
|
|
</Button>
|
|
),
|
|
},
|
|
{
|
|
key: "2",
|
|
label: (
|
|
<Button type="text" onClick={copyToClipboardHexArray}>
|
|
HEX array
|
|
</Button>
|
|
),
|
|
},
|
|
]}
|
|
/>
|
|
);
|
|
|
|
const addon = (
|
|
<Space size="large">
|
|
<Select value={byteOrder} onChange={onByteOrderSelect}>
|
|
<Select.Option value="msb">MSB</Select.Option>
|
|
<Select.Option value="lsb">LSB</Select.Option>
|
|
</Select>
|
|
<Button type="text" size="small" onClick={generateRandom}>
|
|
<ReloadOutlined />
|
|
</Button>
|
|
<Dropdown overlay={copyMenu}>
|
|
<Button type="text" size="small">
|
|
<CopyOutlined />
|
|
</Button>
|
|
</Dropdown>
|
|
</Space>
|
|
);
|
|
|
|
return (
|
|
<Form.Item
|
|
rules={[
|
|
{
|
|
required: props.required,
|
|
message: `Please enter a valid ${props.label}`,
|
|
pattern: new RegExp(/[A-Fa-f0-9]{8}/g),
|
|
},
|
|
]}
|
|
label={props.label}
|
|
name={props.name}
|
|
>
|
|
<Input hidden />
|
|
<Input
|
|
id={`${props.name}Render`}
|
|
onChange={onChange}
|
|
addonAfter={!props.disabled && addon}
|
|
className="input-code"
|
|
value={value}
|
|
disabled={props.disabled}
|
|
/>
|
|
</Form.Item>
|
|
);
|
|
}
|
|
|
|
export default DevAddrInput;
|