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,