@chipmobilesdk/rn-notification 0.4.0 → 0.5.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/CHANGELOG.md +257 -166
- package/README.md +420 -410
- package/package.json +61 -75
- package/skills/sdk-rn-notification/SKILL.md +172 -173
- package/src/channel/migrate.ts +285 -289
- package/src/channel/tone-map.ts +130 -130
- package/src/channel/validate-tone.ts +61 -61
- package/src/contract/channel.ts +192 -188
- package/src/contract/diagnostics.ts +102 -102
- package/src/contract/errors.ts +142 -134
- package/src/contract/index.ts +10 -10
- package/src/contract/interaction.ts +62 -62
- package/src/contract/notification.ts +118 -118
- package/src/contract/permission.ts +55 -60
- package/src/contract/reconcile.ts +88 -88
- package/src/contract/recurrence.ts +129 -129
- package/src/diagnostics/burst.ts +101 -101
- package/src/diagnostics/events.ts +81 -72
- package/src/diagnostics/redact.ts +55 -72
- package/src/diagnostics/snapshot.ts +100 -64
- package/src/engine/adapter.ts +172 -172
- package/src/engine/codec.ts +89 -89
- package/src/engine/notify-kit-engine.ts +715 -707
- package/src/index.ts +208 -208
- package/src/initialize.ts +247 -236
- package/src/interaction/index.ts +146 -146
- package/src/maintenance/cycle.ts +150 -154
- package/src/maintenance/permission-refresh.ts +39 -41
- package/src/maintenance/reanchor.ts +101 -101
- package/src/maintenance/toggle.ts +66 -64
- package/src/maintenance/verify.ts +83 -83
- package/src/maintenance/window-refresh.ts +64 -64
- package/src/permission/index.ts +183 -183
- package/src/permission/state.ts +77 -77
- package/src/push/envelope.ts +165 -165
- package/src/push/fcm-provider.ts +150 -146
- package/src/push/gate.ts +190 -190
- package/src/push/index.ts +33 -29
- package/src/push/instructions.ts +114 -109
- package/src/push/provider-adapter.ts +54 -54
- package/src/push/signout.ts +65 -65
- package/src/reconcile/chunk.ts +53 -53
- package/src/reconcile/diff.ts +142 -145
- package/src/reconcile/duplicates.ts +73 -73
- package/src/reconcile/index.ts +180 -180
- package/src/reconcile/truncate.ts +56 -56
- package/src/recurrence/clock-guard.ts +73 -71
- package/src/recurrence/expand.ts +326 -319
- package/src/recurrence/index.ts +270 -259
- package/src/repeat/expiry.ts +53 -55
- package/src/repeat/index.ts +54 -54
- package/src/repeat/marker.ts +72 -72
- package/src/repeat/place.ts +73 -73
- package/src/schedule/anchor.ts +294 -267
- package/src/schedule/index.ts +174 -181
- package/src/schedule/partition.ts +88 -85
- package/src/schedule/resolve-moment.ts +80 -80
- package/src/schedule/validate-content.ts +111 -111
- package/src/schedule/validate-identifier.ts +126 -126
- package/src/schedule/writer.ts +55 -55
- package/src/state/caps.ts +71 -71
- package/src/state/dedup-ledger.ts +35 -35
- package/src/state/interaction-buffer.ts +101 -73
- package/src/state/mmkv-store.ts +92 -75
- package/src/state/permission-history.ts +41 -41
- package/src/state/record.ts +270 -263
- package/src/state/reset.ts +41 -41
- package/src/state/series-ledger.ts +91 -91
- package/src/state/store.ts +43 -40
- package/src/testing/conformance/index.ts +258 -248
- package/src/testing/fake-engine.ts +462 -468
- package/src/testing/fake-provider.ts +126 -129
- package/src/testing/faults.ts +72 -72
- package/src/testing/index.ts +23 -28
- package/src/testing/measure.ts +337 -337
package/package.json
CHANGED
|
@@ -1,75 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chipmobilesdk/rn-notification",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"private": false,
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/index.ts",
|
|
11
|
-
"react-native": "./src/index.ts",
|
|
12
|
-
"default": "./src/index.ts"
|
|
13
|
-
},
|
|
14
|
-
"./push": {
|
|
15
|
-
"types": "./src/push/index.ts",
|
|
16
|
-
"react-native": "./src/push/index.ts",
|
|
17
|
-
"default": "./src/push/index.ts"
|
|
18
|
-
},
|
|
19
|
-
"./testing": {
|
|
20
|
-
"types": "./src/testing/index.ts",
|
|
21
|
-
"react-native": "./src/testing/index.ts",
|
|
22
|
-
"default": "./src/testing/index.ts"
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"files": [
|
|
26
|
-
"src
|
|
27
|
-
"src
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"@react-native-firebase/messaging":
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"optional": true
|
|
63
|
-
},
|
|
64
|
-
"@react-native-firebase/messaging": {
|
|
65
|
-
"optional": true
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
"engines": {
|
|
69
|
-
"node": ">=22.11.0"
|
|
70
|
-
},
|
|
71
|
-
"publishConfig": {
|
|
72
|
-
"access": "public",
|
|
73
|
-
"registry": "https://registry.npmjs.org/"
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@chipmobilesdk/rn-notification",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"react-native": "./src/index.ts",
|
|
12
|
+
"default": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"./push": {
|
|
15
|
+
"types": "./src/push/index.ts",
|
|
16
|
+
"react-native": "./src/push/index.ts",
|
|
17
|
+
"default": "./src/push/index.ts"
|
|
18
|
+
},
|
|
19
|
+
"./testing": {
|
|
20
|
+
"types": "./src/testing/index.ts",
|
|
21
|
+
"react-native": "./src/testing/index.ts",
|
|
22
|
+
"default": "./src/testing/index.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/__tests__",
|
|
28
|
+
"skills",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md"
|
|
31
|
+
],
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"scripts": {
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json",
|
|
35
|
+
"pack:check": "npm pack --dry-run"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@chipmobilesdk/rn-core": "^0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@react-native-firebase/app": ">=26.0.0",
|
|
42
|
+
"@react-native-firebase/messaging": ">=26.0.0",
|
|
43
|
+
"react": ">=19",
|
|
44
|
+
"react-native": ">=0.85",
|
|
45
|
+
"react-native-localize": ">=3.7.0",
|
|
46
|
+
"react-native-mmkv": ">=4.0.0",
|
|
47
|
+
"react-native-notify-kit": ">=10.5.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@react-native-firebase/app": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"@react-native-firebase/messaging": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,173 +1,172 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sdk-rn-notification
|
|
3
|
-
description: "Local and push notifications via @chipmobilesdk/rn-notification: scheduling, identifiers, wall-clock vs absolute anchoring, idempotent reconciliation, recurrence windows, permissions, alarm-style repeating tones with a bounded window, tone labels in system settings, Android channel migration, tap routing, FCM. Use when scheduling a reminder or alarm, making a notification repeat until handled, naming a tone users see in settings, handling a notification tap, requesting notification permission, changing a channel, or debugging a notification that did not arrive."
|
|
4
|
-
metadata:
|
|
5
|
-
package: "@chipmobilesdk/rn-notification"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`
|
|
69
|
-
`
|
|
70
|
-
`
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
`
|
|
172
|
-
|
|
173
|
-
error.
|
|
1
|
+
---
|
|
2
|
+
name: sdk-rn-notification
|
|
3
|
+
description: "Local and push notifications via @chipmobilesdk/rn-notification: scheduling, identifiers, wall-clock vs absolute anchoring, idempotent reconciliation, recurrence windows, permissions, alarm-style repeating tones with a bounded window, tone labels in system settings, Android channel migration, tap routing, FCM. Use when scheduling a reminder or alarm, making a notification repeat until handled, naming a tone users see in settings, handling a notification tap, requesting notification permission, changing a channel, or debugging a notification that did not arrive."
|
|
4
|
+
metadata:
|
|
5
|
+
package: "@chipmobilesdk/rn-notification"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
disable-model-invocation: false
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# @chipmobilesdk/rn-notification
|
|
11
|
+
|
|
12
|
+
Scheduled local notifications with stable identity and idempotent reconciliation,
|
|
13
|
+
provider-agnostic push, permissions, declarative channels and tones, and tap routing that
|
|
14
|
+
survives a cold start.
|
|
15
|
+
|
|
16
|
+
## Use this when
|
|
17
|
+
|
|
18
|
+
- Scheduling, replacing, or cancelling a notification
|
|
19
|
+
- Reacting to a tap, an action button, or a dismissal
|
|
20
|
+
- Requesting notification or exact-alarm permission
|
|
21
|
+
- Declaring or changing a tone/channel, or enabling push
|
|
22
|
+
- Building an alarm-style reminder that repeats until the user handles it
|
|
23
|
+
- Debugging a notification that did not arrive, or arrived twice
|
|
24
|
+
|
|
25
|
+
**Not** for in-app banners or an inbox (this package renders nothing), for storing app
|
|
26
|
+
data (`@chipmobilesdk/rn-local-db`), or for translating text — resolve strings yourself.
|
|
27
|
+
|
|
28
|
+
## Read before writing any code
|
|
29
|
+
|
|
30
|
+
Three things cause most of the bugs, and none is visible in the type signatures:
|
|
31
|
+
|
|
32
|
+
1. **Identifiers are yours and must be derived from data.** `crypto.randomUUID()` means
|
|
33
|
+
you can never replace or cancel what you scheduled. Nothing is normalized, so
|
|
34
|
+
`Task:1` and `task:1` are two different notifications.
|
|
35
|
+
2. **Wall-clock is the default anchoring mode** — an 08:00 reminder stays 08:00 after the
|
|
36
|
+
user changes time zone. One case re-anchoring cannot fix: read
|
|
37
|
+
`### What re-anchoring cannot fix` before promising exact behaviour to anyone.
|
|
38
|
+
3. **Reconciliation is never automatic.** Maintenance steps registered on
|
|
39
|
+
`runtime.registry` (window refresh, re-anchoring, verification, permission re-read) run
|
|
40
|
+
on every foreground return, but the package never invents a desired set. Data changed
|
|
41
|
+
and you did not call `reconcile()` means the device is stale, by design. The steps are
|
|
42
|
+
not registered for you — they need your rules and expected ids.
|
|
43
|
+
|
|
44
|
+
Read `## Choosing identifiers`, `## Choosing an anchoring mode`, and
|
|
45
|
+
`## Maintenance versus reconciliation` before designing anything.
|
|
46
|
+
|
|
47
|
+
**A refusal must never break the user's work.** Permission denial is a degradation, not a
|
|
48
|
+
failure: the caller's operation still succeeds and the lost capability is reported
|
|
49
|
+
separately. Without exact-alarm capability notifications are still scheduled, flagged
|
|
50
|
+
`mayBeDelayed`. Never gate a user action on a permission answer.
|
|
51
|
+
|
|
52
|
+
## Android: two manifest entries you must add yourself
|
|
53
|
+
|
|
54
|
+
Consumer-owned; this package cannot ship either.
|
|
55
|
+
|
|
56
|
+
- **`SCHEDULE_EXACT_ALARM` arrives whether you want it or not** — the engine declares it
|
|
57
|
+
and manifest merging is transitive. It is store-restricted and needs a Play Console
|
|
58
|
+
declaration. Remove it with `tools:node="remove"` unless you need precise timing.
|
|
59
|
+
- **Backup exclusion** for the state record — a deduplication ledger restored onto another
|
|
60
|
+
device claims payloads were already delivered, silently suppressing real ones.
|
|
61
|
+
|
|
62
|
+
See `### Android` and `### Backup exclusion`.
|
|
63
|
+
|
|
64
|
+
## Public API
|
|
65
|
+
|
|
66
|
+
Three entrypoints. The **root** exports one factory per concern (`createScheduler`,
|
|
67
|
+
`createReconciler`, `createRecurrence`, `createPermissions`, `createChannels`,
|
|
68
|
+
`createInteractions`, `createSnapshot`), the maintenance steps and their registry, plus
|
|
69
|
+
`resolveTone`, `registerBackgroundNotificationHandler`, `resetState`, `clearOwner`,
|
|
70
|
+
`isNonFailure`, `REPEAT_ALERT_BOUNDS` and `DIAGNOSTIC_CODES`.
|
|
71
|
+
**`/push`** carries the push gate, FCM provider, instruction handler and sign-out —
|
|
72
|
+
**import it only if you enable push**, because an app that never touches this subpath ships
|
|
73
|
+
no provider code, no push permissions, and no entitlements. **`/testing`** carries the fake
|
|
74
|
+
engine and provider, the conformance suite and the fault injector.
|
|
75
|
+
|
|
76
|
+
Enumerate exact names from `node_modules/@chipmobilesdk/rn-notification/src/index.ts`
|
|
77
|
+
rather than guessing.
|
|
78
|
+
|
|
79
|
+
## Where the detail lives
|
|
80
|
+
|
|
81
|
+
`node_modules/@chipmobilesdk/rn-notification/README.md`:
|
|
82
|
+
|
|
83
|
+
| Need | Section |
|
|
84
|
+
|---|---|
|
|
85
|
+
| Install, peers, enabling push | `## Installation`, `### Enabling push (optional)` |
|
|
86
|
+
| Native setup | `### iOS`, `### Android`, `### Backup exclusion` |
|
|
87
|
+
| Naming notifications | `## Choosing identifiers` |
|
|
88
|
+
| Tones, alarms, labels, version bumps | `## Tones and channels` |
|
|
89
|
+
| Time zones and travel | `## Choosing an anchoring mode` |
|
|
90
|
+
| Automatic vs your responsibility | `## Maintenance versus reconciliation` |
|
|
91
|
+
| Converging the device to your data | `## Reconciliation`, `## Recurrence` |
|
|
92
|
+
| Permission timing and recovery | `## Permissions` |
|
|
93
|
+
| Taps, actions, cold start | `## Interaction` |
|
|
94
|
+
| Push and the backend contract | `## Push` |
|
|
95
|
+
| On-device storage and privacy | `## What is persisted`, `## Data behaviour …` |
|
|
96
|
+
| Limits and timing budgets | `## Budgets` |
|
|
97
|
+
| Error codes | `## Diagnostic identifiers` |
|
|
98
|
+
|
|
99
|
+
## Alarm-style tones (0.2.0+)
|
|
100
|
+
|
|
101
|
+
For a reminder that must not be missed — medication, a wake-up, an appointment — declare
|
|
102
|
+
the repeat on the **tone**, not on each notification:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
await channels.apply([{
|
|
106
|
+
id: 'medication',
|
|
107
|
+
name: t('notifications.medication.name'), // localized; users read this in settings
|
|
108
|
+
description: t('notifications.medication.hint'),
|
|
109
|
+
urgency: 'max',
|
|
110
|
+
sound: { name: 'chime' },
|
|
111
|
+
vibration: true,
|
|
112
|
+
bypassDoNotDisturb: true, // an alarm silenced by DND is not an alarm
|
|
113
|
+
repeatAlert: { forMs: 15 * 60 * 1000 }, // required bound, 1–60 minutes
|
|
114
|
+
timeSensitive: true,
|
|
115
|
+
version: 1, // unchanged — these need no bump
|
|
116
|
+
}]);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Four things to get right, none of them visible in the signatures:
|
|
120
|
+
|
|
121
|
+
1. **The duration is mandatory and bounded (1–60 min, `REPEAT_ALERT_BOUNDS`).** There is no
|
|
122
|
+
unbounded form; the type forbids it. Do not reach for a workaround — an alert with no
|
|
123
|
+
bound means a user comes home to a phone still ringing.
|
|
124
|
+
2. **Expiry stops the sound; it never removes the notification.** The user must still be
|
|
125
|
+
able to see what they missed. If you find yourself cancelling at expiry, you have
|
|
126
|
+
inverted the requirement. The window is a **floor**, not a precise stop — the entry
|
|
127
|
+
that ends the alert inherits normal delivery accuracy, so without exact alarms it
|
|
128
|
+
overruns (a declared 2 min measured 2m35s–3m00s). Never promise a caller an exact cut-off.
|
|
129
|
+
3. **Repeating tones need one setup step**, at module scope in `index.js`, never in a
|
|
130
|
+
component or an effect:
|
|
131
|
+
```js
|
|
132
|
+
import { registerBackgroundNotificationHandler } from '@chipmobilesdk/rn-notification';
|
|
133
|
+
registerBackgroundNotificationHandler({ engine });
|
|
134
|
+
```
|
|
135
|
+
Without it nothing ends the alert while the app is dead. `apply()` reports
|
|
136
|
+
`REPEAT_STOP_HANDLER_MISSING`. An app with no repeating tone needs none of this.
|
|
137
|
+
4. **iOS refuses repetition entirely** — no looping API exists, and the alert level that
|
|
138
|
+
would need an Apple-granted entitlement does not loop either. It is reported in
|
|
139
|
+
`result.repeatUnavailable`; the notification still delivers and sounds once. Never
|
|
140
|
+
promise a caller an iOS alarm loops. `timeSensitive` on iOS also needs the consuming
|
|
141
|
+
app's own Time Sensitive Notifications capability, or it degrades to `active`.
|
|
142
|
+
|
|
143
|
+
Read `## Tones and channels` before promising behaviour to anyone: it carries the
|
|
144
|
+
per-platform matrix and six limits that stay true (notification volume rather than alarm
|
|
145
|
+
volume on Android, the shade stopping an insistent sound, vendor battery management).
|
|
146
|
+
|
|
147
|
+
## Changing a tone: when a version bump is required
|
|
148
|
+
|
|
149
|
+
A bump means a **new channel**, and a new channel means the user loses every setting they
|
|
150
|
+
adjusted. Never bump one to fix a label.
|
|
151
|
+
|
|
152
|
+
| Change | Bump `version`? |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `name`, `description` | **No** — updated in place on existing installs |
|
|
155
|
+
| `repeatAlert`, `timeSensitive` | **No** — realized per notification |
|
|
156
|
+
| `urgency`, `sound`, `vibration`, `badge`, `groupId`, `bypassDoNotDisturb` | **Yes** |
|
|
157
|
+
|
|
158
|
+
`apply()` reports a label correction as `labelsRefreshed`, distinct from `created`,
|
|
159
|
+
`migrated`, and `unchanged`.
|
|
160
|
+
|
|
161
|
+
## Debugging "it did not arrive"
|
|
162
|
+
|
|
163
|
+
First check the installed version: **before 0.2.1** the state record never persisted on a
|
|
164
|
+
device (it used react-native-mmkv's v3 constructor, absent in v4), so deduplication and
|
|
165
|
+
cold-start tap buffering only lasted one session. Require `0.2.1` or later.
|
|
166
|
+
|
|
167
|
+
Capture a state snapshot (`createSnapshot`) before guessing — permissions, applied channel
|
|
168
|
+
versions, pending set, last maintenance cycle, discrepancies and state health, in one
|
|
169
|
+
place. Some codes are **reported outcomes, not failures** (`TRUNCATED_OVER_CEILING`,
|
|
170
|
+
`EXACT_ALARM_UNAVAILABLE`, `PENDING_SET_DISCREPANCY`, `BURST_THRESHOLD_EXCEEDED`,
|
|
171
|
+
`PAYLOAD_VERSION_SKEW`); use `isNonFailure(code)` rather than treating every code as an
|
|
172
|
+
error.
|