@mpgd/adapter-capacitor 0.10.1 → 0.11.0
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 +126 -19
- package/dist/bridge-events.d.ts +7 -0
- package/dist/bridge-events.js +14 -0
- package/dist/commerce-bridge.js +2 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -12
- package/dist/notifications-bridge.d.ts +82 -0
- package/dist/notifications-bridge.js +333 -0
- package/dist/notifications-provider.d.ts +118 -0
- package/dist/notifications-provider.js +1023 -0
- package/dist/notifications-sdk.d.ts +166 -0
- package/dist/notifications-sdk.js +47 -0
- package/dist/notifications.d.ts +27 -0
- package/dist/notifications.js +35 -0
- package/dist/providers.d.ts +56 -3
- package/dist/providers.js +98 -18
- package/package.json +22 -8
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { bridgeNotificationsEventProtocol, createBridgeError, createBridgeEventSource, isBridgeNotificationsV2Method, } from '@mpgd/bridge';
|
|
2
|
+
import { assertBridgeNotificationsV2Payload, assertBridgeNotificationsV2Result, } from '@mpgd/bridge/notifications';
|
|
3
|
+
import { PlatformOperationError } from '@mpgd/platform';
|
|
4
|
+
import { assertNotificationEventEnvelope, isNotificationErrorCode, NotificationError, notificationsProtocol, notificationsProtocolVersion, toNotificationSubscriptionAdapter, } from '@mpgd/platform/notifications';
|
|
5
|
+
import { namesOtherProtocol } from './bridge-events.js';
|
|
6
|
+
/**
|
|
7
|
+
* In-process `NativeBridge` over a notifications v2 provider. Each
|
|
8
|
+
* `notifications.v2.*` request validates its payload (a payload the contract
|
|
9
|
+
* rejects answers `NOTIFICATIONS_INVALID_INPUT`) and its answer (one the
|
|
10
|
+
* contract rejects answers `NOTIFICATIONS_CONFIGURATION_REQUIRED`, because the
|
|
11
|
+
* provider broke it). A method the
|
|
12
|
+
* provider does not implement answers `NOTIFICATIONS_UNSUPPORTED`, except
|
|
13
|
+
* `openSettings`, which answers `unavailable`. Events go out through
|
|
14
|
+
* `createBridgeEventSource`, which attaches to the provider only for the
|
|
15
|
+
* first listener, so events the provider buffered reach it. Any other method
|
|
16
|
+
* answers `UNSUPPORTED_METHOD`.
|
|
17
|
+
*/
|
|
18
|
+
export function createNotificationProviderBridge(input) {
|
|
19
|
+
const { provider } = input;
|
|
20
|
+
const events = createBridgeEventSource(bridgeNotificationsEventProtocol, provider);
|
|
21
|
+
const serve = async (method, payload) => {
|
|
22
|
+
const checked = checkedPayload(method, payload);
|
|
23
|
+
switch (method) {
|
|
24
|
+
case 'notifications.v2.getCapabilities':
|
|
25
|
+
return {
|
|
26
|
+
protocolVersion: notificationsProtocolVersion,
|
|
27
|
+
provider: provider.provider,
|
|
28
|
+
capabilities: await provider.getCapabilities(),
|
|
29
|
+
};
|
|
30
|
+
case 'notifications.v2.getPermission':
|
|
31
|
+
return provider.getPermission(checked);
|
|
32
|
+
case 'notifications.v2.requestPermission':
|
|
33
|
+
return provider.requestPermission(checked);
|
|
34
|
+
case 'notifications.v2.revokePermission':
|
|
35
|
+
return implemented(provider.revokePermission, method).call(provider, checked);
|
|
36
|
+
case 'notifications.v2.register':
|
|
37
|
+
return { registration: await implemented(provider.register, method).call(provider) };
|
|
38
|
+
case 'notifications.v2.scheduleLocal':
|
|
39
|
+
return implemented(provider.scheduleLocal, method).call(provider, checked);
|
|
40
|
+
case 'notifications.v2.cancelLocal':
|
|
41
|
+
await implemented(provider.cancelLocal, method).call(provider, checked);
|
|
42
|
+
return { cancelled: true };
|
|
43
|
+
case 'notifications.v2.listLocal':
|
|
44
|
+
return { pending: await implemented(provider.listLocal, method).call(provider) };
|
|
45
|
+
case 'notifications.v2.openSettings':
|
|
46
|
+
return {
|
|
47
|
+
status: provider.openSettings === undefined
|
|
48
|
+
? 'unavailable'
|
|
49
|
+
: await provider.openSettings(),
|
|
50
|
+
};
|
|
51
|
+
default: {
|
|
52
|
+
// A method added to the list without a case here fails to compile.
|
|
53
|
+
const unhandled = method;
|
|
54
|
+
throw new NotificationError('NOTIFICATIONS_UNSUPPORTED', {
|
|
55
|
+
message: `The notifications bridge does not serve ${String(unhandled)}.`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
async request(request) {
|
|
62
|
+
if (!isBridgeNotificationsV2Method(request.method)) {
|
|
63
|
+
return createBridgeError(request.id, 'UNSUPPORTED_METHOD', `${request.method} is not a notifications v2 method.`);
|
|
64
|
+
}
|
|
65
|
+
const method = request.method;
|
|
66
|
+
let data;
|
|
67
|
+
try {
|
|
68
|
+
data = await serve(method, request.payload);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return toBridgeError(request, error);
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
return { id: request.id, ok: true, data: assertBridgeNotificationsV2Result(method, data) };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return createBridgeError(request.id, 'NOTIFICATIONS_CONFIGURATION_REQUIRED', `Notifications provider returned invalid data for ${method}: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
subscribe: (listener) => events.subscribe(listener),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** The payload as the contract reads it, or `NOTIFICATIONS_INVALID_INPUT`; both sides of the bridge use it. */
|
|
84
|
+
function checkedPayload(method, payload) {
|
|
85
|
+
try {
|
|
86
|
+
return assertBridgeNotificationsV2Payload(method, payload);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new NotificationError('NOTIFICATIONS_INVALID_INPUT', {
|
|
90
|
+
message: `${method} payload is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
91
|
+
cause: error,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function implemented(method, name) {
|
|
96
|
+
if (method === undefined) {
|
|
97
|
+
throw new NotificationError('NOTIFICATIONS_UNSUPPORTED', {
|
|
98
|
+
message: `The notifications provider does not implement ${name}.`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return method;
|
|
102
|
+
}
|
|
103
|
+
function toBridgeError(request, error) {
|
|
104
|
+
if (error instanceof PlatformOperationError) {
|
|
105
|
+
// A host code kept with `NOTIFICATIONS_CONFIGURATION_REQUIRED` crosses in
|
|
106
|
+
// the message: the bridge error has no field for it.
|
|
107
|
+
const hostCode = error instanceof NotificationError ? error.hostCode : undefined;
|
|
108
|
+
return createBridgeError(request.id, error.code, hostCode === undefined ? error.message : `${error.message} (host code ${hostCode})`, error.retryable);
|
|
109
|
+
}
|
|
110
|
+
return createBridgeError(request.id, 'NOTIFICATIONS_TEMPORARILY_UNAVAILABLE', `${request.method} failed: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Registry entry for a notifications v2 provider, as
|
|
114
|
+
* `createCommerceServiceProvider` is for commerce: it declares no feature and
|
|
115
|
+
* no method, the nine `notifications.v2.*` methods are implied and served in
|
|
116
|
+
* process by `createNotificationProviderBridge`, and the gateway exposes
|
|
117
|
+
* `notificationsV2` and a v1 `notifications` adapter served from it.
|
|
118
|
+
*/
|
|
119
|
+
export function createNotificationServiceProvider(input) {
|
|
120
|
+
const { notifications } = input;
|
|
121
|
+
// Copied entry by entry, `targets` included: an entry that still carries
|
|
122
|
+
// one names no single target, and the v1 adapter counts it as outside.
|
|
123
|
+
const topics = input.topics.map((topic) => ({ ...topic }));
|
|
124
|
+
const service = {
|
|
125
|
+
async getCapabilities() {
|
|
126
|
+
return copyCapabilities(assertBridgeNotificationsV2Result('notifications.v2.getCapabilities', {
|
|
127
|
+
protocolVersion: notificationsProtocolVersion,
|
|
128
|
+
provider: notifications.provider,
|
|
129
|
+
capabilities: await notifications.getCapabilities(),
|
|
130
|
+
}).capabilities);
|
|
131
|
+
},
|
|
132
|
+
assertResult(method, data) {
|
|
133
|
+
assertBridgeNotificationsV2Result(method, data);
|
|
134
|
+
},
|
|
135
|
+
createGatewaySurface({ request, subscribe }) {
|
|
136
|
+
const client = createBridgeNotificationProvider({
|
|
137
|
+
provider: notifications.provider,
|
|
138
|
+
serves: {
|
|
139
|
+
revokePermission: notifications.revokePermission !== undefined,
|
|
140
|
+
register: notifications.register !== undefined,
|
|
141
|
+
local: notifications.scheduleLocal !== undefined
|
|
142
|
+
&& notifications.cancelLocal !== undefined
|
|
143
|
+
&& notifications.listLocal !== undefined,
|
|
144
|
+
},
|
|
145
|
+
request,
|
|
146
|
+
subscribe,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
notificationsV2: client,
|
|
150
|
+
notifications: createNotificationSubscriptionAdapter(client, topics),
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
id: input.id,
|
|
156
|
+
features: [],
|
|
157
|
+
methods: [],
|
|
158
|
+
notificationsV2: service,
|
|
159
|
+
bridge: createNotificationProviderBridge({ provider: notifications }),
|
|
160
|
+
async getAvailability() {
|
|
161
|
+
return {};
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The v1 `NotificationSubscriptionAdapter` served from a v2 provider whose
|
|
167
|
+
* host is readable (RFC "Migration and PR plan" table, through
|
|
168
|
+
* `toNotificationSubscriptionAdapter`): every state comes from the host, a
|
|
169
|
+
* topic outside `topics` reads `configuration-required` and requests
|
|
170
|
+
* `unavailable`.
|
|
171
|
+
*/
|
|
172
|
+
export function createNotificationSubscriptionAdapter(provider, topics) {
|
|
173
|
+
return toNotificationSubscriptionAdapter({
|
|
174
|
+
async getPermission(input) {
|
|
175
|
+
const snapshot = await provider.getPermission(input);
|
|
176
|
+
return {
|
|
177
|
+
...snapshot,
|
|
178
|
+
sources: Object.fromEntries(Object.keys(snapshot.topics).map((topic) => [topic, 'host'])),
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
requestPermission: ({ topics: requested }) => provider.requestPermission({ topics: requested }),
|
|
182
|
+
}, topics);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* `NotificationProvider` over a bridge that serves the `notifications.v2.*`
|
|
186
|
+
* methods and the event envelope. Answers are validated again on this side,
|
|
187
|
+
* so a transport that forgets to stays fail-closed. A bridge failure becomes
|
|
188
|
+
* a `NotificationError`: one with a notifications code keeps it, anything
|
|
189
|
+
* else is `NOTIFICATIONS_TEMPORARILY_UNAVAILABLE` with the failure's
|
|
190
|
+
* retryable flag. An answer the contract rejects is
|
|
191
|
+
* `NOTIFICATIONS_CONFIGURATION_REQUIRED`, which is not retryable, because the
|
|
192
|
+
* provider broke the contract. An event envelope that
|
|
193
|
+
* names another protocol is skipped; a malformed one is dropped with a
|
|
194
|
+
* warning, because it carries nothing the game could route.
|
|
195
|
+
*
|
|
196
|
+
* Events reach the listeners subscribed when they arrive. The Capacitor
|
|
197
|
+
* provider keeps events that arrive while no listener is attached for the
|
|
198
|
+
* next subscriber, so a game that subscribes per scene loses no open.
|
|
199
|
+
*/
|
|
200
|
+
export function createBridgeNotificationProvider(input) {
|
|
201
|
+
const send = async (method, payload) => {
|
|
202
|
+
// Checked here too, so a payload the contract rejects never pauses the
|
|
203
|
+
// game for a request that cannot succeed.
|
|
204
|
+
checkedPayload(method, payload);
|
|
205
|
+
let data;
|
|
206
|
+
try {
|
|
207
|
+
data = await input.request(method, payload);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
throw toNotificationError(error);
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
return assertBridgeNotificationsV2Result(method, data);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
// The provider broke the contract; a retry would not change that.
|
|
217
|
+
throw new NotificationError('NOTIFICATIONS_CONFIGURATION_REQUIRED', {
|
|
218
|
+
message: `Bridge returned invalid data for ${method}.`,
|
|
219
|
+
cause: error,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const protocol = { protocol: notificationsProtocol };
|
|
224
|
+
return {
|
|
225
|
+
provider: input.provider,
|
|
226
|
+
async getCapabilities() {
|
|
227
|
+
return (await send('notifications.v2.getCapabilities', protocol)).capabilities;
|
|
228
|
+
},
|
|
229
|
+
// Async, so wrong input from a caller outside TypeScript rejects instead
|
|
230
|
+
// of throwing synchronously.
|
|
231
|
+
getPermission: async (request) => send('notifications.v2.getPermission', topicsPayload(request)),
|
|
232
|
+
requestPermission: async (request) => send('notifications.v2.requestPermission', topicsPayload(request)),
|
|
233
|
+
...(input.serves.revokePermission
|
|
234
|
+
? {
|
|
235
|
+
revokePermission: async (request) => send('notifications.v2.revokePermission', topicsPayload(request)),
|
|
236
|
+
}
|
|
237
|
+
: {}),
|
|
238
|
+
...(input.serves.register
|
|
239
|
+
? {
|
|
240
|
+
async register() {
|
|
241
|
+
return (await send('notifications.v2.register', protocol)).registration;
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
: {}),
|
|
245
|
+
...(input.serves.local
|
|
246
|
+
? {
|
|
247
|
+
scheduleLocal: (request) => send('notifications.v2.scheduleLocal', request),
|
|
248
|
+
async cancelLocal(request) {
|
|
249
|
+
// A missing list stays missing, so the payload check rejects it.
|
|
250
|
+
await send('notifications.v2.cancelLocal', {
|
|
251
|
+
keys: request?.keys,
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
async listLocal() {
|
|
255
|
+
return (await send('notifications.v2.listLocal', protocol)).pending;
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
: {}),
|
|
259
|
+
async openSettings() {
|
|
260
|
+
return (await send('notifications.v2.openSettings', protocol)).status;
|
|
261
|
+
},
|
|
262
|
+
subscribe(listener) {
|
|
263
|
+
return input.subscribe((envelope) => {
|
|
264
|
+
if (namesOtherProtocol(envelope, bridgeNotificationsEventProtocol)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let event;
|
|
268
|
+
try {
|
|
269
|
+
event = assertNotificationEventEnvelope(envelope).event;
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
console.warn('Dropped an invalid notifications event from the bridge.', error);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
listener(event);
|
|
276
|
+
});
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Only `topics` crosses, since the payload validator refuses unknown keys. A
|
|
282
|
+
* missing request or list stays missing on purpose: the cast lets `send`'s
|
|
283
|
+
* payload validator reject it as `NOTIFICATIONS_INVALID_INPUT`.
|
|
284
|
+
*/
|
|
285
|
+
function topicsPayload(request) {
|
|
286
|
+
return { topics: request?.topics };
|
|
287
|
+
}
|
|
288
|
+
function toNotificationError(error) {
|
|
289
|
+
if (error instanceof NotificationError) {
|
|
290
|
+
return error;
|
|
291
|
+
}
|
|
292
|
+
const code = typeof error === 'object' && error !== null
|
|
293
|
+
? error.code
|
|
294
|
+
: undefined;
|
|
295
|
+
const options = {
|
|
296
|
+
...(error instanceof Error ? { message: error.message } : {}),
|
|
297
|
+
// A transport failure keeps its retryability.
|
|
298
|
+
...(error instanceof PlatformOperationError ? { retryable: error.retryable } : {}),
|
|
299
|
+
cause: error,
|
|
300
|
+
};
|
|
301
|
+
return new NotificationError(isNotificationErrorCode(code) ? code : 'NOTIFICATIONS_TEMPORARILY_UNAVAILABLE', options);
|
|
302
|
+
}
|
|
303
|
+
/** A field-by-field copy, so the gateway never hands out a provider-owned block. */
|
|
304
|
+
function copyCapabilities(capabilities) {
|
|
305
|
+
const { permission, remote, local } = capabilities;
|
|
306
|
+
return {
|
|
307
|
+
availability: capabilities.availability,
|
|
308
|
+
permission: {
|
|
309
|
+
scope: permission.scope,
|
|
310
|
+
prompt: permission.prompt,
|
|
311
|
+
readable: permission.readable,
|
|
312
|
+
serverReadable: permission.serverReadable,
|
|
313
|
+
allowance: permission.allowance,
|
|
314
|
+
userGesture: permission.userGesture,
|
|
315
|
+
...(permission.promptLimit === undefined ? {} : { promptLimit: permission.promptLimit }),
|
|
316
|
+
...(permission.maxTopicsPerPrompt === undefined
|
|
317
|
+
? {}
|
|
318
|
+
: { maxTopicsPerPrompt: permission.maxTopicsPerPrompt }),
|
|
319
|
+
settings: permission.settings,
|
|
320
|
+
},
|
|
321
|
+
remote: {
|
|
322
|
+
supported: remote.supported,
|
|
323
|
+
purposes: [...remote.purposes],
|
|
324
|
+
addressing: remote.addressing,
|
|
325
|
+
content: remote.content,
|
|
326
|
+
},
|
|
327
|
+
local: {
|
|
328
|
+
supported: local.supported,
|
|
329
|
+
...(local.maxPending === undefined ? {} : { maxPending: local.maxPending }),
|
|
330
|
+
},
|
|
331
|
+
open: capabilities.open,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type AppPlugin } from '@capacitor/app';
|
|
2
|
+
import { type NotificationProvider, type NotificationProviderId, type NotificationTopicDeclaration } from '@mpgd/platform/notifications';
|
|
3
|
+
import type { LocalNotificationSdk, PushSdk } from './notifications-sdk.js';
|
|
4
|
+
/** The reference provider id the backend's FCM registry entry uses. */
|
|
5
|
+
export declare const capacitorNotificationProviderId = "fcm";
|
|
6
|
+
/**
|
|
7
|
+
* The notification category (iOS) and action type that carries the
|
|
8
|
+
* advertising opt-out action. An FCM send for an advertising topic names it
|
|
9
|
+
* (`apns.payload.aps.category`) so iOS shows the action.
|
|
10
|
+
*/
|
|
11
|
+
export declare const capacitorAdvertisingActionTypeId = "mpgd-advertising";
|
|
12
|
+
/**
|
|
13
|
+
* Keys of the push data the provider reads. A send carries the topic and the
|
|
14
|
+
* topic's root-relative route in them; every other string entry reaches the
|
|
15
|
+
* game as the open's untrusted `data`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const capacitorNotificationDataKeys: Readonly<{
|
|
18
|
+
readonly topic: 'topic';
|
|
19
|
+
readonly route: 'route';
|
|
20
|
+
}>;
|
|
21
|
+
/** iOS keeps at most 64 pending local notifications per app. */
|
|
22
|
+
export declare const capacitorIosMaxPendingLocalNotifications = 64;
|
|
23
|
+
/** How long `openSettings` waits for the app to leave for the settings screen. */
|
|
24
|
+
export declare const capacitorSettingsLeaveTimeoutMs = 5000;
|
|
25
|
+
/** How long `openSettings` waits for the player to come back from settings. */
|
|
26
|
+
export declare const capacitorSettingsReturnTimeoutMs = 120000;
|
|
27
|
+
export type CapacitorNotificationAppEvents = Pick<AppPlugin, 'addListener'>;
|
|
28
|
+
export interface CapacitorNotificationChannelSettings {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
/** Android importance, 1 (min) to 5 (max); 3 when absent. */
|
|
32
|
+
readonly importance?: 1 | 2 | 3 | 4 | 5;
|
|
33
|
+
}
|
|
34
|
+
export interface CreateCapacitorNotificationProviderInput {
|
|
35
|
+
readonly platform: 'android' | 'ios';
|
|
36
|
+
/**
|
|
37
|
+
* The topics resolved for this target, as the runtime reads them
|
|
38
|
+
* (`resolveNotificationTopicsForTarget(catalog, 'android')` from
|
|
39
|
+
* `@mpgd/catalog`). A topic outside them answers `unavailable` to a request.
|
|
40
|
+
*/
|
|
41
|
+
readonly topics: readonly NotificationTopicDeclaration[];
|
|
42
|
+
/** Remote delivery; absent means the provider declares none. */
|
|
43
|
+
readonly push?: PushSdk;
|
|
44
|
+
/** Local reminders and the permission read; absent means no local reminders. */
|
|
45
|
+
readonly local?: LocalNotificationSdk;
|
|
46
|
+
/** Defaults to `capacitorNotificationProviderId` ('fcm'). */
|
|
47
|
+
readonly provider?: NotificationProviderId;
|
|
48
|
+
/**
|
|
49
|
+
* Denials Android accepts before only settings can change the answer: 2 for
|
|
50
|
+
* a target SDK of 33 or later (Capacitor 8 projects target 36), 1 for an
|
|
51
|
+
* app that targets 32 or lower. iOS always declares 1.
|
|
52
|
+
*/
|
|
53
|
+
readonly androidPromptLimit?: 1 | 2;
|
|
54
|
+
/**
|
|
55
|
+
* Names of the Android channels, by channel id. A topic uses its mapping's
|
|
56
|
+
* `channelId`, or the default channel of its purpose
|
|
57
|
+
* (`capacitorNotificationChannelId`).
|
|
58
|
+
*/
|
|
59
|
+
readonly channels?: Readonly<Record<string, CapacitorNotificationChannelSettings>>;
|
|
60
|
+
/** The title of the advertising opt-out action; '수신거부' when absent. */
|
|
61
|
+
readonly advertisingOptOutTitle?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Opens the OS notification settings for this app. Neither plugin can, so
|
|
64
|
+
* the shell supplies it (for example a small native method). Without it
|
|
65
|
+
* `permission.settings` is false and `openSettings` answers 'unavailable'.
|
|
66
|
+
*/
|
|
67
|
+
readonly openSettings?: () => Promise<void>;
|
|
68
|
+
/** App state events; defaults to `App` from `@capacitor/app`. */
|
|
69
|
+
readonly app?: CapacitorNotificationAppEvents | null;
|
|
70
|
+
/** The device's IANA zone for registrations; defaults to `Intl`. */
|
|
71
|
+
readonly timeZone?: () => string | undefined;
|
|
72
|
+
/** The device locale for registrations; defaults to `navigator.language`. */
|
|
73
|
+
readonly locale?: () => string | undefined;
|
|
74
|
+
readonly settingsLeaveTimeoutMs?: number;
|
|
75
|
+
readonly settingsReturnTimeoutMs?: number;
|
|
76
|
+
/** Reports a native listener or setup failure; defaults to `console.warn`. */
|
|
77
|
+
readonly onError?: (error: unknown) => void;
|
|
78
|
+
}
|
|
79
|
+
/** The provider, with the topics it was created for (the v1 surface reads them). */
|
|
80
|
+
export interface CapacitorNotificationProvider extends NotificationProvider {
|
|
81
|
+
readonly topics: readonly NotificationTopicDeclaration[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The Android channel a topic posts to: its mapping's `channelId`, or the
|
|
85
|
+
* default channel of its purpose (`mpgd-service`, `mpgd-requested`,
|
|
86
|
+
* `mpgd-advertising`), so advertising stays in a channel of its own that the
|
|
87
|
+
* player can turn off in system settings.
|
|
88
|
+
*/
|
|
89
|
+
export declare function capacitorNotificationChannelId(topic: Pick<NotificationTopicDeclaration, 'purpose' | 'mapping'>): string;
|
|
90
|
+
/**
|
|
91
|
+
* The native id of a local reminder key: a stable positive 32-bit integer
|
|
92
|
+
* (FNV-1a), as Android requires. The provider probes the next id when another
|
|
93
|
+
* pending reminder already holds it, and finds reminders by the key it stores
|
|
94
|
+
* in `extra`, so a collision never replaces another reminder.
|
|
95
|
+
*/
|
|
96
|
+
export declare function capacitorLocalNotificationId(key: string): number;
|
|
97
|
+
/**
|
|
98
|
+
* Creates the Capacitor notifications provider over the two plugin SDKs.
|
|
99
|
+
*
|
|
100
|
+
* - **Listeners first.** It registers every native listener synchronously,
|
|
101
|
+
* before any `await`, and keeps them for the process lifetime: Capacitor
|
|
102
|
+
* retains a plugin's events only until its first native listener, so a
|
|
103
|
+
* cold-start open reaches this provider, which keeps it for its first
|
|
104
|
+
* subscriber.
|
|
105
|
+
* - **Permission.** It reads `LocalNotifications.checkPermissions`, which
|
|
106
|
+
* reflects the system setting on every Android version, and maps the OS
|
|
107
|
+
* `denied` to `blocked`: on iOS after the one denial, on Android after the
|
|
108
|
+
* second. Only `requestPermission` shows the OS dialog; `scheduleLocal`
|
|
109
|
+
* answers `permission-required` instead.
|
|
110
|
+
* - **Local reminders.** Inexact (`isExactNotification: false`), on the
|
|
111
|
+
* topic's channel, at most 64 pending on iOS, keeping the nearest, one
|
|
112
|
+
* operation at a time. A `fireAt` already past posts at once, as both
|
|
113
|
+
* plugins do.
|
|
114
|
+
* - **Fails closed.** Without either SDK the provider is `unsupported`;
|
|
115
|
+
* without `push` it declares no remote delivery, without `local` no local
|
|
116
|
+
* reminders.
|
|
117
|
+
*/
|
|
118
|
+
export declare function createCapacitorNotificationProvider(input: CreateCapacitorNotificationProviderInput): CapacitorNotificationProvider;
|