Rebuild
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
|
||||
import useSWR from 'swr';
|
||||
|
||||
import fetcher from '@app/core/utils/fetcher';
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector';
|
||||
|
||||
export interface File {
|
||||
nameModified: string;
|
||||
name: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface Files {
|
||||
data: {
|
||||
files: File[];
|
||||
fileSystem: {
|
||||
total: number;
|
||||
used: number;
|
||||
free: number;
|
||||
};
|
||||
};
|
||||
status: string;
|
||||
}
|
||||
|
||||
export const FileBrowser = (): JSX.Element => {
|
||||
const connectionParams = useAppSelector(
|
||||
(state) => state.app.connectionParams,
|
||||
);
|
||||
|
||||
const { data } = useSWR<Files>(
|
||||
`${connectionParams.HTTP.tls ? 'https' : 'http'}://${
|
||||
connectionParams.HTTP.address
|
||||
}/json/spiffs/browse/static`,
|
||||
fetcher,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full select-none flex-col gap-4 p-4">
|
||||
<div className="w-full flex-grow rounded-md bg-gray-200 dark:bg-primaryDark">
|
||||
<div className="flex h-10 w-full rounded-t-md bg-gray-300 px-4 text-lg font-semibold dark:bg-zinc-700 dark:text-white">
|
||||
<div className="my-auto w-1/3">FileName</div>
|
||||
<div className="my-auto w-1/3">Actions</div>
|
||||
</div>
|
||||
<div className="px-4">
|
||||
{data?.data.files.map((file) => (
|
||||
<div
|
||||
key={file.name}
|
||||
className="flex h-10 w-full border-b border-gray-500 px-4 font-medium dark:text-white"
|
||||
>
|
||||
<div className="my-auto w-1/3">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`${connectionParams.HTTP.tls ? 'https' : 'http'}://${
|
||||
connectionParams.HTTP.address
|
||||
}/${file.name.replace('static/', '')}`}
|
||||
>
|
||||
{file.name.replace('static/', '').replace('.gz', '')}
|
||||
</a>
|
||||
</div>
|
||||
<div className="my-auto w-1/3"></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FiFile, FiInfo } from 'react-icons/fi';
|
||||
import { RiPinDistanceFill } from 'react-icons/ri';
|
||||
import { VscExtensions } from 'react-icons/vsc';
|
||||
|
||||
import { Layout } from '@app/components/layout';
|
||||
import { ExternalSection } from '@app/components/layout/Sidebar/sections/ExternalSection';
|
||||
|
||||
import { FileBrowser } from './FileBrowser';
|
||||
import { Info } from './Info';
|
||||
|
||||
export const Extensions = (): JSX.Element => {
|
||||
const [selectedExtension, setSelectedExtension] = React.useState<
|
||||
'info' | 'fileBrowser' | 'rangeTest'
|
||||
>('info');
|
||||
|
||||
return (
|
||||
<Layout
|
||||
title="Extensions"
|
||||
icon={<VscExtensions />}
|
||||
sidebarContents={
|
||||
<div className="absolute flex h-full w-full flex-col dark:bg-primaryDark">
|
||||
<ExternalSection
|
||||
onClick={(): void => {
|
||||
setSelectedExtension('info');
|
||||
}}
|
||||
icon={<FiInfo />}
|
||||
title="Node Info"
|
||||
/>
|
||||
<ExternalSection
|
||||
onClick={(): void => {
|
||||
setSelectedExtension('fileBrowser');
|
||||
}}
|
||||
icon={<FiFile />}
|
||||
title="File Browser"
|
||||
/>
|
||||
<ExternalSection
|
||||
onClick={(): void => {
|
||||
setSelectedExtension('rangeTest');
|
||||
}}
|
||||
icon={<RiPinDistanceFill />}
|
||||
title="Range Test"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="w-full">
|
||||
{selectedExtension === 'info' && <Info />}
|
||||
|
||||
{selectedExtension === 'fileBrowser' && <FileBrowser />}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
|
||||
import { m } from 'framer-motion';
|
||||
import JSONPretty from 'react-json-pretty';
|
||||
|
||||
import { useAppSelector } from '@app/hooks/useAppSelector.js';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import skypack_hashicon from '@skypack/@emeraldpay/hashicon-react';
|
||||
|
||||
const Hashicon = skypack_hashicon.Hashicon;
|
||||
// import { Hashicon } from '@emeraldpay/hashicon-react';
|
||||
|
||||
export const Info = (): JSX.Element => {
|
||||
const hardwareInfo = useAppSelector(
|
||||
(state) => state.meshtastic.radio.hardware,
|
||||
);
|
||||
const node = useAppSelector((state) =>
|
||||
state.meshtastic.nodes.find(
|
||||
(node) => node.number === hardwareInfo.myNodeNum,
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex w-full select-none flex-col gap-4 p-4">
|
||||
<m.div
|
||||
whileHover={{ scale: 1.01 }}
|
||||
className="flex w-full flex-col gap-4 rounded-md p-8 shadow-md dark:bg-primaryDark"
|
||||
>
|
||||
<div className="m-auto">
|
||||
<Hashicon value={hardwareInfo.myNodeNum.toString()} size={180} />
|
||||
</div>
|
||||
<div className="text-center text-lg font-medium dark:text-white">
|
||||
{node?.user?.longName || 'Unknown'}
|
||||
</div>
|
||||
</m.div>
|
||||
|
||||
<div className="rounded-md p-8 shadow-md dark:bg-primaryDark">
|
||||
<JSONPretty data={hardwareInfo} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user