@auxilium/datalynk-client 1.3.6 โ 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -15
- package/dist/api.d.ts.map +1 -1
- package/dist/banner.d.ts +8 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/index.cjs +86 -52
- package/dist/index.mjs +86 -52
- package/dist/pwa.d.ts +2 -1
- package/dist/pwa.d.ts.map +1 -1
- package/dist/service.worker.mjs +128 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -264,32 +264,73 @@ const user = await api.auth.login('spoke', 'username', 'password', '2faCode');
|
|
|
264
264
|
<h3 id="pwa" style="display: inline">Offline / PWA</h3>
|
|
265
265
|
</summary>
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
### Support
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
#### PWA
|
|
270
|
+
Turns the website into an installable native app
|
|
271
|
+
- The PWA manifest can be configured with: `manifest`
|
|
272
|
+
- The install prompt can be configured with: `pwaSettings`
|
|
273
|
+
```ts
|
|
274
|
+
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
275
|
+
name: "Workplace Occupational Health & Safety Inspection", // REQUIRED: App name
|
|
276
|
+
manifest: { // Set/Override any manifest values
|
|
277
|
+
scope: 'https://lynk-forms.scarborough.auxilium.world/OshawaCL/ohsinspection.html'
|
|
278
|
+
},
|
|
279
|
+
pwaSettings: { // PWA install prompt settings
|
|
280
|
+
timeout: 45, // seconds before prompt shows (default: 30)
|
|
281
|
+
dismissExpiry: 3, // days before prompt shows again (0 = every refresh, default: 7)
|
|
282
|
+
loginLink: true // Show install link in login screen
|
|
283
|
+
},
|
|
284
|
+
});
|
|
270
285
|
|
|
271
|
-
|
|
286
|
+
// Manually trigger
|
|
287
|
+
api.pwa.prompt();
|
|
288
|
+
```
|
|
272
289
|
|
|
273
|
-
|
|
290
|
+
#### Static assets
|
|
291
|
+
Upload the [service worker](https://datalynk-client.scarborough.auxilium.world/dist/service.worker.mjs) `dist/service.worker.mjs` to the *root* directory of the server
|
|
292
|
+
to cache all files for use offline
|
|
293
|
+
```ts
|
|
294
|
+
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
295
|
+
serviceWorker: 'https://.../service.worker.mjs', // Shouldnt be needed but can be overriden
|
|
296
|
+
});
|
|
297
|
+
```
|
|
274
298
|
|
|
275
|
-
|
|
299
|
+
#### Slice Engine
|
|
300
|
+
Slices can be used as normal offline using the slice engine by marking the slice as offline with: `offline`
|
|
276
301
|
```ts
|
|
277
302
|
const api = new Api(`https://${spoke}.auxiliumgroup.com`, {
|
|
278
|
-
name: "Workplace Occupational Health & Safety Inspection", // REQUIRED: App name
|
|
279
303
|
offline: [51306, 51531, 51563], // REQUIRED: Specify slices that must be stored offline
|
|
280
|
-
manifest: { scope: 'https://lynk-forms.scarborough.auxilium.world/OshawaCL/ohsinspection.html' }, // Any manifest overrides suck as PWA "scope"
|
|
281
|
-
pwaSettings: { // PWA install prompt settings
|
|
282
|
-
timeout: 45, // seconds before prompt shows (default: 30)
|
|
283
|
-
dismissExpiry: 3, // days before prompt shows again (0 = every refresh, default: 7)
|
|
284
|
-
loginLink: true // Show install link in login screen
|
|
285
|
-
},
|
|
286
|
-
serviceWorker: 'https://.../service.worker.mjs', // Override service worker URL, must be on same domain (Step 1)
|
|
287
304
|
socket: true // Make sure sockets are enabled
|
|
288
305
|
});
|
|
306
|
+
await api.slice(51306).insert({...}).exec();
|
|
307
|
+
const resp = await api.slice(51306).select().where('id', '<=', 100).exec();
|
|
308
|
+
```
|
|
309
|
+
#### API Requests
|
|
310
|
+
Any API request can be deferred until online, however you dont get responses when offline
|
|
311
|
+
- Only useful for submitting data or when we dont care about the response
|
|
312
|
+
```ts
|
|
313
|
+
api.request(..., {offline: true}).then((resp: T | void) => {
|
|
314
|
+
if(resp != null) {
|
|
315
|
+
// Online logic (has response payload)
|
|
316
|
+
} else {
|
|
317
|
+
// Offline logic (no response payload)
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
</details>
|
|
289
322
|
|
|
290
|
-
|
|
291
|
-
|
|
323
|
+
<details>
|
|
324
|
+
<summary>
|
|
325
|
+
<h3 id="pdf" style="display: inline">PDFs</h3>
|
|
326
|
+
</summary>
|
|
327
|
+
|
|
328
|
+
PDFs can be generated using the API. PDFs can be optionally related to a record by passing an association
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const resp = await api.pdf.fromUrl('https://google.com', {slice: 12345, row: 1, field: 'resume'});
|
|
292
332
|
```
|
|
333
|
+
|
|
293
334
|
</details>
|
|
294
335
|
|
|
295
336
|
<details>
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,QAAQ,EAAQ,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAC,eAAe,EAAuB,MAAM,MAAM,CAAC;AAC3D,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type BannerOptions = {
|
|
2
|
+
color?: string;
|
|
3
|
+
position?: 'top' | 'bottom';
|
|
4
|
+
id?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function createBanner(message: string, options?: BannerOptions): void;
|
|
7
|
+
export declare function removeBanner(id?: string): void;
|
|
8
|
+
//# sourceMappingURL=banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;CACZ,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,QAyBxE;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAoB,QAGlD"}
|
package/dist/index.cjs
CHANGED
|
@@ -1783,13 +1783,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1783
1783
|
pwaLink.addEventListener("click", async (e) => {
|
|
1784
1784
|
var _a;
|
|
1785
1785
|
e.preventDefault();
|
|
1786
|
-
if ((_a = this.api.pwa) == null ? void 0 : _a.
|
|
1787
|
-
this.api.pwa.
|
|
1788
|
-
const choice = await this.api.pwa.
|
|
1786
|
+
if ((_a = this.api.pwa) == null ? void 0 : _a.nativeInstallPrompt) {
|
|
1787
|
+
this.api.pwa.nativeInstallPrompt.prompt();
|
|
1788
|
+
const choice = await this.api.pwa.nativeInstallPrompt.userChoice;
|
|
1789
1789
|
if (choice.outcome === "accepted") {
|
|
1790
1790
|
console.log("PWA installed via login link");
|
|
1791
1791
|
}
|
|
1792
|
-
this.api.pwa.
|
|
1792
|
+
this.api.pwa.nativeInstallPrompt = null;
|
|
1793
1793
|
} else {
|
|
1794
1794
|
this.api.pwa.prompt();
|
|
1795
1795
|
}
|
|
@@ -2309,12 +2309,42 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2309
2309
|
return this.api.request({ "$/auth/mobile/generate": { user: login, method: mode } });
|
|
2310
2310
|
}
|
|
2311
2311
|
}
|
|
2312
|
+
function createBanner(message, options = {}) {
|
|
2313
|
+
options = {
|
|
2314
|
+
color: "#dc3545",
|
|
2315
|
+
position: "top",
|
|
2316
|
+
id: "datalynk-banner",
|
|
2317
|
+
...options
|
|
2318
|
+
};
|
|
2319
|
+
removeBanner(options.id);
|
|
2320
|
+
const banner = document.createElement("div");
|
|
2321
|
+
banner.className = options.id;
|
|
2322
|
+
Object.assign(banner.style, {
|
|
2323
|
+
position: "fixed",
|
|
2324
|
+
[options.position === "top" ? "top" : "bottom"]: "0",
|
|
2325
|
+
left: "0",
|
|
2326
|
+
right: "0",
|
|
2327
|
+
padding: ".75rem",
|
|
2328
|
+
fontSize: "1rem",
|
|
2329
|
+
fontWeight: "bolder",
|
|
2330
|
+
backgroundColor: options.color,
|
|
2331
|
+
color: "#fff",
|
|
2332
|
+
textAlign: "center",
|
|
2333
|
+
zIndex: "9999"
|
|
2334
|
+
});
|
|
2335
|
+
banner.innerHTML = message;
|
|
2336
|
+
document.body.appendChild(banner);
|
|
2337
|
+
}
|
|
2338
|
+
function removeBanner(id = "datalynk-banner") {
|
|
2339
|
+
const banner = document.querySelector(`.${id}`);
|
|
2340
|
+
if (banner) banner.remove();
|
|
2341
|
+
}
|
|
2312
2342
|
class PWA {
|
|
2313
2343
|
constructor(api) {
|
|
2314
|
-
|
|
2344
|
+
/** Capture Install Event */
|
|
2345
|
+
__publicField(this, "nativeInstallPrompt", null);
|
|
2315
2346
|
this.api = api;
|
|
2316
2347
|
}
|
|
2317
|
-
// Holds the beforeinstallprompt event
|
|
2318
2348
|
get headless() {
|
|
2319
2349
|
return typeof window == "undefined";
|
|
2320
2350
|
}
|
|
@@ -2341,34 +2371,36 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2341
2371
|
if (!installBtn) return;
|
|
2342
2372
|
installBtn.onclick = async () => {
|
|
2343
2373
|
var _a;
|
|
2344
|
-
this.
|
|
2345
|
-
|
|
2374
|
+
if (!this.nativeInstallPrompt) return console.warn("PWA is not supported");
|
|
2375
|
+
this.nativeInstallPrompt.prompt();
|
|
2376
|
+
const choice = await this.nativeInstallPrompt.userChoice;
|
|
2346
2377
|
if (choice.outcome === "accepted") {
|
|
2347
2378
|
console.log("PWA installed");
|
|
2348
|
-
localStorage.
|
|
2379
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, "installed");
|
|
2380
|
+
} else {
|
|
2381
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString());
|
|
2349
2382
|
}
|
|
2350
|
-
this.deferredPrompt = null;
|
|
2351
2383
|
(_a = document.querySelector(".pwa-prompt")) == null ? void 0 : _a.remove();
|
|
2352
2384
|
};
|
|
2353
2385
|
}
|
|
2354
2386
|
/** Setup the PWA */
|
|
2355
2387
|
async setup(manifest = {}) {
|
|
2356
|
-
var _a, _b, _c;
|
|
2357
2388
|
window.addEventListener("beforeinstallprompt", (e) => {
|
|
2358
2389
|
e.preventDefault();
|
|
2359
|
-
this.
|
|
2390
|
+
this.nativeInstallPrompt = e;
|
|
2360
2391
|
const actions = document.querySelector(".pwa-prompt-actions");
|
|
2361
2392
|
if (actions && !actions.querySelector("#installPwaBtn")) {
|
|
2362
2393
|
actions.innerHTML = `<button id="installPwaBtn">Install App</button>`;
|
|
2363
2394
|
this.bindInstallButton();
|
|
2364
2395
|
}
|
|
2365
2396
|
});
|
|
2366
|
-
|
|
2367
|
-
timeout:
|
|
2368
|
-
//
|
|
2369
|
-
dismissExpiry:
|
|
2370
|
-
//
|
|
2371
|
-
loginLink:
|
|
2397
|
+
this.api.options.pwaSettings = {
|
|
2398
|
+
timeout: 30,
|
|
2399
|
+
// Seconds
|
|
2400
|
+
dismissExpiry: 7,
|
|
2401
|
+
// Days
|
|
2402
|
+
loginLink: false,
|
|
2403
|
+
...this.api.options.pwaSettings
|
|
2372
2404
|
};
|
|
2373
2405
|
const meta = (key, name, value) => {
|
|
2374
2406
|
const exists = document.querySelector(`meta[${key}="${name}"]`);
|
|
@@ -2402,19 +2434,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2402
2434
|
document.head.append(link);
|
|
2403
2435
|
}
|
|
2404
2436
|
if (!this.headless && !this.iframe && !this.pwa && this.platform != "mac") setTimeout(() => {
|
|
2405
|
-
|
|
2406
|
-
if (
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2411
|
-
}
|
|
2437
|
+
var _a, _b, _c, _d;
|
|
2438
|
+
if (((_b = (_a = this.api.options) == null ? void 0 : _a.pwaSettings) == null ? void 0 : _b.dismissExpiry) === 0) localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2439
|
+
let dismissedDate = localStorage.getItem(`${this.api.options.name}:pwa`);
|
|
2440
|
+
if (dismissedDate) dismissedDate = JSON.parse(dismissedDate);
|
|
2441
|
+
if (!!dismissedDate && Date.now() > dismissedDate + ((_d = (_c = this.api.options) == null ? void 0 : _c.pwaSettings) == null ? void 0 : _d.dismissExpiry) * 6e4 * 60 * 24) return;
|
|
2412
2442
|
this.prompt();
|
|
2413
|
-
},
|
|
2443
|
+
}, this.api.options.pwaSettings.timeout);
|
|
2414
2444
|
}
|
|
2415
2445
|
/** Prompt user to install the app */
|
|
2416
2446
|
async prompt(platform) {
|
|
2417
|
-
var _a;
|
|
2418
2447
|
if (document.querySelector(".pwa-prompt")) return;
|
|
2419
2448
|
const android = (platform || this.platform) == "android";
|
|
2420
2449
|
let style = document.querySelector("style.pwa");
|
|
@@ -2509,10 +2538,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2509
2538
|
<button class="pwa-prompt-close">×</button>
|
|
2510
2539
|
</div>
|
|
2511
2540
|
<div class="pwa-prompt-body">
|
|
2512
|
-
${this.
|
|
2541
|
+
${this.nativeInstallPrompt ? "Click below to install the app directly to your device." : "Add this app to your home screen for a faster, fullscreen experience."}
|
|
2513
2542
|
</div>
|
|
2514
2543
|
<div class="pwa-prompt-actions">
|
|
2515
|
-
${this.
|
|
2544
|
+
${this.nativeInstallPrompt ? `<button id="installPwaBtn">Install App</button>` : `
|
|
2516
2545
|
<table>
|
|
2517
2546
|
<tr>
|
|
2518
2547
|
<td style="width:40px;text-align:center">
|
|
@@ -2531,13 +2560,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2531
2560
|
</div>
|
|
2532
2561
|
`;
|
|
2533
2562
|
const closeBtn = prompt.querySelector(".pwa-prompt-close");
|
|
2534
|
-
const dismissExpiry = ((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ?? 7;
|
|
2535
2563
|
closeBtn.onclick = () => {
|
|
2536
|
-
|
|
2564
|
+
var _a;
|
|
2565
|
+
!!((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ? localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString()) : localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2537
2566
|
prompt.remove();
|
|
2538
2567
|
};
|
|
2539
2568
|
document.body.append(prompt);
|
|
2540
|
-
if (this.
|
|
2569
|
+
if (this.nativeInstallPrompt) this.bindInstallButton();
|
|
2541
2570
|
}
|
|
2542
2571
|
}
|
|
2543
2572
|
class Files {
|
|
@@ -3471,7 +3500,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3471
3500
|
} });
|
|
3472
3501
|
}
|
|
3473
3502
|
}
|
|
3474
|
-
const version = "1.3.
|
|
3503
|
+
const version = "1.3.8";
|
|
3475
3504
|
class WebRtc {
|
|
3476
3505
|
constructor(api) {
|
|
3477
3506
|
__publicField(this, "ice");
|
|
@@ -3681,17 +3710,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3681
3710
|
this.pwa = new PWA(this);
|
|
3682
3711
|
this.superuser = new Superuser(this);
|
|
3683
3712
|
this.webrtc = new WebRtc(this);
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3713
|
+
if (typeof indexedDB != "undefined") {
|
|
3714
|
+
this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
|
|
3715
|
+
this.online$.subscribe(async (online) => {
|
|
3716
|
+
var _a2;
|
|
3717
|
+
if (!online) return;
|
|
3718
|
+
const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
|
|
3719
|
+
const keys = await (table == null ? void 0 : table.getAllKeys());
|
|
3720
|
+
await Promise.allSettled(keys.map(async (k) => {
|
|
3721
|
+
const r = await (table == null ? void 0 : table.get(k));
|
|
3722
|
+
await this.request(r).then(() => table == null ? void 0 : table.delete(k));
|
|
3723
|
+
}));
|
|
3724
|
+
});
|
|
3725
|
+
}
|
|
3695
3726
|
if (typeof window !== "undefined") {
|
|
3696
3727
|
const handleOffline = (state = this.offline) => {
|
|
3697
3728
|
if (this.online != state) this.online$.next(state);
|
|
@@ -3777,6 +3808,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3777
3808
|
}).then(async (resp) => {
|
|
3778
3809
|
const warning = resp.headers["X-Warning"];
|
|
3779
3810
|
if (warning) console.warn(warning);
|
|
3811
|
+
const banner = resp.headers["X-User-Notice"];
|
|
3812
|
+
if (warning) {
|
|
3813
|
+
createBanner(banner);
|
|
3814
|
+
setTimeout(() => removeBanner(), 1e4);
|
|
3815
|
+
}
|
|
3780
3816
|
this.online = true;
|
|
3781
3817
|
let data = JSONAttemptParse(await resp.text());
|
|
3782
3818
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
@@ -3803,15 +3839,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3803
3839
|
}
|
|
3804
3840
|
offlineBanner() {
|
|
3805
3841
|
if (this.options.offlineBanner === false || typeof document == "undefined") return;
|
|
3806
|
-
const banner = document.querySelector(".datalynk-offline-banner");
|
|
3807
3842
|
if (this.online) {
|
|
3808
|
-
banner
|
|
3809
|
-
} else
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
document.body.appendChild(b);
|
|
3843
|
+
removeBanner("datalynk-offline-banner");
|
|
3844
|
+
} else {
|
|
3845
|
+
createBanner("โ ๏ธ You are offline, please reconnect to sync changes", {
|
|
3846
|
+
id: "datalynk-offline-banner",
|
|
3847
|
+
position: this.options.offlineBanner === "top" ? "top" : "bottom"
|
|
3848
|
+
});
|
|
3815
3849
|
}
|
|
3816
3850
|
}
|
|
3817
3851
|
startHeartbeat() {
|
package/dist/index.mjs
CHANGED
|
@@ -1779,13 +1779,13 @@ const _LoginPrompt = class _LoginPrompt {
|
|
|
1779
1779
|
pwaLink.addEventListener("click", async (e) => {
|
|
1780
1780
|
var _a;
|
|
1781
1781
|
e.preventDefault();
|
|
1782
|
-
if ((_a = this.api.pwa) == null ? void 0 : _a.
|
|
1783
|
-
this.api.pwa.
|
|
1784
|
-
const choice = await this.api.pwa.
|
|
1782
|
+
if ((_a = this.api.pwa) == null ? void 0 : _a.nativeInstallPrompt) {
|
|
1783
|
+
this.api.pwa.nativeInstallPrompt.prompt();
|
|
1784
|
+
const choice = await this.api.pwa.nativeInstallPrompt.userChoice;
|
|
1785
1785
|
if (choice.outcome === "accepted") {
|
|
1786
1786
|
console.log("PWA installed via login link");
|
|
1787
1787
|
}
|
|
1788
|
-
this.api.pwa.
|
|
1788
|
+
this.api.pwa.nativeInstallPrompt = null;
|
|
1789
1789
|
} else {
|
|
1790
1790
|
this.api.pwa.prompt();
|
|
1791
1791
|
}
|
|
@@ -2305,12 +2305,42 @@ class Auth {
|
|
|
2305
2305
|
return this.api.request({ "$/auth/mobile/generate": { user: login, method: mode } });
|
|
2306
2306
|
}
|
|
2307
2307
|
}
|
|
2308
|
+
function createBanner(message, options = {}) {
|
|
2309
|
+
options = {
|
|
2310
|
+
color: "#dc3545",
|
|
2311
|
+
position: "top",
|
|
2312
|
+
id: "datalynk-banner",
|
|
2313
|
+
...options
|
|
2314
|
+
};
|
|
2315
|
+
removeBanner(options.id);
|
|
2316
|
+
const banner = document.createElement("div");
|
|
2317
|
+
banner.className = options.id;
|
|
2318
|
+
Object.assign(banner.style, {
|
|
2319
|
+
position: "fixed",
|
|
2320
|
+
[options.position === "top" ? "top" : "bottom"]: "0",
|
|
2321
|
+
left: "0",
|
|
2322
|
+
right: "0",
|
|
2323
|
+
padding: ".75rem",
|
|
2324
|
+
fontSize: "1rem",
|
|
2325
|
+
fontWeight: "bolder",
|
|
2326
|
+
backgroundColor: options.color,
|
|
2327
|
+
color: "#fff",
|
|
2328
|
+
textAlign: "center",
|
|
2329
|
+
zIndex: "9999"
|
|
2330
|
+
});
|
|
2331
|
+
banner.innerHTML = message;
|
|
2332
|
+
document.body.appendChild(banner);
|
|
2333
|
+
}
|
|
2334
|
+
function removeBanner(id = "datalynk-banner") {
|
|
2335
|
+
const banner = document.querySelector(`.${id}`);
|
|
2336
|
+
if (banner) banner.remove();
|
|
2337
|
+
}
|
|
2308
2338
|
class PWA {
|
|
2309
2339
|
constructor(api) {
|
|
2310
|
-
|
|
2340
|
+
/** Capture Install Event */
|
|
2341
|
+
__publicField(this, "nativeInstallPrompt", null);
|
|
2311
2342
|
this.api = api;
|
|
2312
2343
|
}
|
|
2313
|
-
// Holds the beforeinstallprompt event
|
|
2314
2344
|
get headless() {
|
|
2315
2345
|
return typeof window == "undefined";
|
|
2316
2346
|
}
|
|
@@ -2337,34 +2367,36 @@ class PWA {
|
|
|
2337
2367
|
if (!installBtn) return;
|
|
2338
2368
|
installBtn.onclick = async () => {
|
|
2339
2369
|
var _a;
|
|
2340
|
-
this.
|
|
2341
|
-
|
|
2370
|
+
if (!this.nativeInstallPrompt) return console.warn("PWA is not supported");
|
|
2371
|
+
this.nativeInstallPrompt.prompt();
|
|
2372
|
+
const choice = await this.nativeInstallPrompt.userChoice;
|
|
2342
2373
|
if (choice.outcome === "accepted") {
|
|
2343
2374
|
console.log("PWA installed");
|
|
2344
|
-
localStorage.
|
|
2375
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, "installed");
|
|
2376
|
+
} else {
|
|
2377
|
+
localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString());
|
|
2345
2378
|
}
|
|
2346
|
-
this.deferredPrompt = null;
|
|
2347
2379
|
(_a = document.querySelector(".pwa-prompt")) == null ? void 0 : _a.remove();
|
|
2348
2380
|
};
|
|
2349
2381
|
}
|
|
2350
2382
|
/** Setup the PWA */
|
|
2351
2383
|
async setup(manifest = {}) {
|
|
2352
|
-
var _a, _b, _c;
|
|
2353
2384
|
window.addEventListener("beforeinstallprompt", (e) => {
|
|
2354
2385
|
e.preventDefault();
|
|
2355
|
-
this.
|
|
2386
|
+
this.nativeInstallPrompt = e;
|
|
2356
2387
|
const actions = document.querySelector(".pwa-prompt-actions");
|
|
2357
2388
|
if (actions && !actions.querySelector("#installPwaBtn")) {
|
|
2358
2389
|
actions.innerHTML = `<button id="installPwaBtn">Install App</button>`;
|
|
2359
2390
|
this.bindInstallButton();
|
|
2360
2391
|
}
|
|
2361
2392
|
});
|
|
2362
|
-
|
|
2363
|
-
timeout:
|
|
2364
|
-
//
|
|
2365
|
-
dismissExpiry:
|
|
2366
|
-
//
|
|
2367
|
-
loginLink:
|
|
2393
|
+
this.api.options.pwaSettings = {
|
|
2394
|
+
timeout: 30,
|
|
2395
|
+
// Seconds
|
|
2396
|
+
dismissExpiry: 7,
|
|
2397
|
+
// Days
|
|
2398
|
+
loginLink: false,
|
|
2399
|
+
...this.api.options.pwaSettings
|
|
2368
2400
|
};
|
|
2369
2401
|
const meta = (key, name, value) => {
|
|
2370
2402
|
const exists = document.querySelector(`meta[${key}="${name}"]`);
|
|
@@ -2398,19 +2430,16 @@ class PWA {
|
|
|
2398
2430
|
document.head.append(link);
|
|
2399
2431
|
}
|
|
2400
2432
|
if (!this.headless && !this.iframe && !this.pwa && this.platform != "mac") setTimeout(() => {
|
|
2401
|
-
|
|
2402
|
-
if (
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2407
|
-
}
|
|
2433
|
+
var _a, _b, _c, _d;
|
|
2434
|
+
if (((_b = (_a = this.api.options) == null ? void 0 : _a.pwaSettings) == null ? void 0 : _b.dismissExpiry) === 0) localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2435
|
+
let dismissedDate = localStorage.getItem(`${this.api.options.name}:pwa`);
|
|
2436
|
+
if (dismissedDate) dismissedDate = JSON.parse(dismissedDate);
|
|
2437
|
+
if (!!dismissedDate && Date.now() > dismissedDate + ((_d = (_c = this.api.options) == null ? void 0 : _c.pwaSettings) == null ? void 0 : _d.dismissExpiry) * 6e4 * 60 * 24) return;
|
|
2408
2438
|
this.prompt();
|
|
2409
|
-
},
|
|
2439
|
+
}, this.api.options.pwaSettings.timeout);
|
|
2410
2440
|
}
|
|
2411
2441
|
/** Prompt user to install the app */
|
|
2412
2442
|
async prompt(platform) {
|
|
2413
|
-
var _a;
|
|
2414
2443
|
if (document.querySelector(".pwa-prompt")) return;
|
|
2415
2444
|
const android = (platform || this.platform) == "android";
|
|
2416
2445
|
let style = document.querySelector("style.pwa");
|
|
@@ -2505,10 +2534,10 @@ class PWA {
|
|
|
2505
2534
|
<button class="pwa-prompt-close">×</button>
|
|
2506
2535
|
</div>
|
|
2507
2536
|
<div class="pwa-prompt-body">
|
|
2508
|
-
${this.
|
|
2537
|
+
${this.nativeInstallPrompt ? "Click below to install the app directly to your device." : "Add this app to your home screen for a faster, fullscreen experience."}
|
|
2509
2538
|
</div>
|
|
2510
2539
|
<div class="pwa-prompt-actions">
|
|
2511
|
-
${this.
|
|
2540
|
+
${this.nativeInstallPrompt ? `<button id="installPwaBtn">Install App</button>` : `
|
|
2512
2541
|
<table>
|
|
2513
2542
|
<tr>
|
|
2514
2543
|
<td style="width:40px;text-align:center">
|
|
@@ -2527,13 +2556,13 @@ class PWA {
|
|
|
2527
2556
|
</div>
|
|
2528
2557
|
`;
|
|
2529
2558
|
const closeBtn = prompt.querySelector(".pwa-prompt-close");
|
|
2530
|
-
const dismissExpiry = ((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ?? 7;
|
|
2531
2559
|
closeBtn.onclick = () => {
|
|
2532
|
-
|
|
2560
|
+
var _a;
|
|
2561
|
+
!!((_a = this.api.options.pwaSettings) == null ? void 0 : _a.dismissExpiry) ? localStorage.setItem(`${this.api.options.name}:pwa`, Date.now().toString()) : localStorage.removeItem(`${this.api.options.name}:pwa`);
|
|
2533
2562
|
prompt.remove();
|
|
2534
2563
|
};
|
|
2535
2564
|
document.body.append(prompt);
|
|
2536
|
-
if (this.
|
|
2565
|
+
if (this.nativeInstallPrompt) this.bindInstallButton();
|
|
2537
2566
|
}
|
|
2538
2567
|
}
|
|
2539
2568
|
class Files {
|
|
@@ -3467,7 +3496,7 @@ class Superuser {
|
|
|
3467
3496
|
} });
|
|
3468
3497
|
}
|
|
3469
3498
|
}
|
|
3470
|
-
const version = "1.3.
|
|
3499
|
+
const version = "1.3.8";
|
|
3471
3500
|
class WebRtc {
|
|
3472
3501
|
constructor(api) {
|
|
3473
3502
|
__publicField(this, "ice");
|
|
@@ -3677,17 +3706,19 @@ const _Api = class _Api {
|
|
|
3677
3706
|
this.pwa = new PWA(this);
|
|
3678
3707
|
this.superuser = new Superuser(this);
|
|
3679
3708
|
this.webrtc = new WebRtc(this);
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3709
|
+
if (typeof indexedDB != "undefined") {
|
|
3710
|
+
this.database = new Database("datalynk", ["pending", ...this.options.offline || []]);
|
|
3711
|
+
this.online$.subscribe(async (online) => {
|
|
3712
|
+
var _a2;
|
|
3713
|
+
if (!online) return;
|
|
3714
|
+
const table = (_a2 = this.database) == null ? void 0 : _a2.table("pending");
|
|
3715
|
+
const keys = await (table == null ? void 0 : table.getAllKeys());
|
|
3716
|
+
await Promise.allSettled(keys.map(async (k) => {
|
|
3717
|
+
const r = await (table == null ? void 0 : table.get(k));
|
|
3718
|
+
await this.request(r).then(() => table == null ? void 0 : table.delete(k));
|
|
3719
|
+
}));
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3691
3722
|
if (typeof window !== "undefined") {
|
|
3692
3723
|
const handleOffline = (state = this.offline) => {
|
|
3693
3724
|
if (this.online != state) this.online$.next(state);
|
|
@@ -3773,6 +3804,11 @@ const _Api = class _Api {
|
|
|
3773
3804
|
}).then(async (resp) => {
|
|
3774
3805
|
const warning = resp.headers["X-Warning"];
|
|
3775
3806
|
if (warning) console.warn(warning);
|
|
3807
|
+
const banner = resp.headers["X-User-Notice"];
|
|
3808
|
+
if (warning) {
|
|
3809
|
+
createBanner(banner);
|
|
3810
|
+
setTimeout(() => removeBanner(), 1e4);
|
|
3811
|
+
}
|
|
3776
3812
|
this.online = true;
|
|
3777
3813
|
let data = JSONAttemptParse(await resp.text());
|
|
3778
3814
|
if (!resp.ok || (data == null ? void 0 : data.error)) throw Object.assign(errorFromCode(resp.status, data.error), data);
|
|
@@ -3799,15 +3835,13 @@ const _Api = class _Api {
|
|
|
3799
3835
|
}
|
|
3800
3836
|
offlineBanner() {
|
|
3801
3837
|
if (this.options.offlineBanner === false || typeof document == "undefined") return;
|
|
3802
|
-
const banner = document.querySelector(".datalynk-offline-banner");
|
|
3803
3838
|
if (this.online) {
|
|
3804
|
-
banner
|
|
3805
|
-
} else
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
document.body.appendChild(b);
|
|
3839
|
+
removeBanner("datalynk-offline-banner");
|
|
3840
|
+
} else {
|
|
3841
|
+
createBanner("โ ๏ธ You are offline, please reconnect to sync changes", {
|
|
3842
|
+
id: "datalynk-offline-banner",
|
|
3843
|
+
position: this.options.offlineBanner === "top" ? "top" : "bottom"
|
|
3844
|
+
});
|
|
3811
3845
|
}
|
|
3812
3846
|
}
|
|
3813
3847
|
startHeartbeat() {
|
package/dist/pwa.d.ts
CHANGED
package/dist/pwa.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAE1B,qBAAa,GAAG;
|
|
1
|
+
{"version":3,"file":"pwa.d.ts","sourceRoot":"","sources":["../src/pwa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAE1B,qBAAa,GAAG;IAwBH,OAAO,CAAC,QAAQ,CAAC,GAAG;IAvBhC,4BAA4B;IACrB,mBAAmB,EAAE,GAAG,CAAQ;IAEvC,IAAI,QAAQ,IAAI,OAAO,CAAyC;IAEhE,IAAI,MAAM,IAAI,OAAO,CAAyC;IAE9D,IAAI,MAAM,IAAI,OAAO,CAAuD;IAE5E,IAAI,QAAQ,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAQ1E;IAED,IAAI,GAAG,IAAI,OAAO,CAEjB;gBAE4B,GAAG,EAAE,GAAG;IAErC,OAAO,CAAC,iBAAiB;IAiBzB,oBAAoB;IACd,KAAK,CAAC,QAAQ,GAAE,GAAQ;IAqE9B,qCAAqC;IAC/B,MAAM,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK;CA6IzC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {Api} from 'https://datalynk-client.scarborough.auxilium.world/dist/index.mjs';
|
|
2
|
+
|
|
3
|
+
let api = null;
|
|
4
|
+
const CACHE_NAME = 'datalynk';
|
|
5
|
+
let ONLINE = true;
|
|
6
|
+
const netChan = 'BroadcastChannel' in self ? new BroadcastChannel('sw-net') : null;
|
|
7
|
+
|
|
8
|
+
function broadcastStatus(online) {
|
|
9
|
+
if(ONLINE === online) return;
|
|
10
|
+
ONLINE = online;
|
|
11
|
+
netChan?.postMessage({type: 'status', online, ts: Date.now()});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function getSettings() {
|
|
15
|
+
const cache = await caches.open('api-settings');
|
|
16
|
+
const response = await cache.match('/settings');
|
|
17
|
+
if(!response) return {};
|
|
18
|
+
try {
|
|
19
|
+
return (await response.clone().json()) || {};
|
|
20
|
+
} catch {
|
|
21
|
+
const txt = await response.text();
|
|
22
|
+
try { return JSON.parse(txt || '{}') || {}; }
|
|
23
|
+
catch { return null; }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function setSettings(settings) {
|
|
28
|
+
const oldSettings = await getSettings();
|
|
29
|
+
const cache = await caches.open('api-settings');
|
|
30
|
+
const newSettings = {...oldSettings, ...settings};
|
|
31
|
+
await cache.put(new Request('/settings'), new Response(JSON.stringify(newSettings), {headers: {'Content-Type': 'application/json'}}));
|
|
32
|
+
return newSettings;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.addEventListener('install', (event) => {
|
|
36
|
+
event.waitUntil(self.skipWaiting());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
self.addEventListener('activate', (event) => {
|
|
40
|
+
event.waitUntil((async () => {
|
|
41
|
+
await self.clients.claim();
|
|
42
|
+
|
|
43
|
+
const keys = await caches.keys();
|
|
44
|
+
await Promise.all(
|
|
45
|
+
keys.filter((k) => k.startsWith('app-cache-') && k !== CACHE_NAME)
|
|
46
|
+
.map((k) => caches.delete(k))
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const settings = await getSettings();
|
|
50
|
+
if(settings?.url) {
|
|
51
|
+
api = new Api(settings.url, { ...(settings.options || {}), serviceWorker: false });
|
|
52
|
+
if(settings.token) api.token = settings.token;
|
|
53
|
+
}
|
|
54
|
+
})());
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
netChan?.addEventListener('message', (e) => {
|
|
58
|
+
if(e.data === 'status?') netChan?.postMessage({ type: 'status', online: ONLINE, ts: Date.now() });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
self.addEventListener('fetch', (event) => {
|
|
62
|
+
const request = event.request;
|
|
63
|
+
|
|
64
|
+
if(request.method !== 'GET') {
|
|
65
|
+
event.respondWith((async () => {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(request);
|
|
68
|
+
broadcastStatus(true);
|
|
69
|
+
return res;
|
|
70
|
+
} catch(err) {
|
|
71
|
+
broadcastStatus(false);
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
})());
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
event.respondWith((async () => {
|
|
79
|
+
const cache = await caches.open(CACHE_NAME);
|
|
80
|
+
if(!ONLINE) {
|
|
81
|
+
const cachedEarly = await cache.match(request);
|
|
82
|
+
if(cachedEarly) return cachedEarly;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const networkResponse = await fetch(request);
|
|
87
|
+
broadcastStatus(true);
|
|
88
|
+
if(networkResponse.status === 404) { // purge stale
|
|
89
|
+
await cache.delete(request);
|
|
90
|
+
return networkResponse;
|
|
91
|
+
}
|
|
92
|
+
const isCacheable = networkResponse.ok || networkResponse.type === 'opaque';
|
|
93
|
+
if(isCacheable) {
|
|
94
|
+
const isOpaqueNav = networkResponse.type === 'opaque' && request.mode === 'navigate';
|
|
95
|
+
if (!isOpaqueNav) {
|
|
96
|
+
event.waitUntil(cache.put(request, networkResponse.clone()));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return networkResponse;
|
|
100
|
+
} catch {
|
|
101
|
+
broadcastStatus(false);
|
|
102
|
+
const cached = await cache.match(request);
|
|
103
|
+
if(cached) return cached;
|
|
104
|
+
if(request.mode === 'navigate') {
|
|
105
|
+
const fallback = (await cache.match('/')) || (await cache.match('/index.html'));
|
|
106
|
+
if(fallback) return fallback;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return new Response('You are offline', {
|
|
110
|
+
status: 503,
|
|
111
|
+
statusText: 'Service Unavailable',
|
|
112
|
+
headers: {'Content-Type': 'text/plain'}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
})());
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
self.addEventListener('message', async (event) => {
|
|
119
|
+
if(event.data?.options) { // ๐ง Update settings
|
|
120
|
+
api = new Api(event.data.options.url, { ...event.data.options, serviceWorker: false });
|
|
121
|
+
await setSettings(event.data.options);
|
|
122
|
+
} else if(event.data?.token) { // ๐ Update token
|
|
123
|
+
if(api) api.token = event.data.token;
|
|
124
|
+
await setSettings({token: event.data.token});
|
|
125
|
+
} else if(event.data?.clearCache) { // ๐งน Clear cache (debugging)
|
|
126
|
+
await caches.delete(CACHE_NAME);
|
|
127
|
+
}
|
|
128
|
+
});
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@auxilium/datalynk-client",
|
|
3
3
|
"description": "Datalynk client library",
|
|
4
4
|
"repository": "https://gitlab.auxiliumgroup.com/auxilium/datalynk/datalynk-client",
|
|
5
|
-
"version": "1.3.
|
|
5
|
+
"version": "1.3.8",
|
|
6
6
|
"author": "Zak Timson <zaktimson@gmail.com>",
|
|
7
7
|
"private": false,
|
|
8
8
|
"main": "./dist/index.cjs",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsc && npx vite build",
|
|
23
|
+
"postbuild": "node -e \"const fs=require('fs');fs.cpSync('src/service.worker.mjs','dist/service.worker.mjs')\"",
|
|
23
24
|
"datalynk-models": "node --no-warnings ./bin/datalynk-models.mjs",
|
|
24
25
|
"docs": "typedoc --cleanOutputDir false --out ./docs --entryPoints src/**/*.ts --readme README.md",
|
|
25
26
|
"watch": "vite build --watch"
|