committed by
GitHub
29 changed files with 5898 additions and 5237 deletions
@ -4,9 +4,9 @@ name: meshtastic-web build |
|||||
on: |
on: |
||||
# Triggers the workflow on push or pull request events but only for the master branch |
# Triggers the workflow on push or pull request events but only for the master branch |
||||
push: |
push: |
||||
branches: [ master ] |
branches: [master] |
||||
pull_request: |
pull_request: |
||||
branches: [ master ] |
branches: [master] |
||||
|
|
||||
# Allows you to run this workflow manually from the Actions tab |
# Allows you to run this workflow manually from the Actions tab |
||||
workflow_dispatch: |
workflow_dispatch: |
||||
@ -19,31 +19,31 @@ jobs: |
|||||
# Checks-out repository |
# Checks-out repository |
||||
- name: Checkout |
- name: Checkout |
||||
uses: actions/checkout@v2 |
uses: actions/checkout@v2 |
||||
|
- uses: pnpm/[email protected] |
||||
# Build project |
with: |
||||
|
version: 6.14.3 |
||||
- uses: actions/setup-node@v2 |
- uses: actions/setup-node@v2 |
||||
with: |
with: |
||||
node-version: '14' |
node-version: '16' |
||||
cache: 'yarn' |
cache: 'pnpm' |
||||
- run: yarn install --ignore-optional |
- run: pnpm lint |
||||
- run: yarn lint |
- run: pnpm build |
||||
- run: yarn build |
- run: pnpm package |
||||
- run: yarn package |
|
||||
- run: tree build/output |
- run: tree build/output |
||||
|
|
||||
# Create a zip file from the output folder |
# Create a zip file from the output folder |
||||
- name: Create output zip file |
- name: Create output zip file |
||||
uses: papeloto/action-zip@v1 |
uses: papeloto/action-zip@v1 |
||||
with: |
with: |
||||
files: build/output/ |
files: build/output/ |
||||
dest: output.zip |
dest: output.zip |
||||
|
|
||||
# Upload Artifact |
# Upload Artifact |
||||
- name: Upload a Build Artifact |
- name: Upload a Build Artifact |
||||
uses: "marvinpinto/action-automatic-releases@latest" |
uses: 'marvinpinto/action-automatic-releases@latest' |
||||
with: |
with: |
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}" |
repo_token: '${{ secrets.GITHUB_TOKEN }}' |
||||
automatic_release_tag: "latest" |
automatic_release_tag: 'latest' |
||||
prerelease: false |
prerelease: false |
||||
files: | |
files: | |
||||
output.zip |
output.zip |
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,158 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import ApexChart from 'react-apexcharts'; |
||||
|
|
||||
|
import { Button } from './Button'; |
||||
|
|
||||
|
type DefaultDivProps = JSX.IntrinsicElements['div']; |
||||
|
|
||||
|
interface ISeries { |
||||
|
name: string; |
||||
|
data: { |
||||
|
x: string | Date; |
||||
|
y: number; |
||||
|
}[]; |
||||
|
} |
||||
|
|
||||
|
interface ChartProps extends DefaultDivProps { |
||||
|
title: string; |
||||
|
description: string; |
||||
|
hasMultipleSeries: boolean; |
||||
|
series: ISeries[]; |
||||
|
} |
||||
|
|
||||
|
export const Chart = ({ |
||||
|
title, |
||||
|
description, |
||||
|
hasMultipleSeries, |
||||
|
series, |
||||
|
...props |
||||
|
}: ChartProps): JSX.Element => { |
||||
|
const [activeSeries, setActiveSeries] = React.useState<ISeries>(series[0]); |
||||
|
return ( |
||||
|
<div |
||||
|
className="flex flex-col flex-auto text-white shadow-md dark bg-primaryDark rounded-3xl" |
||||
|
{...props} |
||||
|
> |
||||
|
<div className="flex items-center justify-between mx-10 mt-10"> |
||||
|
<div className="flex flex-col"> |
||||
|
<div className="mr-4 text-2xl font-semibold leading-7 tracking-tight md:text-3xl"> |
||||
|
{title} |
||||
|
</div> |
||||
|
<div className="font-medium text-gray-400">{description}</div> |
||||
|
</div> |
||||
|
{hasMultipleSeries && ( |
||||
|
<div className="flex space-x-2"> |
||||
|
{series.map((data, index) => ( |
||||
|
<Button |
||||
|
active={data.name === activeSeries.name} |
||||
|
key={index} |
||||
|
className="font-medium" |
||||
|
onClick={(): void => { |
||||
|
setActiveSeries(series[index]); |
||||
|
}} |
||||
|
> |
||||
|
{data.name} |
||||
|
</Button> |
||||
|
))} |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
<div className="h-80"> |
||||
|
<ApexChart |
||||
|
height="96%" |
||||
|
type="area" |
||||
|
options={{ |
||||
|
chart: { |
||||
|
animations: { |
||||
|
speed: 400, |
||||
|
animateGradually: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
}, |
||||
|
toolbar: { |
||||
|
show: false, |
||||
|
}, |
||||
|
zoom: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
}, |
||||
|
colors: ['#818CF8'], |
||||
|
dataLabels: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
fill: { |
||||
|
colors: ['#312E81'], |
||||
|
}, |
||||
|
grid: { |
||||
|
padding: { |
||||
|
top: 10, |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
}, |
||||
|
|
||||
|
xaxis: { |
||||
|
lines: { |
||||
|
show: false, |
||||
|
}, |
||||
|
}, |
||||
|
yaxis: { |
||||
|
lines: { |
||||
|
show: false, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
stroke: { |
||||
|
width: 2, |
||||
|
}, |
||||
|
tooltip: { |
||||
|
followCursor: true, |
||||
|
theme: 'dark', |
||||
|
x: { |
||||
|
format: 'MMM dd, yyyy', |
||||
|
}, |
||||
|
y: { |
||||
|
formatter: (value: number): string => `${value}`, |
||||
|
}, |
||||
|
}, |
||||
|
xaxis: { |
||||
|
axisBorder: { |
||||
|
show: false, |
||||
|
}, |
||||
|
axisTicks: { |
||||
|
show: false, |
||||
|
}, |
||||
|
crosshairs: { |
||||
|
stroke: { |
||||
|
color: '#475569', |
||||
|
dashArray: 0, |
||||
|
width: 2, |
||||
|
}, |
||||
|
}, |
||||
|
labels: { |
||||
|
style: { |
||||
|
colors: '#CBD5E1', |
||||
|
}, |
||||
|
}, |
||||
|
tooltip: { |
||||
|
enabled: false, |
||||
|
}, |
||||
|
type: 'datetime', |
||||
|
}, |
||||
|
yaxis: { |
||||
|
axisTicks: { |
||||
|
show: false, |
||||
|
}, |
||||
|
axisBorder: { |
||||
|
show: false, |
||||
|
}, |
||||
|
show: false, |
||||
|
}, |
||||
|
}} |
||||
|
series={[activeSeries]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,47 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Tab } from '@headlessui/react'; |
||||
|
|
||||
|
type DefaultDivProps = JSX.IntrinsicElements['div']; |
||||
|
|
||||
|
interface TabProps extends DefaultDivProps { |
||||
|
tabs: { |
||||
|
name: string; |
||||
|
body: JSX.Element; |
||||
|
}[]; |
||||
|
} |
||||
|
|
||||
|
export const Tabs = ({ tabs, className, ...props }: TabProps): JSX.Element => { |
||||
|
return ( |
||||
|
<Tab.Group as="div" className={className}> |
||||
|
<Tab.List className="flex border-l border-r border-t shadow-md rounded-t-3xl dark:border-gray-600"> |
||||
|
{tabs.map((tab) => ( |
||||
|
<Tab |
||||
|
key={tab.name} |
||||
|
className={({ selected }): string => |
||||
|
`w-full text-lg font-medium p-2 border-b-2 ${ |
||||
|
selected |
||||
|
? 'dark:border-gray-200 border-gray-600' |
||||
|
: 'border-transparent dark:border-transparent' |
||||
|
}` |
||||
|
} |
||||
|
> |
||||
|
{tab.name} |
||||
|
</Tab> |
||||
|
))} |
||||
|
</Tab.List> |
||||
|
<Tab.Panels className="h-full"> |
||||
|
{tabs.map((tab, index) => ( |
||||
|
<Tab.Panel |
||||
|
key={index} |
||||
|
className={ |
||||
|
'border dark:border-gray-600 rounded-b-3xl p-4 h-full shadow-md' |
||||
|
} |
||||
|
> |
||||
|
{tab.body} |
||||
|
</Tab.Panel> |
||||
|
))} |
||||
|
</Tab.Panels> |
||||
|
</Tab.Group> |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,51 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Switch } from '@headlessui/react'; |
||||
|
|
||||
|
type DefaultButtonProps = JSX.IntrinsicElements['button']; |
||||
|
|
||||
|
interface ToggleProps extends DefaultButtonProps { |
||||
|
label?: string; |
||||
|
valid?: boolean; |
||||
|
validationMessage?: string; |
||||
|
} |
||||
|
|
||||
|
export const Toggle = ({ |
||||
|
label, |
||||
|
valid, |
||||
|
validationMessage, |
||||
|
id, |
||||
|
...props |
||||
|
}: ToggleProps): JSX.Element => { |
||||
|
const [enabled, setEnabled] = React.useState(false); |
||||
|
|
||||
|
return ( |
||||
|
<div className="w-full"> |
||||
|
<label htmlFor={id} className="block text-sm font-medium dark:text-white"> |
||||
|
{label} |
||||
|
</label> |
||||
|
<div className="float-right"> |
||||
|
<Switch |
||||
|
id={id} |
||||
|
{...props} |
||||
|
checked={enabled} |
||||
|
onChange={setEnabled} |
||||
|
className={`${ |
||||
|
enabled ? 'bg-primary' : 'bg-gray-200 dark:bg-primaryDark' |
||||
|
} |
||||
|
relative inline-flex flex-shrink-0 h-[38px] w-[74px] border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75`}
|
||||
|
> |
||||
|
<span className="sr-only">Use setting</span> |
||||
|
<span |
||||
|
aria-hidden="true" |
||||
|
className={`${enabled ? 'translate-x-9' : 'translate-x-0'} |
||||
|
pointer-events-none inline-block h-[34px] w-[34px] rounded-full bg-white shadow-lg transform ring-0 transition ease-in-out duration-200`}
|
||||
|
/> |
||||
|
</Switch> |
||||
|
</div> |
||||
|
{!valid && ( |
||||
|
<div className="text-sm text-gray-600">{validationMessage}</div> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -1,31 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import Avatar from 'boring-avatars'; |
|
||||
|
|
||||
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
type DefaultDivProps = JSX.IntrinsicElements['div']; |
|
||||
|
|
||||
export interface NodeProps { |
|
||||
node: Protobuf.NodeInfo; |
|
||||
} |
|
||||
|
|
||||
export const Node = ({ |
|
||||
node, |
|
||||
...props |
|
||||
}: NodeProps & DefaultDivProps): JSX.Element => { |
|
||||
return ( |
|
||||
<div |
|
||||
{...props} |
|
||||
className="flex space-x-4 items-center w-full rounded-md dark:bg-primaryDark shadow-md border dark:border-gray-600 p-2 mt-6 dark:text-white hover:bg-gray-200 dark:hover:bg-gray-900" |
|
||||
> |
|
||||
<Avatar |
|
||||
size={30} |
|
||||
name={node.user?.longName ?? 'UNK'} |
|
||||
variant="beam" |
|
||||
colors={['#213435', '#46685B', '#648A64', '#A6B985', '#E1E3AC']} |
|
||||
/> |
|
||||
<div>{node.user?.longName}</div> |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
@ -1,3 +1,9 @@ |
|||||
import { IHTTPConnection } from '@meshtastic/meshtasticjs'; |
import { |
||||
|
IBLEConnection, |
||||
|
IHTTPConnection, |
||||
|
ISerialConnection, |
||||
|
} from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
export const connection = new IHTTPConnection(); |
export const connection = new IHTTPConnection(); |
||||
|
export const bleConnection = new IBLEConnection(); |
||||
|
export const serialConnection = new ISerialConnection(); |
||||
|
|||||
@ -0,0 +1,137 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
import { useTranslation } from 'react-i18next'; |
||||
|
|
||||
|
import { Input } from '@app/components/generic/Input'; |
||||
|
import { Tabs } from '@app/components/generic/Tabs'; |
||||
|
import { Toggle } from '@app/components/generic/Toggle'; |
||||
|
import { |
||||
|
bleConnection, |
||||
|
connection, |
||||
|
serialConnection, |
||||
|
} from '@app/core/connection'; |
||||
|
import { useAppSelector } from '@app/hooks/redux'; |
||||
|
import { Button } from '@components/generic/Button'; |
||||
|
import { PrimaryTemplate } from '@components/templates/PrimaryTemplate'; |
||||
|
import { LinkIcon, MenuIcon, SaveIcon } from '@heroicons/react/outline'; |
||||
|
import type { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export interface ConnectionProps { |
||||
|
navOpen: boolean; |
||||
|
setNavOpen: React.Dispatch<React.SetStateAction<boolean>>; |
||||
|
} |
||||
|
|
||||
|
export const Connection = ({ |
||||
|
navOpen, |
||||
|
setNavOpen, |
||||
|
}: ConnectionProps): JSX.Element => { |
||||
|
const { t } = useTranslation(); |
||||
|
const user = useAppSelector((state) => state.meshtastic.user); |
||||
|
|
||||
|
const { register, handleSubmit, formState } = useForm<Protobuf.User>({ |
||||
|
defaultValues: user, |
||||
|
}); |
||||
|
|
||||
|
const onSubmit = handleSubmit((data) => { |
||||
|
void connection.setOwner(data); |
||||
|
}); |
||||
|
|
||||
|
return ( |
||||
|
<PrimaryTemplate |
||||
|
title="Connection" |
||||
|
tagline="Settings" |
||||
|
button={ |
||||
|
<Button |
||||
|
icon={<MenuIcon className="w-5 h-5" />} |
||||
|
onClick={(): void => { |
||||
|
setNavOpen(!navOpen); |
||||
|
}} |
||||
|
circle |
||||
|
/> |
||||
|
} |
||||
|
footer={ |
||||
|
<Button |
||||
|
className="px-10 ml-auto" |
||||
|
icon={<SaveIcon className="w-5 h-5" />} |
||||
|
disabled={!formState.isDirty} |
||||
|
active |
||||
|
border |
||||
|
> |
||||
|
{t('strings.save_changes')} |
||||
|
</Button> |
||||
|
} |
||||
|
> |
||||
|
<div className="w-full max-w-3xl md:max-w-xl"> |
||||
|
<div className="mb-2 flex w-full border dark:border-gray-600 rounded-3xl p-2"> |
||||
|
Current connection method: |
||||
|
<div className="ml-2 rounded-full bg-gray-400 dark:bg-primaryDark text-sm px-1 my-auto"> |
||||
|
BLE |
||||
|
</div> |
||||
|
</div> |
||||
|
<form className="space-y-2" onSubmit={onSubmit}> |
||||
|
<Tabs |
||||
|
className="h-60" |
||||
|
tabs={[ |
||||
|
{ |
||||
|
name: 'HTTP', |
||||
|
body: ( |
||||
|
<div className="space-y-2"> |
||||
|
<Input label={'Device URL'} /> |
||||
|
<Toggle label="Use TLS?" /> |
||||
|
</div> |
||||
|
), |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Bluetooth', |
||||
|
body: ( |
||||
|
<div className="space-y-2"> |
||||
|
Devices: |
||||
|
<Button |
||||
|
onClick={async (): Promise<void> => { |
||||
|
console.log(await bleConnection.getDevices()); |
||||
|
}} |
||||
|
> |
||||
|
Get Devices |
||||
|
</Button> |
||||
|
<div className="flex justify-between rounded-3xl border dark:border-600 p-2"> |
||||
|
Device Name |
||||
|
<LinkIcon className="my-auto mr-2 w-5 h-5 text-gray-300" /> |
||||
|
</div> |
||||
|
<div className="flex justify-between rounded-3xl border dark:border-600 p-2"> |
||||
|
Device Name |
||||
|
<LinkIcon className="my-auto mr-2 w-5 h-5 text-gray-600" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
), |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Serial', |
||||
|
body: ( |
||||
|
<div className="space-y-2"> |
||||
|
Devices: |
||||
|
<Button |
||||
|
onClick={async (): Promise<void> => { |
||||
|
console.log(await serialConnection.getPorts()); |
||||
|
}} |
||||
|
> |
||||
|
Get Devices |
||||
|
</Button> |
||||
|
<div className="flex justify-between rounded-3xl border dark:border-600 p-2"> |
||||
|
Device Name |
||||
|
<LinkIcon className="my-auto mr-2 w-5 h-5 text-gray-300" /> |
||||
|
</div> |
||||
|
<div className="flex justify-between rounded-3xl border dark:border-600 p-2"> |
||||
|
Device Name |
||||
|
<LinkIcon className="my-auto mr-2 w-5 h-5 text-gray-600" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
), |
||||
|
}, |
||||
|
]} |
||||
|
/> |
||||
|
</form> |
||||
|
</div> |
||||
|
</PrimaryTemplate> |
||||
|
); |
||||
|
}; |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue