Move plugins to settings page
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import type React from 'react';
|
||||
|
||||
import { FiCode, FiSliders } from 'react-icons/fi';
|
||||
|
||||
import { TabButton } from '@app/components/TabButton';
|
||||
import type { Plugin } from '@app/pages/settings/Plugins';
|
||||
import { Sidebar } from '@components/generic/Sidebar';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
import { ExternalNotificationsDebugPanel } from './panels/ExternalNotifications/DebugPanel';
|
||||
import { ExternalNotificationsSettingsPlanel } from './panels/ExternalNotifications/SettingsPlanel';
|
||||
import { RangeTestDebugPanel } from './panels/RangeTest/DebugPanel';
|
||||
import { RangeTestSettingsPanel } from './panels/RangeTest/SettingsPanel';
|
||||
import { SerialDebugPanel } from './panels/Serial/DebugPanel';
|
||||
import { SerialSettingsPanel } from './panels/Serial/SettingsPanel';
|
||||
import { StoreForwardDebugPanel } from './panels/StoreForward/DebugPanel';
|
||||
import { StoreForwardSettingsPanel } from './panels/StoreForward/SettingsPanel';
|
||||
|
||||
export interface PluginsSidebarProps {
|
||||
plugin?: Plugin;
|
||||
closeSidebar: () => void;
|
||||
}
|
||||
|
||||
export const PluginsSidebar = ({
|
||||
plugin,
|
||||
closeSidebar,
|
||||
}: PluginsSidebarProps): JSX.Element => {
|
||||
return (
|
||||
<Sidebar
|
||||
title={plugin ?? 'Please select plugin'}
|
||||
tagline={plugin ? 'settings' : '...'}
|
||||
closeSidebar={closeSidebar}
|
||||
>
|
||||
{plugin && (
|
||||
<Tab.Group>
|
||||
<div className="shadow-md">
|
||||
<Tab.List className="flex justify-between border-b border-gray-300 dark:border-gray-600">
|
||||
<TabButton>
|
||||
<FiSliders />
|
||||
</TabButton>
|
||||
<TabButton>
|
||||
<FiCode />
|
||||
</TabButton>
|
||||
</Tab.List>
|
||||
</div>
|
||||
<Tab.Panels className="flex flex-grow overflow-y-auto bg-gray-100 dark:bg-primaryDark">
|
||||
{plugin === 'Range Test' && (
|
||||
<>
|
||||
<RangeTestSettingsPanel />
|
||||
<RangeTestDebugPanel />
|
||||
</>
|
||||
)}
|
||||
{plugin === 'External Notifications' && (
|
||||
<>
|
||||
<ExternalNotificationsSettingsPlanel />
|
||||
<ExternalNotificationsDebugPanel />
|
||||
</>
|
||||
)}
|
||||
{plugin === 'Serial' && (
|
||||
<>
|
||||
<SerialSettingsPanel />
|
||||
<SerialDebugPanel />
|
||||
</>
|
||||
)}
|
||||
{plugin === 'Store & Forward' && (
|
||||
<>
|
||||
<StoreForwardSettingsPanel />
|
||||
<StoreForwardDebugPanel />
|
||||
</>
|
||||
)}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
)}
|
||||
</Sidebar>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import type React from 'react';
|
||||
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { CopyButton } from '@components/menu/buttons/CopyButton';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
export const ExternalNotificationsDebugPanel = (): JSX.Element => {
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const debugData = {
|
||||
extNotificationPluginActive: preferences.extNotificationPluginActive,
|
||||
extNotificationPluginAlertBell: preferences.extNotificationPluginAlertBell,
|
||||
extNotificationPluginAlertMessage:
|
||||
preferences.extNotificationPluginAlertMessage,
|
||||
extNotificationPluginEnabled: preferences.extNotificationPluginEnabled,
|
||||
extNotificationPluginOutput: preferences.extNotificationPluginOutput,
|
||||
extNotificationPluginOutputMs: preferences.extNotificationPluginOutputMs,
|
||||
};
|
||||
|
||||
return (
|
||||
<Tab.Panel className="relative">
|
||||
<div className="fixed right-0 m-2">
|
||||
<CopyButton data={JSON.stringify(debugData)} />
|
||||
</div>
|
||||
<JSONPretty className="max-w-sm" data={debugData} />
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { Form } from '@app/components/generic/form/Form';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const ExternalNotificationsSettingsPlanel = (): JSX.Element => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({
|
||||
defaultValues: preferences,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
setLoading(true);
|
||||
await connection.setPreferences(data, async (): Promise<void> => {
|
||||
reset({ ...data });
|
||||
setLoading(false);
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const pluginEnabled = useWatch({
|
||||
control,
|
||||
name: 'extNotificationPluginEnabled',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tab.Panel className="flex flex-col w-full">
|
||||
<Form loading={loading}>
|
||||
<Checkbox
|
||||
label="Plugin Enabled"
|
||||
{...register('extNotificationPluginEnabled')}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Active"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('extNotificationPluginActive')}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Bell"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('extNotificationPluginAlertBell')}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Message"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('extNotificationPluginAlertMessage')}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Output"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('extNotificationPluginOutput', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Output MS"
|
||||
suffix="ms"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('extNotificationPluginOutputMs', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark">
|
||||
<div className="p-2 ml-auto">
|
||||
<IconButton
|
||||
disabled={!formState.isDirty}
|
||||
onClick={async (): Promise<void> => {
|
||||
await onSubmit();
|
||||
}}
|
||||
icon={<FiSave />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import type React from 'react';
|
||||
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { CopyButton } from '@components/menu/buttons/CopyButton';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
export const RangeTestDebugPanel = (): JSX.Element => {
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const debugData = {
|
||||
rangeTestPluginEnabled: preferences.rangeTestPluginEnabled,
|
||||
rangeTestPluginSave: preferences.rangeTestPluginSave,
|
||||
rangeTestPluginSender: preferences.rangeTestPluginSender,
|
||||
};
|
||||
|
||||
return (
|
||||
<Tab.Panel className="relative">
|
||||
<div className="fixed right-0 m-2">
|
||||
<CopyButton data={JSON.stringify(debugData)} />
|
||||
</div>
|
||||
<JSONPretty className="max-w-sm" data={debugData} />
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { Form } from '@app/components/generic/form/Form';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const RangeTestSettingsPanel = (): JSX.Element => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({
|
||||
defaultValues: preferences,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
setLoading(true);
|
||||
await connection.setPreferences(data, async (): Promise<void> => {
|
||||
reset({ ...data });
|
||||
setLoading(false);
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const pluginEnabled = useWatch({
|
||||
control,
|
||||
name: 'rangeTestPluginEnabled',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tab.Panel className="flex flex-col w-full">
|
||||
<Form loading={loading}>
|
||||
<Checkbox
|
||||
label="Range Test Plugin Enabled?"
|
||||
{...register('rangeTestPluginEnabled')}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Range Test Plugin Save?"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('rangeTestPluginSave')}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Message Interval"
|
||||
disabled={!pluginEnabled}
|
||||
suffix="Seconds"
|
||||
{...register('rangeTestPluginSender', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark">
|
||||
<div className="p-2 ml-auto">
|
||||
<IconButton
|
||||
disabled={!formState.isDirty}
|
||||
onClick={async (): Promise<void> => {
|
||||
await onSubmit();
|
||||
}}
|
||||
icon={<FiSave />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import type React from 'react';
|
||||
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { CopyButton } from '@components/menu/buttons/CopyButton';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
export const SerialDebugPanel = (): JSX.Element => {
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const debugData = {
|
||||
serialpluginEnabled: preferences.serialpluginEnabled,
|
||||
serialpluginEcho: preferences.serialpluginEcho,
|
||||
serialpluginMode: preferences.serialpluginMode,
|
||||
serialpluginRxd: preferences.serialpluginRxd,
|
||||
serialpluginTxd: preferences.serialpluginTxd,
|
||||
serialpluginTimeout: preferences.serialpluginTimeout,
|
||||
};
|
||||
|
||||
return (
|
||||
<Tab.Panel className="relative">
|
||||
<div className="fixed right-0 m-2">
|
||||
<CopyButton data={JSON.stringify(debugData)} />
|
||||
</div>
|
||||
<JSONPretty className="max-w-sm" data={debugData} />
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { Form } from '@app/components/generic/form/Form';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const SerialSettingsPanel = (): JSX.Element => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({
|
||||
defaultValues: preferences,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
setLoading(true);
|
||||
await connection.setPreferences(data, async (): Promise<void> => {
|
||||
reset({ ...data });
|
||||
setLoading(false);
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const pluginEnabled = useWatch({
|
||||
control,
|
||||
name: 'serialpluginEnabled',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tab.Panel className="flex flex-col w-full">
|
||||
<Form loading={loading}>
|
||||
<Checkbox label="Plugin Enabled" {...register('serialpluginEnabled')} />
|
||||
<Checkbox
|
||||
label="Echo"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('serialpluginEcho')}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="number"
|
||||
label="RX"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('serialpluginRxd', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="TX"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('serialpluginTxd', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Mode"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('serialpluginMode', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Timeout"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('serialpluginTimeout', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark">
|
||||
<div className="p-2 ml-auto">
|
||||
<IconButton
|
||||
disabled={!formState.isDirty}
|
||||
onClick={async (): Promise<void> => {
|
||||
await onSubmit();
|
||||
}}
|
||||
icon={<FiSave />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import type React from 'react';
|
||||
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { CopyButton } from '@components/menu/buttons/CopyButton';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
export const StoreForwardDebugPanel = (): JSX.Element => {
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const debugData = {
|
||||
storeForwardPluginEnabled: preferences.storeForwardPluginEnabled,
|
||||
storeForwardPluginHeartbeat: preferences.storeForwardPluginHeartbeat,
|
||||
storeForwardPluginRecords: preferences.storeForwardPluginRecords,
|
||||
storeForwardPluginHistoryReturnMax:
|
||||
preferences.storeForwardPluginHistoryReturnMax,
|
||||
storeForwardPluginHistoryReturnWindow:
|
||||
preferences.storeForwardPluginHistoryReturnWindow,
|
||||
};
|
||||
|
||||
return (
|
||||
<Tab.Panel className="relative">
|
||||
<div className="fixed right-0 m-2">
|
||||
<CopyButton data={JSON.stringify(debugData)} />
|
||||
</div>
|
||||
<JSONPretty className="max-w-sm" data={debugData} />
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
|
||||
import { Form } from '@app/components/generic/form/Form';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import { Checkbox, IconButton, Input } from '@meshtastic/components';
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
|
||||
export const StoreForwardSettingsPanel = (): JSX.Element => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const preferences = useAppSelector(
|
||||
(state) => state.meshtastic.radio.preferences,
|
||||
);
|
||||
|
||||
const { register, handleSubmit, formState, reset, control } =
|
||||
useForm<Protobuf.RadioConfig_UserPreferences>({
|
||||
defaultValues: preferences,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
reset(preferences);
|
||||
}, [reset, preferences]);
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
setLoading(true);
|
||||
await connection.setPreferences(data, async (): Promise<void> => {
|
||||
reset({ ...data });
|
||||
setLoading(false);
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const pluginEnabled = useWatch({
|
||||
control,
|
||||
name: 'storeForwardPluginEnabled',
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tab.Panel className="flex flex-col w-full">
|
||||
<Form loading={loading}>
|
||||
<Checkbox
|
||||
label="Plugin Enabled"
|
||||
{...register('storeForwardPluginEnabled')}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Heartbeat Enabled"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('storeForwardPluginHeartbeat')}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="Number of records"
|
||||
suffix="Records"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('storeForwardPluginRecords', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="History return max"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('storeForwardPluginHistoryReturnMax', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
label="History return window"
|
||||
disabled={!pluginEnabled}
|
||||
{...register('storeForwardPluginHistoryReturnWindow', {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</Form>
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark">
|
||||
<div className="p-2 ml-auto">
|
||||
<IconButton
|
||||
disabled={!formState.isDirty}
|
||||
onClick={async (): Promise<void> => {
|
||||
await onSubmit();
|
||||
}}
|
||||
icon={<FiSave />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
);
|
||||
};
|
||||
+3
-4
@@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { FiSave } from 'react-icons/fi';
|
||||
import { MdRefresh, MdVisibility, MdVisibilityOff } from 'react-icons/md';
|
||||
|
||||
import { Loading } from '@app/components/generic/Loading';
|
||||
import { Form } from '@app/components/generic/form/Form';
|
||||
import { connection } from '@app/core/connection';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import { Checkbox, IconButton, Input, Select } from '@meshtastic/components';
|
||||
@@ -73,8 +73,7 @@ export const SettingsPanel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
|
||||
return (
|
||||
<Tab.Panel className="flex flex-col w-full">
|
||||
{loading && <Loading />}
|
||||
<form className="flex-grow gap-3 p-2">
|
||||
<Form loading={loading}>
|
||||
{channel?.index !== 0 && (
|
||||
<>
|
||||
<Checkbox
|
||||
@@ -122,7 +121,7 @@ export const SettingsPanel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
||||
/>
|
||||
<Checkbox label="Uplink Enabled" {...register('uplinkEnabled')} />
|
||||
<Checkbox label="Downlink Enabled" {...register('downlinkEnabled')} />
|
||||
</form>
|
||||
</Form>
|
||||
<div className="flex w-full bg-white dark:bg-secondaryDark">
|
||||
<div className="p-2 ml-auto">
|
||||
<IconButton
|
||||
Reference in New Issue
Block a user