diff --git a/.gitignore b/.gitignore index 448d7cb1..b44f41b3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ Thumbs.db .classpath *.launch .settings/ +dev-dist/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..aaf3357d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "semi": false, + "trailingComma": "all" +} diff --git a/bun.lockb b/bun.lockb index 0a097f48..af3ef1cd 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/index.html b/index.html index e4ce9c73..9ff0aadb 100644 --- a/index.html +++ b/index.html @@ -1,25 +1,23 @@ - + - - - + connect - - - + + + + + + + + + - +
diff --git a/package.json b/package.json index 39ed0974..af6d446d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,11 @@ "type": "module", "devDependencies": { "@solidjs/testing-library": "^0.8.8", + "@vite-pwa/assets-generator": "^0.2.4", + "vite-plugin-pwa": "^0.20.0", + "sharp": "0.32.6", + "sharp-ico": "0.1.5", + "workbox-window": "^7.1.0", "@stylistic/eslint-plugin": "^2.1.0", "@testing-library/jest-dom": "^6.4.6", "@testing-library/user-event": "^14.5.2", diff --git a/public/images/logo-connect-dark.svg b/public/logo-connect-dark.svg similarity index 100% rename from public/images/logo-connect-dark.svg rename to public/logo-connect-dark.svg diff --git a/public/images/logo-connect-light.svg b/public/logo-connect-light.svg similarity index 100% rename from public/images/logo-connect-light.svg rename to public/logo-connect-light.svg diff --git a/pwa-assets.config.ts b/pwa-assets.config.ts new file mode 100644 index 00000000..388032fa --- /dev/null +++ b/pwa-assets.config.ts @@ -0,0 +1,31 @@ +import { + AllAppleDeviceNames, + combinePresetAndAppleSplashScreens, + defineConfig, + minimal2023Preset, +} from '@vite-pwa/assets-generator/config' + +import { readFile } from 'node:fs/promises' + +export default defineConfig({ + headLinkOptions: { + preset: '2023', + }, + preset: combinePresetAndAppleSplashScreens( + minimal2023Preset, + { + // dark splash screens using black background (the default) + darkResizeOptions: { background: 'black', fit: 'contain' }, + // or using a custom background color + // darkResizeOptions: { background: '#1f1f1f' }, + async darkImageResolver(imageName) { + return imageName === 'public/logo-connect-light.svg' + ? await readFile('public/logo-connect-dark.svg') + : undefined + }, + }, + + AllAppleDeviceNames, + ), + images: ['public/logo-connect-light.svg'], +}) diff --git a/src/App.tsx b/src/App.tsx index 24bce32f..dec08311 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ +import { Route, Router } from '@solidjs/router' import { Suspense, lazy, type VoidComponent } from 'solid-js' -import { Router, Route } from '@solidjs/router' +import OfflineIndicator from './components/OfflineIndicator' const Login = lazy(() => import('./pages/auth/login')) const Logout = lazy(() => import('./pages/auth/logout')) @@ -9,13 +10,14 @@ const Dashboard = lazy(() => import('./pages/dashboard')) const App: VoidComponent = () => { return ( - {props.children}}> - - - - - - + + {props.children}}> + + + + + + ) } diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx new file mode 100644 index 00000000..038e6999 --- /dev/null +++ b/src/components/OfflineIndicator.tsx @@ -0,0 +1,31 @@ +import { ParentProps, createSignal, onCleanup } from 'solid-js' + +const OfflineIndicator = (props: ParentProps) => { + const [isOffline, setIsOffline] = createSignal(!navigator.onLine) + + const updateOnlineStatus = () => { + setIsOffline(!navigator.onLine) + } + + window.addEventListener('online', updateOnlineStatus) + window.addEventListener('offline', updateOnlineStatus) + + onCleanup(() => { + window.removeEventListener('online', updateOnlineStatus) + window.removeEventListener('offline', updateOnlineStatus) + }) + + return ( +
+ {isOffline() ? ( +
+ You are currently offline +
+ ) : ( + props.children + )} +
+ ) +} + +export default OfflineIndicator diff --git a/src/pages/dashboard/Dashboard.tsx b/src/pages/dashboard/Dashboard.tsx index 1fd638f4..0b18cf35 100644 --- a/src/pages/dashboard/Dashboard.tsx +++ b/src/pages/dashboard/Dashboard.tsx @@ -1,15 +1,15 @@ +import { Navigate, useLocation, type RouteSectionProps } from '@solidjs/router' +import type { Component } from 'solid-js' import { Accessor, - createContext, - createResource, - createSignal, Match, Setter, Show, Switch, + createContext, + createResource, + createSignal, } from 'solid-js' -import type { Component } from 'solid-js' -import { Navigate, type RouteSectionProps, useLocation } from '@solidjs/router' import { getDevices } from '~/api/devices' import { getProfile } from '~/api/profile' @@ -20,10 +20,10 @@ import Drawer from '~/components/material/Drawer' import IconButton from '~/components/material/IconButton' import TopAppBar from '~/components/material/TopAppBar' -import DeviceList from './components/DeviceList' +import storage from '~/utils/storage' import DeviceActivity from './activities/DeviceActivity' import RouteActivity from './activities/RouteActivity' -import storage from '~/utils/storage' +import DeviceList from './components/DeviceList' type DashboardState = { drawer: Accessor @@ -45,15 +45,15 @@ const DashboardDrawer = (props: { > comma connect -

- Devices -

+

Devices

{(devices: Device[]) => }

- + ) } @@ -78,7 +78,8 @@ const DashboardLayout: Component = () => { if (dongleId()) return undefined const lastSelectedDongleId = storage.getItem('lastSelectedDongleId') - if (devices()?.some((device) => device.dongle_id === lastSelectedDongleId)) return lastSelectedDongleId + if (devices()?.some((device) => device.dongle_id === lastSelectedDongleId)) + return lastSelectedDongleId return devices()?.[0]?.dongle_id } @@ -108,9 +109,8 @@ const DashboardLayout: Component = () => { - {(defaultDongleId) => ( - - )} + + {(defaultDongleId) => } diff --git a/tsconfig.json b/tsconfig.json index 055cdb11..1d633ff4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,11 @@ "allowJs": true, "noEmit": true, "strict": true, - "types": ["vite/client", "@testing-library/jest-dom"], + "types": [ + "vite/client", + "@testing-library/jest-dom", + "vite-plugin-pwa/client" + ], "isolatedModules": true, "paths": { "~/*": ["./src/*"] diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..3a48e56b --- /dev/null +++ b/vercel.json @@ -0,0 +1,3 @@ +{ + "rewrites": [{ "source": "/(.*)", "destination": "/" }] +} diff --git a/vite.config.ts b/vite.config.ts index b4322217..18e25e64 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,7 @@ +import devtools from 'solid-devtools/vite' import { defineConfig } from 'vite' +import { VitePWA } from 'vite-plugin-pwa' import solid from 'vite-plugin-solid' -import devtools from 'solid-devtools/vite' export default defineConfig({ plugins: [ @@ -8,6 +9,36 @@ export default defineConfig({ solid({ ssr: false, }), + VitePWA({ + registerType: 'autoUpdate', + injectRegister: 'auto', + + pwaAssets: { + disabled: false, + config: true, + }, + + manifest: { + name: 'connect', + short_name: 'conect', + description: 'manage your openpilot experience', + theme_color: '#1B1B1F', + background_color: '#1B1B1F', + }, + + workbox: { + globPatterns: ['**/*.{js,css,html,svg,png,ico}'], + cleanupOutdatedCaches: true, + clientsClaim: true, + }, + + devOptions: { + enabled: true, + navigateFallback: 'index.html', + suppressWarnings: true, + type: 'module', + }, + }), ], server: { port: 3000,