Skip to content

Commit

Permalink
Ap mode (#6)
Browse files Browse the repository at this point in the history
* connected missing logic for button

* added ap mode status

* integrated ap mode logic

* ap mode ui
  • Loading branch information
luckmer authored Jan 26, 2024
1 parent e1fb74e commit 18a4087
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 44 deletions.
4 changes: 2 additions & 2 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export interface IProps {

export const Footer: Component<IProps> = (props) => {
return (
<footer class=" flex w-full pb-[24px] pr-[40px] pl-[40px] justify-end gap-[10px]">
<footer class="flex w-full pb-[24px] pr-[40px] pl-[40px] justify-end gap-[10px]">
<Show when={props.onClickSecond}>
<Button
isActive={false}
isActive={props.isSecondActive}
type={props.secondType}
label={props.secondLabel ?? ''}
onClick={props.onClickSecond}
Expand Down
119 changes: 111 additions & 8 deletions src/containers/FlashFirmware/FlashFirmware.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import { useNavigate } from '@solidjs/router'
import { ask } from '@tauri-apps/api/dialog'
import { listen } from '@tauri-apps/api/event'
import { removeFile } from '@tauri-apps/api/fs'
import { appConfigDir, appDataDir, join } from '@tauri-apps/api/path'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import { WebviewWindow, getCurrent } from '@tauri-apps/api/window'
import { createEffect, createMemo, createSignal } from 'solid-js'
import { WebviewWindow, appWindow, getCurrent } from '@tauri-apps/api/window'
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'
import { debug, error } from 'tauri-plugin-log-api'
import FlashFirmware from '@pages/FlashFirmware/FlashFirmware'
import { installModalClassName, installModalTarget, installationSuccess, usb } from '@src/static'
import { ENotificationType } from '@src/static/types/enums'
import { ENotificationType, TITLEBAR_ACTION } from '@src/static/types/enums'
import { CustomHTMLElement } from '@src/static/types/interfaces'
import { useAppAPIContext } from '@store/context/api'
import { useAppNotificationsContext } from '@store/context/notifications'

export const ManageFlashFirmware = () => {
const navigate = useNavigate()

const { downloadAsset, getFirmwareType, activeBoard, ssid, password } = useAppAPIContext()
const {
downloadAsset,
getFirmwareType,
activeBoard,
ssid,
password,
apModeStatus,
setAPModeStatus,
useRequestHook,
} = useAppAPIContext()
const { addNotification } = useAppNotificationsContext()

const [manifest, setManifest] = createSignal<string>('')
const [port, setPort] = createSignal<Navigator | null>(null)
const [installationConfirmed, setInstallationConfirmed] = createSignal<boolean>(false)
const [response, setResponse] = createSignal<object>()

const erase = async () => {
const appConfigPath = await appConfigDir()
Expand All @@ -30,8 +41,65 @@ export const ManageFlashFirmware = () => {
await removeFile(manifestPath)
}

const isUSBBoard = createMemo(() => {
return activeBoard().includes(usb)
const isUSBBoard = createMemo(() => activeBoard().includes(usb))

const _listen = async () => {
const unlisten = await listen<string>('request-response', (event) => {
const parsedResponse = JSON.parse(event.payload)
setResponse(parsedResponse)
debug(`[NetworkSettings]: ${JSON.stringify(parsedResponse)}`)
})
return unlisten
}

const listenToResponse = async () => {
const unlisten = await _listen()
onCleanup(unlisten)
}

const configureAPConnection = async () => {
addNotification({
title: 'Making request',
message: 'Making request',
type: ENotificationType.INFO,
})
debug(`ssid: ${ssid()}`)
debug(`pass: ${password()}`)
debug(`confirmPass: ${password()}`)

//* Check if there is a response from the device
await useRequestHook('ping', '192.168.4.1')

if (response()!['msg'] !== 'ok') {
addNotification({
title: 'Error',
message:
'Could not connect to device, please connect your PC to the EyeTrackVR Access Point and try again.',
type: ENotificationType.ERROR,
})
return
}

//* Make Request to set network settings
await useRequestHook(
'wifi',
'192.168.4.1',
`?ssid=${ssid()}&password=${password()}&networkName=${ssid()}&channel=1&power=52&adhoc=0`,
)

//* Trigger save of network settings
addNotification({
title: 'Success',
message: response()!['msg'],
type: ENotificationType.SUCCESS,
})
await useRequestHook('save', '192.168.4.1')
}

createEffect(() => {
if (apModeStatus()) {
listenToResponse().catch(console.error)
}
})

createEffect(() => {
Expand Down Expand Up @@ -117,14 +185,49 @@ export const ManageFlashFirmware = () => {
})
}
if (installationConfirmed() && port() !== null) {
configureWifiConnection().catch(handleWifiConfigurationError)
if (!apModeStatus()) {
configureWifiConnection().catch(handleWifiConfigurationError)
}
}
})

return (
<FlashFirmware
isAPModeActive={apModeStatus()}
isUSBBoard={isUSBBoard()}
manifest={manifest()}
onClickEnableAPMode={() => setAPModeStatus(!apModeStatus())}
onClickHeader={(action: TITLEBAR_ACTION) => {
switch (action) {
case TITLEBAR_ACTION.MINIMIZE:
appWindow.minimize()
break
case TITLEBAR_ACTION.MAXIMIZE:
appWindow.toggleMaximize()
break
case TITLEBAR_ACTION.CLOSE:
appWindow.close()
break
default:
return
}
}}
onClickConfigurAPMode={() => {
if (!apModeStatus()) return
configureAPConnection().catch(() => {
addNotification({
title: 'AP Mode configuration failed',
message: 'Failed to configure AP Mode',
type: ENotificationType.ERROR,
})
})
}}
onClickOpenModal={(id) => {
const el = document.getElementById(id)
if (el instanceof HTMLDialogElement) {
el.showModal()
}
}}
checkSameFirmware={(manifest, improvInfo) => {
const manifestFirmware = manifest.name.toLowerCase()
const deviceFirmware = improvInfo.firmware.toLowerCase()
Expand Down
105 changes: 105 additions & 0 deletions src/pages/APMode/APMode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { FaSolidXmark } from 'solid-icons/fa'
import { Component, createMemo } from 'solid-js'
import { Button } from '@components/Buttons/DefaultButton'
import { Titlebar } from '@components/Titlebar/Titlebar'
import { apModalID } from '@src/static'
import { TITLEBAR_ACTION } from '@src/static/types/enums'

export interface IProps {
onClickOpenModal: (id: string) => void
onClickHeader: (action: TITLEBAR_ACTION) => void
onClickEnableAPMode: () => void
onClickConfigurAPMode: () => void
isAPModeActive: boolean
}

export const APMode: Component<IProps> = (props) => {
const styles = createMemo(() => {
if (props.isAPModeActive) {
return 'ml-auto flex items-center justify-center lead pt-[10.5px] pb-[10.5px] pl-[10.5px] pr-[10.5px] rounded-full border border-solid border-[#192736] bg-[#9793FD] cursor-pointer focus-visible:border-[#fff]'
}
return 'ml-auto flex items-center justify-center lead pt-[10.5px] pb-[10.5px] pl-[10.5px] pr-[10.5px] rounded-full border border-solid border-[#192736] bg-[#0D1B26] cursor-pointer focus-visible:border-[#9793FD]'
})

const buttonLabel = createMemo(() =>
props.isAPModeActive ? 'Disable AP mode' : 'Enable AP mode',
)

return (
<div>
<button
class={styles()}
onClick={(e) => {
e.preventDefault()
props.onClickOpenModal(apModalID)
}}>
<p class="text-white leading-[12px]">AP mode</p>
</button>
<dialog id={apModalID} class="modal">
<Titlebar onClickHeader={props.onClickHeader} />
<div class="modal-box w-auto h-auto bg-transparent overflow-visible">
<div class=" w-[500px] bg-[#0D1B26] p-[12px] rounded-[12px] border border-solid border-[#192736] z-10">
<div class="flex flex-col gap-[14px]">
<div class="flex justify-between">
<div>
<p class="text-left text-[18px] text-white font-[500] leading-[20px] not-italic">
AP mode
</p>
</div>
<div class="modal-action mt-0">
<form method="dialog">
<button class="cursor-pointer p-[4px] rounded-full border border-solid border-[#0D1B26] focus-visible:border-[#9793FD]">
<p class="text-white text-left">
<FaSolidXmark size={20} fill="#FFFFFF" />
</p>
</button>
</form>
</div>
</div>
<div class="flex flex-col gap-[14px]">
<div>
<p class="text-left text-[18px] text-[#9793FD] font-[200] leading-[20px] ">
Important!
</p>
</div>
<div>
{props.isAPModeActive ? (
<p class="text-left text-[14px] text-white font-[500] not-italic">
Before pressing the <code>Send AP Request</code> check
that you have the firmware already{' '}
<code>installed</code> and you are connected to
<code>EyeTrackVR</code> Wi-Fi.
</p>
) : (
<p class="text-left text-[14px] text-white font-[500] not-italic">
Read the <code>documentation</code> before turning on{' '}
<code>AP mode</code>.
</p>
)}
</div>
</div>
<div class="flex justify-center gap-[10px]">
<Button
isActive={props.isAPModeActive}
type={'button'}
label={buttonLabel()}
onClick={props.onClickEnableAPMode}
/>
<Button
isLoadingPrimaryButton={false}
isActive={false}
type={'button'}
label={'Send AP request'}
onClick={props.onClickConfigurAPMode}
/>
</div>
</div>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button class="cursor-default" />
</form>
</dialog>
</div>
)
}
78 changes: 48 additions & 30 deletions src/pages/FlashFirmware/FlashFirmware.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,70 @@ import { Component } from 'solid-js'
import { EspWebButton } from '@components/Buttons/EspWebButton/EspWebButton'
import { FlashButton } from '@components/Buttons/FlashButton/FlashButton'
import { Footer } from '@components/Footer/Footer'
import { APMode } from '@pages/APMode/APMode'
import { TITLEBAR_ACTION } from '@src/static/types/enums'

export interface IProps {
onClickBack: () => void
onClickDownloadFirmware: () => void
onClickOpenDocs: () => void
onClickEraseSoft: () => void
onClickConfigurAPMode: () => void
manifest: string
checkSameFirmware: (manifest: { name: string }, improvInfo: { firmware: string }) => void
onClickOpenModal: (id: string) => void
onClickHeader: (action: TITLEBAR_ACTION) => void
onClickEnableAPMode: () => void
isUSBBoard: boolean
isAPModeActive: boolean
}

const AppSettingsPage: Component<IProps> = (props) => {
return (
<div class="flex flex-col justify-between h-full mr-[24px] ml-[24px]">
<div class="flex h-full justify-center items-center">
<div class="bg-[#0D1B26] w-auto p-[24px] flex flex-col gap-[22px] rounded-[24px] border-solid border-1 border-[#192736]">
<div>
<p class="text-white font-[500] leading-[20px] text-[20px] not-italic text-left">
Flash settings
</p>
</div>
<div class="grid grid-cols-2 grid-rows-2 min-[800px]:grid-rows-1 min-[800px]:grid-cols-4 grid-flow-col gap-[16px]">
<FlashButton
step="1/2"
label="Download firmware assets"
onClick={props.onClickDownloadFirmware}
img={<FaSolidDownload size={48} fill="#FFFFFFe3" />}
/>
<EspWebButton
step="2/2"
label="Flash mode"
img={<FaSolidPlug size={48} fill="#FFFFFFe3" />}
manifest={props.manifest}
checkSameFirmware={props.checkSameFirmware}
/>
<FlashButton
label="Open ETVR Docs"
onClick={props.onClickOpenDocs}
img={<FaSolidGraduationCap size={48} fill="#FFFFFFe3" />}
/>
<FlashButton
label="Erase Firmware Assets"
onClick={props.onClickEraseSoft}
img={<FaSolidTrashCan size={48} fill="#FFFFFFe3" />}
<div class="flex flex-col h-full justify-center items-center">
<div class="flex flex-col gap-[10px]">
<div class="flex flex-row">
<APMode
isAPModeActive={props.isAPModeActive}
onClickOpenModal={props.onClickOpenModal}
onClickHeader={props.onClickHeader}
onClickEnableAPMode={props.onClickEnableAPMode}
onClickConfigurAPMode={props.onClickConfigurAPMode}
/>
</div>
<div class="bg-[#0D1B26] w-auto p-[24px] flex flex-col gap-[22px] rounded-[24px] border-solid border-1 border-[#192736]">
<div>
<p class="text-white font-[500] leading-[20px] text-[20px] not-italic text-left">
Flash settings
</p>
</div>
<div class="grid grid-cols-2 grid-rows-2 min-[800px]:grid-rows-1 min-[800px]:grid-cols-4 grid-flow-col gap-[16px]">
<FlashButton
step="1/2"
label="Download firmware assets"
onClick={props.onClickDownloadFirmware}
img={<FaSolidDownload size={48} fill="#FFFFFFe3" />}
/>
<EspWebButton
step="2/2"
label="Flash mode"
img={<FaSolidPlug size={48} fill="#FFFFFFe3" />}
manifest={props.manifest}
checkSameFirmware={props.checkSameFirmware}
/>
<FlashButton
label="Open ETVR Docs"
onClick={props.onClickOpenDocs}
img={<FaSolidGraduationCap size={48} fill="#FFFFFFe3" />}
/>
<FlashButton
label="Erase Firmware Assets"
onClick={props.onClickEraseSoft}
img={<FaSolidTrashCan size={48} fill="#FFFFFFe3" />}
/>
</div>
</div>
</div>
</div>
<Footer
Expand Down
1 change: 1 addition & 0 deletions src/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const installModalClassName = 'mdc-button__label'
export const installModalTarget = 'Install'
export const installationSuccess = 'Installation complete!'
export const questionModalId = 'questionModal'
export const apModalID = 'apMode'
export const debugModalId = 'debugModal'

const circleSize = Math.PI * (radius * 2)
Expand Down
1 change: 1 addition & 0 deletions src/static/types/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export interface AppStoreAPI {
activeBoard: string
ssid: string
password: string
apModeStatus: boolean
}

export interface UiStore {
Expand Down
Loading

0 comments on commit 18a4087

Please sign in to comment.