@livx.cc/native-kit 0.45.3 → 0.45.5
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/package.json +1 -1
- package/src/core/web-adapter.ts +30 -1
- package/src/index.ts +1 -1
- package/src/modules/notifications.ts +25 -0
package/package.json
CHANGED
package/src/core/web-adapter.ts
CHANGED
|
@@ -268,7 +268,36 @@ export class WebAdapter implements NativeKitAdapter {
|
|
|
268
268
|
case 'notifications.schedule': {
|
|
269
269
|
if (Notification.permission !== 'granted') throw new KitError('DENIED', 'Permission not granted');
|
|
270
270
|
const delay = (p.delaySec ?? 1) * 1000;
|
|
271
|
-
|
|
271
|
+
// Rich options degrade one step at a time rather than all-or-nothing: `icon`/`image` are
|
|
272
|
+
// honoured by the plain Notification constructor, but ACTION BUTTONS exist only on
|
|
273
|
+
// ServiceWorkerRegistration.showNotification — so use the SW when there is one and fall back
|
|
274
|
+
// to a button-less banner when there isn't, instead of refusing the call.
|
|
275
|
+
const opts: NotificationOptions & { image?: string; actions?: Array<{ action: string; title: string }> } = {
|
|
276
|
+
body: p.body,
|
|
277
|
+
icon: p.icon,
|
|
278
|
+
tag: p.sender || undefined,
|
|
279
|
+
silent: !!p.silent,
|
|
280
|
+
data: { url: p.deepLink, actions: p.actions },
|
|
281
|
+
};
|
|
282
|
+
if (p.image) opts.image = p.image;
|
|
283
|
+
const buttons: Array<{ id: string; title: string }> = Array.isArray(p.actions) ? p.actions.slice(0, 3) : [];
|
|
284
|
+
setTimeout(async () => {
|
|
285
|
+
const reg = buttons.length ? await navigator.serviceWorker?.ready.catch(() => null) : null;
|
|
286
|
+
if (reg && buttons.length) {
|
|
287
|
+
opts.actions = buttons.map((b) => ({ action: b.id, title: b.title }));
|
|
288
|
+
await reg.showNotification(p.title, opts).catch((e: Error) => console.warn('[kit] showNotification failed', e));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const n = new Notification(p.title, opts);
|
|
292
|
+
// A tap on the banner focuses this tab and follows the deep link when it is a web URL —
|
|
293
|
+
// a custom scheme is a native-only target and navigating to it here would just error.
|
|
294
|
+
n.onclick = () => {
|
|
295
|
+
window.focus();
|
|
296
|
+
const url = String(p.deepLink ?? '');
|
|
297
|
+
if (/^https?:\/\//i.test(url)) location.href = url;
|
|
298
|
+
n.close();
|
|
299
|
+
};
|
|
300
|
+
}, delay);
|
|
272
301
|
return { id: p.id ?? Date.now() % 100000 } as T;
|
|
273
302
|
}
|
|
274
303
|
case 'notifications.pending':
|
package/src/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type { Orientation, OrientationLock } from './modules/screen';
|
|
|
27
27
|
export type { KeyboardInfo } from './modules/keyboard';
|
|
28
28
|
export type { FsDirectory, FsEncoding, FsEntry, PickedFile } from './modules/fs';
|
|
29
29
|
export type { DeviceInfo } from './modules/device';
|
|
30
|
-
export type { ScheduleOptions } from './modules/notifications';
|
|
30
|
+
export type { ScheduleOptions, NotificationAction } from './modules/notifications';
|
|
31
31
|
export type { PushMessage, PushPlatform, PushToken } from './modules/push';
|
|
32
32
|
export type { GeoPosition } from './modules/geo';
|
|
33
33
|
export type { HeadingSample } from './modules/heading';
|
|
@@ -44,6 +44,31 @@ export interface ScheduleOptions {
|
|
|
44
44
|
* falls back to the DEFAULT alert — never to silence. iOS only; Android uses its channel sound.
|
|
45
45
|
*/
|
|
46
46
|
sound?: string;
|
|
47
|
+
/**
|
|
48
|
+
* HERO ARTWORK — an image URL or data-URI that turns the banner into a rich card: an expandable
|
|
49
|
+
* attachment on iOS (UNNotificationAttachment) and a BigPictureStyle image on Android. A failed
|
|
50
|
+
* download degrades to a plain banner rather than dropping the notification.
|
|
51
|
+
*/
|
|
52
|
+
image?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Up to 3 tappable BUTTONS. Each carries its own `deepLink` (falling back to the notification's
|
|
55
|
+
* when omitted), delivered through the same `deeplink.open` event as a banner tap — so
|
|
56
|
+
* `kit.lifecycle.onDeepLink` routes a button tap with no extra wiring. iOS draws 2 on a collapsed
|
|
57
|
+
* banner (4 expanded), Android 3; extra entries, and entries missing an id or title, are dropped.
|
|
58
|
+
* On the web they need a service worker (buttons are a ServiceWorkerRegistration feature) —
|
|
59
|
+
* without one the banner still shows, just without buttons.
|
|
60
|
+
*/
|
|
61
|
+
actions?: NotificationAction[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** One tappable button on a rich notification. */
|
|
65
|
+
export interface NotificationAction {
|
|
66
|
+
/** Stable id — what identifies the tapped button. */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Button label. */
|
|
69
|
+
title: string;
|
|
70
|
+
/** Where the tap lands. Defaults to the notification's own `deepLink`. */
|
|
71
|
+
deepLink?: string;
|
|
47
72
|
}
|
|
48
73
|
|
|
49
74
|
export class NotificationsModule {
|