diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index eefe7d5..63e8f9e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,23 +2,20 @@ mod device; mod ble; mod device_repo; -use crate::{ble::scan_devices, device::Device, device_repo::{repo_from_devices, try_from_file, Repo}}; +use crate::{ble::scan_devices, device_repo::{repo_from_devices, try_from_file, Repo}}; // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ #[tauri::command] -async fn read_repo() -> Vec { - vec![] +async fn read_repo() -> Repo { + match try_from_file() { + Ok(repo) => repo, + Err(_) => Repo::new() + } } #[tauri::command] async fn scan() -> Repo { - match try_from_file() { - Ok(repo) => repo, - Err(_) => { - let devices = (scan_devices(20).await).unwrap_or_default(); - repo_from_devices(devices) - } - } + repo_from_devices((scan_devices(5).await).unwrap_or_default()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] diff --git a/src/index.html b/src/index.html index 7cbffe5..58437f5 100644 --- a/src/index.html +++ b/src/index.html @@ -11,9 +11,7 @@

Welcome to RideInn

-
- Подключаем устройства... - +
diff --git a/src/main.js b/src/main.js index 03d5a18..c08b5e0 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,7 @@ +import { EVENTS } from "./state.js"; +import { fsm } from "./ui.js"; const { invoke } = window.__TAURI__.core; -let greetInputEl; -let greetMsgEl; - async function readRepo() { return await invoke("read_repo"); } @@ -11,18 +10,38 @@ async function scan() { return await invoke("scan"); } +const isArray = (maybeArray) => + maybeArray?.constructor && maybeArray?.constructor === Array; + +const isEmptyArray = (maybeArray) => + isArray(maybeArray) && maybeArray.length === 0; + +const getClosestDevices = () => { + console.warn("getClosestDevices NOT IMPLEMENTED YET"); + return []; +}; + +const isRepoEmpty = (repo) => + (!repo.cad_sensors || isEmptyArray(repo.cad_sensors)) && + (!repo.hr_sensors || isEmptyArray(repo.hr_sensors)) && + (!repo.trainers || isEmptyArray(repo.trainers)); + window.addEventListener("DOMContentLoaded", async () => { - const messageContainer = document.getElementById("msg"); - const findButton = document.getElementById("find"); + const [savedDevices, scannedDevices] = await Promise.all([ + readRepo(), + scan(), + ]); - const connectedDeviced = await readRepo(); + console.log(savedDevices); - if (connectedDeviced.length === 0) { - messageContainer.innerText = "Ищем устройства..."; + if (isRepoEmpty(savedDevices)) { + const closest = getClosestDevices(scannedDevices); + if (closest.length === 0) fsm.dispatch(EVENTS.TO_ERROR); + } else { + if (isRepoEmpty(scannedDevices)) { + fsm.dispatch(EVENTS.TO_MANUAL_CONNECT); + } else { + fsm.dispatch(EVENTS.TO_READY); + } } - - findButton.addEventListener("click", async () => { - const lol = await scan(); - console.log("devices", lol); - }); }); diff --git a/src/state.js b/src/state.js new file mode 100644 index 0000000..3235b55 --- /dev/null +++ b/src/state.js @@ -0,0 +1,58 @@ +const STATES = Object.freeze({ + INITIAL: "initial", + SEARCH: "search", + MANUAL_CONNECT: "manualConnect", + READY_TO_ROLL: "ready", + ERROR: "error", +}); + +const EVENTS = Object.freeze({ + TO_SEARCH: "toSearch", + TO_MANUAL_CONNECT: "toManualConnect", + TO_READY: "toReady", + TO_ERROR: "toError", +}); + +const transitions = new Map([ + [[STATES.INITIAL, EVENTS.TO_SEARCH], STATES.SEARCH], + [[STATES.INITIAL, EVENTS.TO_ERROR], STATES.ERROR], + + [[STATES.SEARCH, EVENTS.TO_MANUAL_CONNECT], STATES.MANUAL_CONNECT], + [[STATES.SEARCH, EVENTS.TO_READY], STATES.READY_TO_ROLL], + + [[STATES.MANUAL_CONNECT, EVENTS.TO_READY], STATES.READY_TO_ROLL], + [[STATES.MANUAL_CONNECT, EVENTS.TO_ERROR], STATES.ERROR], +]); + +function createFSM(initialState = STATES.INITIAL) { + const fsm = new EventTarget(); + let state = initialState; + + fsm.getState = () => state; + + fsm.dispatch = (eventName) => { + const key = JSON.stringify([state, eventName]); + const next = [...transitions.entries()].find( + ([k]) => JSON.stringify(k) === key, + )?.[1]; + + if (next) { + const prev = state; + state = next; + + fsm.dispatchEvent( + new CustomEvent("statechange", { + detail: { prev, next, trigger: eventName }, + }), + ); + + return true; + } + console.warn(`No transition from "${state}" on "${eventName}"`); + return false; + }; + + return fsm; +} + +export { createFSM, STATES, EVENTS }; diff --git a/src/styles.css b/src/styles.css index e57b8ac..e5864df 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,6 +1,3 @@ -.logo.vanilla:hover { - filter: drop-shadow(0 0 2em #ffe21c); -} :root { font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; @@ -17,80 +14,6 @@ -webkit-text-size-adjust: 100%; } -.container { - margin: 0; - padding-top: 10vh; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: 0.75s; -} - -.logo.tauri:hover { - filter: drop-shadow(0 0 2em #24c8db); -} - -.row { - display: flex; - justify-content: center; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - -h1 { - text-align: center; -} - -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { - cursor: pointer; -} - -button:hover { - border-color: #396cd8; -} -button:active { - border-color: #396cd8; - background-color: #e8e8e8; -} - -input, -button { - outline: none; -} - -#greet-input { - margin-right: 5px; -} - @media (prefers-color-scheme: dark) { :root { color: #f6f6f6; diff --git a/src/ui.js b/src/ui.js new file mode 100644 index 0000000..7be32bd --- /dev/null +++ b/src/ui.js @@ -0,0 +1,32 @@ +import { createFSM, STATES } from "./state.js"; + +const fsm = createFSM(); +const container = document.getElementById("app"); + +const pages = { + [STATES.INITIAL]: ` +

INITIAL

+ `, + [STATES.SEARCH]: ` +

SEARCH

+ `, + [STATES.MANUAL_CONNECT]: ` +

MANUAL CONNECT

+ `, + [STATES.ERROR]: ` +

ERROR

+ `, +}; + +function render(state) { + container.innerHTML = pages[state] ?? "

Unknown state

"; +} + +fsm.addEventListener("statechange", (e) => { + const { next } = e.detail; + render(next); +}); + +render(fsm.getState()); + +export { fsm };