react-native-alarms 0.0.5 → 0.0.7
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/.husky/commit-msg +1 -0
- package/.husky/pre-commit +0 -0
- package/AGENTS.md +291 -0
- package/AlarmService.d.ts +141 -32
- package/AlarmService.ts +238 -39
- package/LICENSE.md +21 -0
- package/README.md +159 -9
- package/android/app/src/main/java/com/reactnativealarm/alarm/AlarmData.kt +154 -8
- package/android/app/src/main/java/com/reactnativealarm/alarm/AlarmModule.kt +2 -2
- package/android/app/src/main/java/com/reactnativealarm/alarm/AlarmRingActivity.kt +3 -2
- package/android/local.properties +8 -0
- package/commitlint.config.mjs +1 -0
- package/index.d.ts +3 -3
- package/index.ts +5 -5
- package/ios/alarm/AlarmData.swift +20 -6
- package/ios/alarm/AlarmKitBackend.swift +43 -40
- package/ios/alarm/AlarmModule.swift +68 -64
- package/ios/alarm/AlarmRingPresenter.swift +26 -19
- package/ios/alarm/AlarmStore.swift +9 -9
- package/ios/alarm/NotificationBackend.swift +33 -33
- package/package.json +5 -2
- package/specs/NativeAlarm.ts +120 -71
- package/src/assets/recording.mp4 +0 -0
- package/src/assets/sample-1.png +0 -0
- package/src/assets/sample-2.png +0 -0
- package/src/assets/sample-3.png +0 -0
- package/src/screens/index.ts +0 -0
- package/src/types/index.ts +246 -0
- package/src/utils/AlarmRegistry.ts +57 -0
- package/src/utils/index.ts +1 -0
package/AlarmService.ts
CHANGED
|
@@ -1,31 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* High-level alarm management API.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Provides a convenient wrapper around the native `Alarm` module for
|
|
5
|
+
* scheduling and managing alarms, handling permissions, querying alarm
|
|
6
|
+
* state, and subscribing to native alarm events.
|
|
7
|
+
*
|
|
8
|
+
* This API can also be used for time-sensitive reminders and custom
|
|
9
|
+
* scheduled experiences supported by the native alarm presentation flow.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { AlarmService } from 'react-native-alarms/service';
|
|
14
|
+
*
|
|
15
|
+
* const permission = await AlarmService.checkPermission();
|
|
16
|
+
*
|
|
17
|
+
* if (permission.status !== 'granted') {
|
|
18
|
+
* await AlarmService.requestPermission();
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* const alarmId = await AlarmService.scheduleAlarm({
|
|
22
|
+
* timestamp: Date.now() + 60_000,
|
|
23
|
+
* title: 'Reminder',
|
|
24
|
+
* body: 'Your scheduled event is ready.',
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
6
27
|
*/
|
|
7
|
-
|
|
8
28
|
import { NativeEventEmitter, NativeModules } from 'react-native';
|
|
9
29
|
import type {
|
|
10
30
|
AlarmConfig,
|
|
11
|
-
|
|
31
|
+
AlarmProp,
|
|
12
32
|
AlarmPermission,
|
|
13
33
|
AlarmEvent,
|
|
14
|
-
} from './
|
|
34
|
+
} from './src/types';
|
|
35
|
+
import { AlarmRegistry } from './src/utils';
|
|
15
36
|
|
|
16
37
|
const { Alarm: NativeAlarm } = NativeModules;
|
|
17
38
|
|
|
18
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the native alarm module when it is linked.
|
|
41
|
+
*
|
|
42
|
+
* The module is loaded conditionally so importing the service does not
|
|
43
|
+
* immediately fail when the native module is unavailable.
|
|
44
|
+
*/
|
|
19
45
|
const alarm = NativeAlarm ? require('./specs/NativeAlarm').default : null;
|
|
20
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Native event emitter used to subscribe to alarm lifecycle events.
|
|
49
|
+
*/
|
|
21
50
|
const alarmEmitter = NativeAlarm ? new NativeEventEmitter(NativeAlarm) : null;
|
|
22
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Callback invoked when a native alarm event is received.
|
|
54
|
+
*
|
|
55
|
+
* @param alarm Event data emitted by the native alarm module.
|
|
56
|
+
*/
|
|
23
57
|
type AlarmEventListener = (alarm: AlarmEvent) => void;
|
|
24
58
|
|
|
25
59
|
/**
|
|
26
|
-
* High-level
|
|
60
|
+
* High-level service for scheduling and managing native alarms.
|
|
61
|
+
*
|
|
62
|
+
* All methods are static, so the service can be used directly without
|
|
63
|
+
* creating an instance.
|
|
64
|
+
*
|
|
65
|
+
* The service maintains JavaScript event listeners and forwards events
|
|
66
|
+
* received from the native module to registered callbacks.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const unsubscribe = AlarmService.onAlarmFired((alarm) => {
|
|
71
|
+
* console.log('Alarm fired:', alarm.title);
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Remove this listener when it is no longer needed.
|
|
75
|
+
* unsubscribe();
|
|
76
|
+
* ```
|
|
27
77
|
*/
|
|
28
78
|
export class AlarmService {
|
|
79
|
+
/**
|
|
80
|
+
* Registered JavaScript listeners grouped by event type.
|
|
81
|
+
*
|
|
82
|
+
* A `Set` prevents the same callback reference from being registered
|
|
83
|
+
* more than once for the same event.
|
|
84
|
+
*/
|
|
29
85
|
private static listeners: {
|
|
30
86
|
fired: Set<AlarmEventListener>;
|
|
31
87
|
dismissed: Set<AlarmEventListener>;
|
|
@@ -36,64 +92,113 @@ export class AlarmService {
|
|
|
36
92
|
snoozed: new Set(),
|
|
37
93
|
};
|
|
38
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Native event subscriptions created by {@link initialize}.
|
|
97
|
+
*/
|
|
39
98
|
private static subscriptions: any[] = [];
|
|
40
99
|
|
|
41
100
|
/**
|
|
42
|
-
*
|
|
101
|
+
* Initializes native event forwarding.
|
|
102
|
+
*
|
|
103
|
+
* Registers listeners for alarm firing, dismissal, and snoozing.
|
|
104
|
+
* Calling this method when subscriptions already exist does not
|
|
105
|
+
* register duplicate native subscriptions.
|
|
106
|
+
*
|
|
107
|
+
* This method is called automatically by the event subscription helpers.
|
|
43
108
|
*/
|
|
44
|
-
static initialize() {
|
|
109
|
+
static initialize(): void {
|
|
45
110
|
if (!alarmEmitter || !this.subscriptions.length) {
|
|
46
111
|
this.subscriptions = [
|
|
47
112
|
alarmEmitter?.addListener('onAlarmFired', (data: any) => {
|
|
48
|
-
this.listeners.fired.forEach(listener =>
|
|
113
|
+
this.listeners.fired.forEach(listener =>
|
|
114
|
+
listener(data as AlarmEvent),
|
|
115
|
+
);
|
|
49
116
|
}),
|
|
50
117
|
alarmEmitter?.addListener('onAlarmDismissed', (data: any) => {
|
|
51
|
-
this.listeners.dismissed.forEach(listener =>
|
|
118
|
+
this.listeners.dismissed.forEach(listener =>
|
|
119
|
+
listener(data as AlarmEvent),
|
|
120
|
+
);
|
|
52
121
|
}),
|
|
53
122
|
alarmEmitter?.addListener('onAlarmSnoozed', (data: any) => {
|
|
54
|
-
this.listeners.snoozed.forEach(listener =>
|
|
123
|
+
this.listeners.snoozed.forEach(listener =>
|
|
124
|
+
listener(data as AlarmEvent),
|
|
125
|
+
);
|
|
55
126
|
}),
|
|
56
127
|
].filter(Boolean);
|
|
57
128
|
}
|
|
58
129
|
}
|
|
59
130
|
|
|
60
131
|
/**
|
|
61
|
-
*
|
|
132
|
+
* Removes native event subscriptions created by the service.
|
|
133
|
+
*
|
|
134
|
+
* This stops forwarding native events to JavaScript listeners until
|
|
135
|
+
* {@link initialize} is called again.
|
|
136
|
+
*
|
|
137
|
+
* Registered JavaScript callbacks remain in their respective sets.
|
|
138
|
+
* Use the unsubscribe functions returned by the event helpers to
|
|
139
|
+
* remove individual callbacks.
|
|
62
140
|
*/
|
|
63
|
-
static cleanup() {
|
|
141
|
+
static cleanup(): void {
|
|
64
142
|
this.subscriptions.forEach(sub => sub?.remove());
|
|
65
143
|
this.subscriptions = [];
|
|
66
144
|
}
|
|
67
145
|
|
|
68
146
|
/**
|
|
69
|
-
*
|
|
147
|
+
* Subscribes to alarms when they fire.
|
|
148
|
+
*
|
|
149
|
+
* The service initializes native event forwarding automatically.
|
|
150
|
+
*
|
|
151
|
+
* @param listener Callback invoked with the fired alarm's event data.
|
|
152
|
+
* @returns A function that unregisters this callback.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```typescript
|
|
156
|
+
* const unsubscribe = AlarmService.onAlarmFired(alarm => {
|
|
157
|
+
* console.log('Fired:', alarm.alarmId, alarm.title);
|
|
158
|
+
* });
|
|
159
|
+
*
|
|
160
|
+
* // Later:
|
|
161
|
+
* unsubscribe();
|
|
162
|
+
* ```
|
|
70
163
|
*/
|
|
71
|
-
static onAlarmFired(listener: AlarmEventListener) {
|
|
164
|
+
static onAlarmFired(listener: AlarmEventListener): () => boolean {
|
|
72
165
|
this.initialize();
|
|
73
166
|
this.listeners.fired.add(listener);
|
|
167
|
+
|
|
74
168
|
return () => this.listeners.fired.delete(listener);
|
|
75
169
|
}
|
|
76
170
|
|
|
77
171
|
/**
|
|
78
|
-
*
|
|
172
|
+
* Subscribes to alarms when they are dismissed.
|
|
173
|
+
*
|
|
174
|
+
* @param listener Callback invoked with the dismissed alarm's event data.
|
|
175
|
+
* @returns A function that unregisters this callback.
|
|
79
176
|
*/
|
|
80
|
-
static onAlarmDismissed(listener: AlarmEventListener) {
|
|
177
|
+
static onAlarmDismissed(listener: AlarmEventListener): () => boolean {
|
|
81
178
|
this.initialize();
|
|
82
179
|
this.listeners.dismissed.add(listener);
|
|
180
|
+
|
|
83
181
|
return () => this.listeners.dismissed.delete(listener);
|
|
84
182
|
}
|
|
85
183
|
|
|
86
184
|
/**
|
|
87
|
-
*
|
|
185
|
+
* Subscribes to alarms when they are snoozed.
|
|
186
|
+
*
|
|
187
|
+
* @param listener Callback invoked with the snoozed alarm's event data.
|
|
188
|
+
* @returns A function that unregisters this callback.
|
|
88
189
|
*/
|
|
89
|
-
static onAlarmSnoozed(listener: AlarmEventListener) {
|
|
190
|
+
static onAlarmSnoozed(listener: AlarmEventListener): () => boolean {
|
|
90
191
|
this.initialize();
|
|
91
192
|
this.listeners.snoozed.add(listener);
|
|
193
|
+
|
|
92
194
|
return () => this.listeners.snoozed.delete(listener);
|
|
93
195
|
}
|
|
94
196
|
|
|
95
197
|
/**
|
|
96
|
-
*
|
|
198
|
+
* Retrieves the current alarm permission and capability status.
|
|
199
|
+
*
|
|
200
|
+
* @returns A promise resolving to the current permission status.
|
|
201
|
+
* @throws Error if the native alarm module is not linked.
|
|
97
202
|
*/
|
|
98
203
|
static async checkPermission(): Promise<AlarmPermission> {
|
|
99
204
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -101,7 +206,12 @@ export class AlarmService {
|
|
|
101
206
|
}
|
|
102
207
|
|
|
103
208
|
/**
|
|
104
|
-
*
|
|
209
|
+
* Requests the permissions required for alarm scheduling.
|
|
210
|
+
*
|
|
211
|
+
* The permissions requested depend on the platform and its capabilities.
|
|
212
|
+
*
|
|
213
|
+
* @returns A promise resolving to the updated permission status.
|
|
214
|
+
* @throws Error if the native alarm module is not linked.
|
|
105
215
|
*/
|
|
106
216
|
static async requestPermission(): Promise<AlarmPermission> {
|
|
107
217
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -109,15 +219,42 @@ export class AlarmService {
|
|
|
109
219
|
}
|
|
110
220
|
|
|
111
221
|
/**
|
|
112
|
-
*
|
|
222
|
+
* Schedules a new alarm or time-based reminder.
|
|
223
|
+
*
|
|
224
|
+
* @param config Configuration containing the trigger timestamp,
|
|
225
|
+
* title, optional message, repeat settings, and custom data.
|
|
226
|
+
* @returns A promise resolving to the scheduled alarm ID.
|
|
227
|
+
* @throws Error if the native alarm module is not linked.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const alarmId = await AlarmService.scheduleAlarm({
|
|
232
|
+
* timestamp: Date.now() + 5 * 60_000,
|
|
233
|
+
* title: 'Take a break',
|
|
234
|
+
* body: 'Your scheduled break starts now.',
|
|
235
|
+
* data: JSON.stringify({ type: 'break-reminder' }),
|
|
236
|
+
* });
|
|
237
|
+
* ```
|
|
113
238
|
*/
|
|
114
239
|
static async scheduleAlarm(config: AlarmConfig): Promise<string> {
|
|
115
|
-
if (!alarm)
|
|
116
|
-
|
|
240
|
+
if (!alarm) {
|
|
241
|
+
throw new Error('Alarm module not linked');
|
|
242
|
+
}
|
|
243
|
+
const componentName = AlarmRegistry.resolve(config.presentation?.type);
|
|
244
|
+
return alarm.scheduleAlarm({
|
|
245
|
+
...config,
|
|
246
|
+
componentName,
|
|
247
|
+
componentProps: config.presentation?.props ?? {},
|
|
248
|
+
});
|
|
117
249
|
}
|
|
118
250
|
|
|
119
251
|
/**
|
|
120
|
-
*
|
|
252
|
+
* Updates an existing scheduled alarm.
|
|
253
|
+
*
|
|
254
|
+
* @param alarmId Identifier of the alarm to update.
|
|
255
|
+
* @param config Updated alarm configuration.
|
|
256
|
+
* @returns A promise resolving to `true` if the update succeeds.
|
|
257
|
+
* @throws Error if the native alarm module is not linked.
|
|
121
258
|
*/
|
|
122
259
|
static async updateAlarm(
|
|
123
260
|
alarmId: string,
|
|
@@ -128,7 +265,11 @@ export class AlarmService {
|
|
|
128
265
|
}
|
|
129
266
|
|
|
130
267
|
/**
|
|
131
|
-
*
|
|
268
|
+
* Cancels a specific scheduled alarm.
|
|
269
|
+
*
|
|
270
|
+
* @param alarmId Identifier of the alarm to cancel.
|
|
271
|
+
* @returns A promise resolving to whether cancellation succeeded.
|
|
272
|
+
* @throws Error if the native alarm module is not linked.
|
|
132
273
|
*/
|
|
133
274
|
static async cancelAlarm(alarmId: string): Promise<boolean> {
|
|
134
275
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -136,7 +277,10 @@ export class AlarmService {
|
|
|
136
277
|
}
|
|
137
278
|
|
|
138
279
|
/**
|
|
139
|
-
*
|
|
280
|
+
* Cancels all scheduled alarms managed by the native module.
|
|
281
|
+
*
|
|
282
|
+
* @returns A promise that resolves when the operation completes.
|
|
283
|
+
* @throws Error if the native alarm module is not linked.
|
|
140
284
|
*/
|
|
141
285
|
static async cancelAllAlarms(): Promise<void> {
|
|
142
286
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -144,7 +288,11 @@ export class AlarmService {
|
|
|
144
288
|
}
|
|
145
289
|
|
|
146
290
|
/**
|
|
147
|
-
*
|
|
291
|
+
* Stops a currently ringing alarm.
|
|
292
|
+
*
|
|
293
|
+
* @param alarmId Identifier of the ringing alarm.
|
|
294
|
+
* @returns A promise that resolves when the operation completes.
|
|
295
|
+
* @throws Error if the native alarm module is not linked.
|
|
148
296
|
*/
|
|
149
297
|
static async stopAlarm(alarmId: string): Promise<void> {
|
|
150
298
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -152,7 +300,17 @@ export class AlarmService {
|
|
|
152
300
|
}
|
|
153
301
|
|
|
154
302
|
/**
|
|
155
|
-
*
|
|
303
|
+
* Snoozes a currently ringing alarm.
|
|
304
|
+
*
|
|
305
|
+
* @param alarmId Identifier of the ringing alarm.
|
|
306
|
+
* @param minutes Snooze duration in minutes. Defaults to `5`.
|
|
307
|
+
* @returns A promise that resolves when the operation completes.
|
|
308
|
+
* @throws Error if the native alarm module is not linked.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* await AlarmService.snoozeAlarm(alarmId, 10);
|
|
313
|
+
* ```
|
|
156
314
|
*/
|
|
157
315
|
static async snoozeAlarm(alarmId: string, minutes?: number): Promise<void> {
|
|
158
316
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
@@ -160,37 +318,78 @@ export class AlarmService {
|
|
|
160
318
|
}
|
|
161
319
|
|
|
162
320
|
/**
|
|
163
|
-
*
|
|
321
|
+
* Retrieves the alarm that is currently ringing.
|
|
322
|
+
*
|
|
323
|
+
* @returns A promise resolving to the ringing alarm, or `null`
|
|
324
|
+
* if no alarm is currently ringing.
|
|
325
|
+
* @throws Error if the native alarm module is not linked.
|
|
164
326
|
*/
|
|
165
|
-
static async getRingingAlarm(): Promise<
|
|
327
|
+
static async getRingingAlarm(): Promise<AlarmProp | null> {
|
|
166
328
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
167
329
|
return alarm.getRingingAlarm();
|
|
168
330
|
}
|
|
169
331
|
|
|
170
332
|
/**
|
|
171
|
-
*
|
|
333
|
+
* Retrieves a scheduled alarm by its identifier.
|
|
334
|
+
*
|
|
335
|
+
* @param alarmId Identifier of the alarm to retrieve.
|
|
336
|
+
* @returns A promise resolving to the alarm, or `null` if not found.
|
|
337
|
+
* @throws Error if the native alarm module is not linked.
|
|
172
338
|
*/
|
|
173
|
-
static async getAlarm(alarmId: string): Promise<
|
|
339
|
+
static async getAlarm(alarmId: string): Promise<AlarmProp | null> {
|
|
174
340
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
175
341
|
return alarm.getAlarm(alarmId);
|
|
176
342
|
}
|
|
177
343
|
|
|
178
344
|
/**
|
|
179
|
-
*
|
|
345
|
+
* Retrieves all alarms managed by the native module.
|
|
346
|
+
*
|
|
347
|
+
* @returns A promise resolving to the list of scheduled alarms.
|
|
348
|
+
* @throws Error if the native alarm module is not linked.
|
|
180
349
|
*/
|
|
181
|
-
static async getAllAlarms(): Promise<
|
|
350
|
+
static async getAllAlarms(): Promise<AlarmProp[]> {
|
|
182
351
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
183
352
|
return alarm.getAllAlarms();
|
|
184
353
|
}
|
|
185
354
|
|
|
186
355
|
/**
|
|
187
|
-
*
|
|
356
|
+
* Checks whether an alarm is currently scheduled.
|
|
357
|
+
*
|
|
358
|
+
* @param alarmId Identifier of the alarm to check.
|
|
359
|
+
* @returns A promise resolving to `true` if scheduled, otherwise `false`.
|
|
360
|
+
* @throws Error if the native alarm module is not linked.
|
|
188
361
|
*/
|
|
189
362
|
static async isScheduled(alarmId: string): Promise<boolean> {
|
|
190
363
|
if (!alarm) throw new Error('Alarm module not linked');
|
|
191
364
|
return alarm.isScheduled(alarmId);
|
|
192
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Opens the device manufacturer's Auto Start settings screen.
|
|
369
|
+
*
|
|
370
|
+
* This is primarily intended for Android devices whose manufacturers
|
|
371
|
+
* provide additional background-execution settings.
|
|
372
|
+
*
|
|
373
|
+
* On iOS, the native implementation resolves to `false`.
|
|
374
|
+
*
|
|
375
|
+
* @returns A promise resolving to `true` if the settings screen was
|
|
376
|
+
* opened, or `false` if unsupported or unavailable.
|
|
377
|
+
* @throws Error if the native alarm module is not linked.
|
|
378
|
+
*/
|
|
379
|
+
static async openAutoStartSettings(): Promise<boolean> {
|
|
380
|
+
if (!alarm) throw new Error('Alarm module not linked');
|
|
381
|
+
return alarm.openAutoStartSettings();
|
|
382
|
+
}
|
|
193
383
|
}
|
|
194
384
|
|
|
195
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Default export for convenient service access.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```typescript
|
|
390
|
+
* import AlarmService from 'react-native-alarms/service';
|
|
391
|
+
*
|
|
392
|
+
* const alarms = await AlarmService.getAllAlarms();
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
196
395
|
export default AlarmService;
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 react-native-alarm contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# React Native Alarms 🔔
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A cross-platform native scheduling, reminder, and wake-up system for React Native. Schedule time-sensitive events on Android and iOS, deliver reminders, and present a registered custom React Native screen when a scheduled event fires—using one unified API.
|
|
4
|
+
|
|
5
|
+
Although it can power a traditional alarm clock, react-native-alarms is also useful for exact-time reminders, scheduled check-ins, custom lock-screen experiences, and time-based app workflows. Build the experience around your own UI and event data instead of being limited to a standard alarm screen.
|
|
6
|
+
|
|
7
|
+
> **Platform note:** The operating system controls background delivery and screen presentation. Exact timing, full-screen presentation, and behavior when an app is terminated depend on platform version, permissions, device settings, and the native scheduling mechanism. This library does not provide unrestricted background JavaScript execution.
|
|
8
|
+
|
|
4
9
|
|
|
5
10
|
[](https://badge.fury.io/js/react-native-alarms)
|
|
6
11
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -65,6 +70,44 @@ AppRegistry.registerComponent('AlarmRing', () => AlarmRingScreen);
|
|
|
65
70
|
Call `Alarm.stopAlarm(alarmId)` or `Alarm.snoozeAlarm(alarmId, minutes)` from its
|
|
66
71
|
buttons; the screen closes itself.
|
|
67
72
|
|
|
73
|
+
#### Custom presentation component
|
|
74
|
+
|
|
75
|
+
You can register **multiple** ring screens and choose which one fires per alarm using the `presentation` field in `AlarmConfig`:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// index.js
|
|
79
|
+
import { AppRegistry } from 'react-native';
|
|
80
|
+
import { AlarmRingScreen } from './src/AlarmRingScreen';
|
|
81
|
+
import { ReminderScreen } from './src/ReminderScreen';
|
|
82
|
+
|
|
83
|
+
AppRegistry.registerComponent('AlarmRing', () => AlarmRingScreen); // default
|
|
84
|
+
AppRegistry.registerComponent('Reminder', () => ReminderScreen); // custom
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { AlarmRegistry, AlarmService } from 'react-native-alarms/service';
|
|
89
|
+
|
|
90
|
+
// 1. Map your key to the registered component name
|
|
91
|
+
AlarmRegistry.register('reminder', 'Reminder');
|
|
92
|
+
|
|
93
|
+
// 2. Pass it when scheduling
|
|
94
|
+
await AlarmService.scheduleAlarm({
|
|
95
|
+
timestamp: Date.now() + 60_000,
|
|
96
|
+
title: 'Standup',
|
|
97
|
+
presentation: {
|
|
98
|
+
type: 'reminder', // resolves to the 'Reminder' component
|
|
99
|
+
props: { color: '#6200EE' } // forwarded to the screen as componentProps
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The native layer maps `presentation` to two internal fields:
|
|
105
|
+
|
|
106
|
+
| Internal field | Type | Default | Description |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `componentName` | `string` | `"AlarmRing"` | Registered `AppRegistry` component name shown when the alarm rings. |
|
|
109
|
+
| `componentProps` / `componentPropsJSON` | `object` / `string` | `{}` | Key-value props forwarded to the ring screen on **both platforms**. On Android they arrive as top-level bundle extras and a nested `componentProps` map; on iOS as a parsed `[String: Any]` dictionary. |
|
|
110
|
+
|
|
68
111
|
### Direct module access
|
|
69
112
|
|
|
70
113
|
```ts
|
|
@@ -179,9 +222,28 @@ type AlarmConfig = {
|
|
|
179
222
|
snoozeMinutes?: number; // Snooze duration in minutes
|
|
180
223
|
repeat?: AlarmRepeat; // Repeat configuration
|
|
181
224
|
data?: string; // Custom JSON string payload
|
|
225
|
+
presentation?: AlarmPresentation; // Custom ring screen & props
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
type AlarmPresentation = {
|
|
229
|
+
/** Registered presentation type (key passed to AlarmRegistry.register). */
|
|
230
|
+
type?: string;
|
|
231
|
+
/**
|
|
232
|
+
* Props forwarded to the ring screen component.
|
|
233
|
+
* Available as `componentProps` on both Android and iOS.
|
|
234
|
+
*/
|
|
235
|
+
props?: Record<string, unknown>;
|
|
182
236
|
};
|
|
183
237
|
```
|
|
184
238
|
|
|
239
|
+
> **How the native layer stores these:** `AlarmConfig.presentation` is resolved by `AlarmService` before being handed off to the native module. The resulting fields stored in the native `AlarmData` model are:
|
|
240
|
+
>
|
|
241
|
+
> | Field | Type | Default | Description |
|
|
242
|
+
> |---|---|---|---|
|
|
243
|
+
> | `componentName` | `string` | `"AlarmRing"` | The `AppRegistry` name of the component shown at ring time. Derived from `AlarmRegistry.resolve(presentation.type)`. |
|
|
244
|
+
> | `componentProps` | `object` | `{}` | Parsed props object (from `presentation.props`). Available on both platforms at ring time as `componentProps`. |
|
|
245
|
+
> | `componentPropsJSON` | `string` | `"{}"` | JSON-serialised representation of `componentProps`, persisted alongside the alarm so props survive a device reboot. |
|
|
246
|
+
|
|
185
247
|
#### `AlarmRepeat`
|
|
186
248
|
Repeat configuration for recurring alarms.
|
|
187
249
|
|
|
@@ -361,6 +423,22 @@ const styles = StyleSheet.create({
|
|
|
361
423
|
});
|
|
362
424
|
```
|
|
363
425
|
|
|
426
|
+
## 📸 Screenshots, Demos & Examples
|
|
427
|
+
|
|
428
|
+
Explore how `react-native-alarms` works on Android and iOS.
|
|
429
|
+
|
|
430
|
+
### 🖼️ Screenshots
|
|
431
|
+
|
|
432
|
+
| Sample 1 | Sample 2 | Sample 3 |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| <img src="src/assets/sample-1.png" width="180" alt="Sample 1" /> | <img src="src/assets/sample-2.png" width="180" alt="Sample 2" /> | <img src="src/assets/sample-3.png" width="180" alt="Sample 3" /> |
|
|
435
|
+
|
|
436
|
+
### 🎥 Demo Video
|
|
437
|
+
|
|
438
|
+
Watch the library in action, including alarm scheduling, ringing, and Stop/Snooze interactions.
|
|
439
|
+
|
|
440
|
+
[▶️ Watch the React Native Alarms Demo](src/assets/recording.mp4)
|
|
441
|
+
|
|
364
442
|
## Troubleshooting
|
|
365
443
|
|
|
366
444
|
### "Alarm Native module is not linked"
|
|
@@ -417,27 +495,99 @@ For issues and questions, please visit:
|
|
|
417
495
|
|
|
418
496
|
The library supports completely custom alarm UI screens with custom data payloads.
|
|
419
497
|
|
|
420
|
-
###
|
|
498
|
+
### Using `componentName` and `componentProps`
|
|
499
|
+
|
|
500
|
+
The `presentation` field lets you choose **which registered component** is displayed and pass **arbitrary props** directly to it — without encoding everything inside a JSON `data` string.
|
|
501
|
+
|
|
502
|
+
#### 1. Register your screens
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
// index.js
|
|
506
|
+
import { AppRegistry } from 'react-native';
|
|
507
|
+
import { AlarmRingScreen } from './src/AlarmRingScreen';
|
|
508
|
+
import { MeetingAlarmScreen } from './src/MeetingAlarmScreen';
|
|
509
|
+
|
|
510
|
+
AppRegistry.registerComponent('AlarmRing', () => AlarmRingScreen); // default fallback
|
|
511
|
+
AppRegistry.registerComponent('MeetingAlarm', () => MeetingAlarmScreen); // custom
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### 2. Map a key to the component name
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
import { AlarmRegistry } from 'react-native-alarms';
|
|
518
|
+
|
|
519
|
+
AlarmRegistry.register('meeting', 'MeetingAlarm');
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### 3. Schedule with `presentation`
|
|
421
523
|
|
|
422
524
|
```typescript
|
|
423
|
-
import { NativeModules } from 'react-native';
|
|
424
525
|
import { AlarmService } from 'react-native-alarms/service';
|
|
425
526
|
|
|
426
|
-
|
|
427
|
-
|
|
527
|
+
await AlarmService.scheduleAlarm({
|
|
528
|
+
timestamp: Date.now() + 5 * 60 * 1000,
|
|
529
|
+
title: 'Team standup',
|
|
530
|
+
presentation: {
|
|
531
|
+
type: 'meeting', // resolves to 'MeetingAlarm'
|
|
532
|
+
props: {
|
|
533
|
+
roomName: 'Zoom Room A',
|
|
534
|
+
meetingUrl: 'https://zoom.us/j/123',
|
|
535
|
+
accentColor: '#6200EE',
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### 4. Receive `componentProps` in your screen
|
|
542
|
+
|
|
543
|
+
The props passed via `presentation.props` are forwarded to the ring screen on both platforms:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
// MeetingAlarmScreen.tsx
|
|
547
|
+
type Props = {
|
|
548
|
+
alarmId: string;
|
|
549
|
+
title: string;
|
|
550
|
+
snoozeEnabled: boolean;
|
|
551
|
+
snoozeMinutes: number;
|
|
552
|
+
data?: string;
|
|
553
|
+
// componentProps fields arrive as top-level props:
|
|
554
|
+
roomName?: string;
|
|
555
|
+
meetingUrl?: string;
|
|
556
|
+
accentColor?: string;
|
|
557
|
+
};
|
|
428
558
|
|
|
559
|
+
export function MeetingAlarmScreen({
|
|
560
|
+
alarmId,
|
|
561
|
+
title,
|
|
562
|
+
snoozeEnabled,
|
|
563
|
+
snoozeMinutes,
|
|
564
|
+
roomName,
|
|
565
|
+
meetingUrl,
|
|
566
|
+
accentColor = '#007AFF',
|
|
567
|
+
}: Props) {
|
|
429
568
|
return (
|
|
430
|
-
<View style={{ backgroundColor:
|
|
431
|
-
<Text>{title}</Text>
|
|
569
|
+
<View style={{ flex: 1, backgroundColor: accentColor }}>
|
|
570
|
+
<Text style={{ fontSize: 28, color: '#fff' }}>{title}</Text>
|
|
571
|
+
<Text style={{ color: '#fff' }}>{roomName}</Text>
|
|
572
|
+
|
|
573
|
+
{snoozeEnabled && (
|
|
574
|
+
<TouchableOpacity onPress={() => AlarmService.snoozeAlarm(alarmId, snoozeMinutes)}>
|
|
575
|
+
<Text>Snooze {snoozeMinutes} min</Text>
|
|
576
|
+
</TouchableOpacity>
|
|
577
|
+
)}
|
|
432
578
|
<TouchableOpacity onPress={() => AlarmService.stopAlarm(alarmId)}>
|
|
433
|
-
<Text>
|
|
579
|
+
<Text>Dismiss</Text>
|
|
434
580
|
</TouchableOpacity>
|
|
435
581
|
</View>
|
|
436
582
|
);
|
|
437
583
|
}
|
|
438
584
|
```
|
|
439
585
|
|
|
440
|
-
|
|
586
|
+
> **Platform behaviour**
|
|
587
|
+
> - **Android**: `componentProps` entries are flattened into the launch `Bundle` alongside standard fields (`alarmId`, `title`, etc.), and also available as a nested `componentProps` bundle. The `AlarmRingActivity` uses `componentName` to resolve which registered React root to display.
|
|
588
|
+
> - **iOS**: `componentProps` (parsed from `componentPropsJSON`) is merged into the surface's initial properties passed to the React component.
|
|
589
|
+
|
|
590
|
+
### Passing Custom Data (legacy approach)
|
|
441
591
|
|
|
442
592
|
```typescript
|
|
443
593
|
await AlarmService.scheduleAlarm({
|