From 30aaa49699facc7cbe1be8d875bd5057a1f509a5 Mon Sep 17 00:00:00 2001 From: Amos Wong Date: Thu, 15 Feb 2024 23:50:15 +0800 Subject: [PATCH] feat: install basic install feature --- index.html | 7 + package.json | 1 + src/App.tsx | 3 +- .../AppStorageController.tsx | 19 +- src/services/Espruino/Comms.ts | 164 ++++++++++++++++++ src/services/GlobalProgress.tsx | 49 ++++++ yarn.lock | 12 ++ 7 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 src/services/Espruino/Comms.ts create mode 100644 src/services/GlobalProgress.tsx diff --git a/index.html b/index.html index 17074b6..cf0d835 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,13 @@
+ + + + + + + diff --git a/package.json b/package.json index 58c4b7f..3d0a194 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "react-router-dom": "^6.22.0", "rehype-highlight": "^7.0.0", "rehype-sanitize": "^6.0.0", diff --git a/src/App.tsx b/src/App.tsx index cefd644..e7e7f54 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,6 +9,7 @@ import { } from "react-router-dom"; import App from './views/Apps'; import { SWRConfig } from 'swr'; +import { GlobalProgressToaster } from './services/GlobalProgress'; const router = createHashRouter( createRoutesFromElements( @@ -32,11 +33,11 @@ const router = createHashRouter( ) ); - function Entry() { return ( + ) } diff --git a/src/components/AppListItemDetailView/AppStorageController.tsx b/src/components/AppListItemDetailView/AppStorageController.tsx index f865b8b..338e8b3 100644 --- a/src/components/AppListItemDetailView/AppStorageController.tsx +++ b/src/components/AppListItemDetailView/AppStorageController.tsx @@ -3,6 +3,7 @@ import { AppDetailViewProps } from "./interface"; import { UiButton } from "../Buttons/UiButton"; import { ButtonIconContainer } from "../Buttons/ButtonIconContainer"; import { faGear, faDownload } from "@fortawesome/free-solid-svg-icons"; +import { EspruinoComms } from "../../services/Espruino/Comms"; interface ControlButtonProps extends AppDetailViewProps { hasConfiguration: boolean; @@ -24,7 +25,23 @@ const InstallControlButton = (props: ControlButtonProps) => { } return ( - + { + try { + const device = await EspruinoComms.getDeviceInfo(); + await EspruinoComms.uploadApp(props.app, { + device: { + ...device, + appsInstalled: device.apps, + } + }) + } catch (error) { + alert((error as Error).message); + throw error; + } + }} + > diff --git a/src/services/Espruino/Comms.ts b/src/services/Espruino/Comms.ts new file mode 100644 index 0000000..cf867b5 --- /dev/null +++ b/src/services/Espruino/Comms.ts @@ -0,0 +1,164 @@ +import { AppItem } from "../../api/banglejs/interface"; + +export const EspruinoComms: typeof Comms = { + // write: () => { + // throw new Error("not implemented"); + // }, + // showMessage: () => { + // throw new Error("not implemented"); + // }, + // showUploadFinished: () => { + // throw new Error("not implemented"); + // }, + // getProgressCmd: () => { + // throw new Error("not implemented"); + // }, + // reset: () => { + // throw new Error("not implemented"); + // }, + // uploadCommandList: () => { + // throw new Error("not implemented"); + // }, + uploadApp: (...args) => { + return Comms.uploadApp(...args) + }, + getDeviceInfo: (...args) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + console.log(Comms); + return Comms.getDeviceInfo(...args) + }, + // getAppInfo: () => { + // throw new Error("not implemented"); + // }, + // removeApp: () => { + // throw new Error("not implemented"); + // }, + // removeAllApps: () => { + // throw new Error("not implemented"); + // }, + // setTime: () => { + // throw new Error("not implemented"); + // }, + // resetDevice: () => { + // throw new Error("not implemented"); + // }, + isConnected: () => { + return Comms.isConnected() + }, + // disconnectDevice: () => { + // throw new Error("not implemented"); + // }, + // watchConnectionChange: () => { + // throw new Error("not implemented"); + // }, + // listFiles: () => { + // throw new Error("not implemented"); + // }, + // readTextBlock: () => { + // throw new Error("not implemented"); + // }, + // readFile: () => { + // throw new Error("not implemented"); + // }, + // readStorageFile: () => { + // throw new Error("not implemented"); + // }, + // writeFile: () => { + // throw new Error("not implemented"); + // }, + // handlers: () => { + // throw new Error("not implemented"); + // }, + // on: () => { + // throw new Error("not implemented"); + // }, +}; + + +declare const Comms: { + uploadApp: (app: AppItem, options: { + /** + * object of translations, eg 'lang/de_DE.json' + */ + language?: { + /** + * Translations that apply for all apps + */ + GLOBAL: undefined | { + [key: string]: string + } + /** + * App-specific overrides + */ + [appId: string]: undefined | { + [key: string]: string + } + }; + /** + * { id : ..., version : ... } info about the currently connected device + */ + device: { + id: string; + appsInstalled: InstalledApp[]; + version: string; + exptr: number; + }; + /** + * if true, showUploadFinished isn't called (displaying the reboot message) + * @default false + */ + noFinish?: boolean; + /** + * if true, don't reset the device before + * + * @default false + * + * reset to ensure we have enough memory to upload what we need to + */ + noReset?: boolean; + }) => Promise; + getDeviceInfo: (noReset?: boolean) => Promise<{ + apps: InstalledApp[]; + /** + * @example 1708008913000 + */ + currentTime: number; + /** + * @example 495324 + */ + exptr: number; + /** + * @example "BANGLEJS2" + */ + id: string; + /** + * @example 3533326687 + */ + uid: number; + /** + * @example "2v21" + */ + version: string; + }> + isConnected: () => boolean; +}; + +interface InstalledApp { + /** + * @example "synthwave.info,synthwave.app.js,synthwave.img" + */ + files: string; + /** + * @example "synthwave" + */ + id: string; + /** + * @example "clock" + */ + type: string; + /** + * @example "0.01" + */ + version: string; +} \ No newline at end of file diff --git a/src/services/GlobalProgress.tsx b/src/services/GlobalProgress.tsx new file mode 100644 index 0000000..1e4e4ed --- /dev/null +++ b/src/services/GlobalProgress.tsx @@ -0,0 +1,49 @@ +import toast, { Toaster } from 'react-hot-toast'; + +interface Progress { + show: ( + options: { + title: string; + domElement?: never; + sticky?: boolean; + interval?: number; + percent?: number; + min?: number; + max?: number; + } + ) => void; + hide: ( + options: { + sticky?: boolean, + } + ) => void; +} + +const augmentedWindow = window as unknown as { Progress: Progress }; +const GLOBAL_PROGRESS_ID = "GLOBAL_PROGRESS_ID"; + +augmentedWindow.Progress = Object.defineProperties(Object.create(null), { + show: { + get: (): Progress["show"] => (options) => { + console.log("showing toast"); + toast(options.title, { + id: GLOBAL_PROGRESS_ID, + position: "bottom-left", + }) + }, + configurable: false, + }, + hide: { + get: (): Progress["hide"] => (options) => { + console.log("hiding toast"); + if (options.sticky) { + toast.dismiss(GLOBAL_PROGRESS_ID); + } + }, + configurable: false, + }, +}); + +export const GlobalProgressToaster = () => { + return +}; diff --git a/yarn.lock b/yarn.lock index 3bbaacd..4ff7630 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1421,6 +1421,11 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +goober@^2.1.10: + version "2.1.14" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.14.tgz#4a5c94fc34dc086a8e6035360ae1800005135acd" + integrity sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg== + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -2385,6 +2390,13 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-hot-toast@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994" + integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== + dependencies: + goober "^2.1.10" + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"