Skip to content

Commit 9fb6f84

Browse files
configure: fixes + offline & persistent storage warning
1 parent b9daf0d commit 9fb6f84

File tree

15 files changed

+291
-104
lines changed

15 files changed

+291
-104
lines changed

apps/configure/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dependencies": {
1212
"@kobalte/core": "^0.13.3",
1313
"@mattrax/ui": "workspace:*",
14+
"@solid-primitives/connectivity": "^0.3.20",
1415
"@solid-primitives/context": "^0.2.3",
1516
"@solid-primitives/event-listener": "^2.3.3",
1617
"@solid-primitives/memo": "^1.3.9",

apps/configure/src/components/search/FilterBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function FilterBar(props: ReturnType<typeof createSearchPageContext>) {
4747
description: "My cool view",
4848
data,
4949
});
50-
navigate(`/views/${id}`);
50+
navigate(`../views/${id}`);
5151
},
5252
}));
5353

apps/configure/src/components/search/configuration.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file contains the configuration for data fetching, filtering, ordering, and rendering.
22

33
import { Badge } from "@mattrax/ui";
4+
import { A } from "@solidjs/router";
45
import { type Entity, defineEntity } from "./filters";
56

67
const typeColumn = (type: string) =>
@@ -20,12 +21,12 @@ export const entities = {
2021
name: {
2122
header: "Name",
2223
render: (user) => (
23-
<a
24+
<A
2425
class="font-medium hover:underline focus:underline p-1 -m-1 w-full block"
25-
href={`/users/${user.id}`}
26+
href={`../users/${user.id}`}
2627
>
2728
{user.name}
28-
</a>
29+
</A>
2930
),
3031
raw: (user) => user.name,
3132
},
@@ -99,12 +100,12 @@ export const entities = {
99100
name: {
100101
header: "Name",
101102
render: (device) => (
102-
<a
103+
<A
103104
class="font-medium hover:underline focus:underline p-1 -m-1 w-full block"
104-
href={`/devices/${device.id}`}
105+
href={`../devices/${device.id}`}
105106
>
106107
{device.name}
107-
</a>
108+
</A>
108109
),
109110
raw: (device) => device.name,
110111
},
@@ -169,12 +170,12 @@ export const entities = {
169170
name: {
170171
header: "Name",
171172
render: (group) => (
172-
<a
173+
<A
173174
class="font-medium hover:underline focus:underline p-1 -m-1 w-full block"
174-
href={`/groups/${group.id}`}
175+
href={`../groups/${group.id}`}
175176
>
176177
{group.name}
177-
</a>
178+
</A>
178179
),
179180
raw: (group) => group.name,
180181
},
@@ -200,12 +201,12 @@ export const entities = {
200201
name: {
201202
header: "Name",
202203
render: (policy) => (
203-
<a
204+
<A
204205
class="font-medium hover:underline focus:underline p-1 -m-1 w-full block"
205-
href={`/policies/${policy.id}`}
206+
href={`../policies/${policy.id}`}
206207
>
207208
{policy.name}
208-
</a>
209+
</A>
209210
),
210211
raw: (policy) => policy.name,
211212
},
@@ -231,12 +232,12 @@ export const entities = {
231232
name: {
232233
header: "Name",
233234
render: (app) => (
234-
<a
235+
<A
235236
class="font-medium hover:underline focus:underline p-1 -m-1 w-full block"
236-
href={`/applications/${app.id}`}
237+
href={`../applications/${app.id}`}
237238
>
238239
{app.name}
239-
</a>
240+
</A>
240241
),
241242
raw: (app) => app.name,
242243
},

apps/configure/src/lib/createTimer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export function createTimer2(
1818
});
1919
};
2020

21+
timer = setTimeout(trigger, untrack(timeout));
22+
2123
onCleanup(() => {
2224
if (timer) clearTimeout(timer);
2325
});

apps/configure/src/lib/db.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,13 @@ export const dbVersion = 1;
302302
export const openAndInitDb = (name: string, createIfNotFound = false) =>
303303
openDB<Database>(name, dbVersion, {
304304
upgrade(db, oldVersion, newVersion, tx) {
305-
// Aborting causes the DB creation to fail
306-
if (oldVersion === 0 && !createIfNotFound) return tx.abort();
305+
// Aborting causes the DB creation to fail (in only Chrome *sigh*)
306+
if (oldVersion === 0 && !createIfNotFound) {
307+
if ("chrome" in window) return tx.abort();
308+
db.close();
309+
window.indexedDB.deleteDatabase(name);
310+
return;
311+
}
307312

308313
db.createObjectStore("_kv");
309314
db.createObjectStore("_meta");

apps/configure/src/lib/query.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ function createDbObserver(
1717
db: IDBPDatabase<Database>,
1818
onChange: (objectStore: string) => void,
1919
) {
20+
const stores = [...db.objectStoreNames];
21+
if (stores.length === 0) return;
22+
2023
// @ts-expect-error // TODO: Typescript support for IndexedDB Observers polyfill
2124
const observer = db.observe(
2225
// This subscribes to all object stores.
@@ -28,8 +31,10 @@ function createDbObserver(
2831
onChange(metadata.objectStoreName);
2932
},
3033
);
31-
32-
onCleanup(() => observer.stop());
34+
if (!observer) console.error("Error registering observer!");
35+
onCleanup(() => {
36+
if (observer) observer.stop();
37+
});
3338
}
3439

3540
// create a query that will automatically rerun when the database changes

apps/configure/src/lib/sync/index.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { IDBPDatabase } from "idb";
33
import { toast } from "solid-sonner";
44
import type { Database } from "../db";
55
import { deleteKey, getKey } from "../kv";
6+
import { didLastSyncCompleteSuccessfully } from "./operation";
67
import * as schema from "./schema";
78

89
export type SyncEngine = ReturnType<typeof initSync>;
@@ -16,6 +17,16 @@ export function initSync(db: IDBPDatabase<Database>) {
1617
return {
1718
db,
1819
async syncAll(abort: AbortController): Promise<string | undefined> {
20+
if (abort.signal.aborted) return;
21+
if (!navigator.onLine) {
22+
console.warn("Sync cancelled due to navigator being offline!");
23+
return;
24+
}
25+
26+
const isSyncLockAlreadyHeld = (await navigator.locks.query()).held?.find(
27+
(lock) => lock.name === "sync",
28+
);
29+
1930
// Be aware the way use this lock intentionally queues up syncs.
2031
// Eg. if two tabs hit this code path, one will sync and then the other will sync (both triggering a full sync).
2132
//
@@ -25,7 +36,13 @@ export function initSync(db: IDBPDatabase<Database>) {
2536
// To mitigate the impacts of this we should // TODO
2637
const result = await navigator.locks.request("sync", async (lock) => {
2738
if (!lock) return;
28-
if (abort.signal.aborted) return;
39+
40+
// The sync was queued while a sync was already in progress but it succeeded so we can skip it.
41+
if (
42+
abort.signal.aborted ||
43+
(isSyncLockAlreadyHeld && (await didLastSyncCompleteSuccessfully(db)))
44+
)
45+
return;
2946

3047
const accessToken = await getKey(db, "accessToken");
3148
if (!accessToken) {

apps/configure/src/lib/sync/operation.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,10 @@ export async function resetSyncState(db: IDBPDatabase<Database>) {
154154
await db.clear("_meta");
155155
});
156156
}
157+
158+
export async function didLastSyncCompleteSuccessfully(
159+
db: IDBPDatabase<Database>,
160+
) {
161+
const meta = await db.getAll("_meta");
162+
return meta.every((m) => "syncedAt" in m);
163+
}

apps/configure/src/lib/sync/schema.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const orgSchema = z.object({
8383
),
8484
assignedPlans: z.array(
8585
z.object({
86-
assignedDateTime: z.string(),
86+
assignedDateTime: z.string().datetime(),
8787
capabilityStatus: z.enum([
8888
"Enabled",
8989
"Warning",
@@ -205,8 +205,8 @@ export const users = defineSyncEntity("users", {
205205
businessPhones: z.array(z.string()).optional(),
206206
mobilePhone: z.string().optional(),
207207
preferredLanguage: z.string().optional(),
208-
lastPasswordChangeDateTime: z.string().optional(),
209-
createdDateTime: z.string(),
208+
lastPasswordChangeDateTime: z.string().datetime().optional(),
209+
createdDateTime: z.string().datetime(),
210210
}),
211211
upsert: async (db, data, _syncId) => {
212212
const tx = db.transaction("users", "readwrite");
@@ -311,8 +311,8 @@ export const devices = defineSyncEntity("devices", {
311311
model: z.string().optional(),
312312
operatingSystem: z.string().optional(),
313313
operatingSystemVersion: z.string().optional(),
314-
approximateLastSignInDateTime: z.string().optional(),
315-
registrationDateTime: z.string().optional(),
314+
approximateLastSignInDateTime: z.string().datetime().optional(),
315+
registrationDateTime: z.string().datetime().optional(),
316316
deviceCategory: z.string().optional(),
317317
}),
318318
upsert: async (db, data, _syncId) => {
@@ -366,7 +366,7 @@ export const groups = defineSyncEntity("groups", {
366366
description: z.string().optional(),
367367
securityEnabled: z.boolean().default(false),
368368
visibility: z.enum(["Private", "Public", "HiddenMembership"]).nullable(),
369-
createdDateTime: z.string(),
369+
createdDateTime: z.string().datetime(),
370370
"members@delta": z
371371
.array(
372372
z.object({
@@ -477,8 +477,8 @@ export const policies = defineSyncEntity("policies", {
477477
.nullable()
478478
// Microsoft's endpoints are inconsistent.
479479
.transform((v) => (v === "" ? null : v)),
480-
createdDateTime: z.string(),
481-
lastModifiedDateTime: z.string(),
480+
createdDateTime: z.string().datetime(),
481+
lastModifiedDateTime: z.string().datetime(),
482482
assignments: z.array(
483483
z.object({
484484
id: z.string(),
@@ -585,8 +585,8 @@ export const scripts = defineSyncEntity("scripts", {
585585
.nullable()
586586
// Microsoft's endpoints are cringe.
587587
.transform((v) => (v === "" ? null : v)),
588-
createdDateTime: z.string(),
589-
lastModifiedDateTime: z.string(),
588+
createdDateTime: z.string().datetime(),
589+
lastModifiedDateTime: z.string().datetime(),
590590
runAsAccount: z.enum(["system", "user"]),
591591
fileName: z.string(),
592592
scriptContent: z.string().nullable(),
@@ -705,8 +705,8 @@ export const apps = defineSyncEntity("apps", {
705705
description: z.string().optional(),
706706
publisher: z.string().optional(),
707707
largeIcon: z.any().optional(),
708-
createdDateTime: z.string(),
709-
lastModifiedDateTime: z.string(),
708+
createdDateTime: z.string().datetime(),
709+
lastModifiedDateTime: z.string().datetime(),
710710
isFeatured: z.boolean().default(false),
711711
privacyInformationUrl: z.string().nullable(),
712712
informationUrl: z.string().nullable(),

apps/configure/src/routes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const routes = [
1111
component: lazy(() => import("./routes/home")),
1212
},
1313
{
14-
path: "/:userId",
14+
path: "/:uid",
1515
component: lazy(() => import("./routes/(dash)")),
1616
children: [
1717
{
@@ -31,7 +31,7 @@ export const routes = [
3131
component: lazy(() => import("./routes/(dash)/devices/(devices)")),
3232
},
3333
{
34-
path: "/devices/:userId",
34+
path: "/devices/:deviceId",
3535
component: lazy(() => import("./routes/(dash)/devices/[deviceId]")),
3636
},
3737
{

0 commit comments

Comments
 (0)