omgkit 2.13.0 → 2.16.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 +129 -10
- package/package.json +2 -2
- package/plugin/agents/api-designer.md +5 -0
- package/plugin/agents/architect.md +8 -0
- package/plugin/agents/brainstormer.md +4 -0
- package/plugin/agents/cicd-manager.md +6 -0
- package/plugin/agents/code-reviewer.md +6 -0
- package/plugin/agents/copywriter.md +2 -0
- package/plugin/agents/data-engineer.md +255 -0
- package/plugin/agents/database-admin.md +10 -0
- package/plugin/agents/debugger.md +10 -0
- package/plugin/agents/devsecops.md +314 -0
- package/plugin/agents/docs-manager.md +4 -0
- package/plugin/agents/domain-decomposer.md +181 -0
- package/plugin/agents/embedded-systems.md +397 -0
- package/plugin/agents/fullstack-developer.md +12 -0
- package/plugin/agents/game-systems-designer.md +375 -0
- package/plugin/agents/git-manager.md +10 -0
- package/plugin/agents/journal-writer.md +2 -0
- package/plugin/agents/ml-engineer.md +284 -0
- package/plugin/agents/observability-engineer.md +353 -0
- package/plugin/agents/oracle.md +9 -0
- package/plugin/agents/performance-engineer.md +290 -0
- package/plugin/agents/pipeline-architect.md +6 -0
- package/plugin/agents/planner.md +12 -0
- package/plugin/agents/platform-engineer.md +325 -0
- package/plugin/agents/project-manager.md +3 -0
- package/plugin/agents/researcher.md +5 -0
- package/plugin/agents/scientific-computing.md +426 -0
- package/plugin/agents/scout.md +3 -0
- package/plugin/agents/security-auditor.md +7 -0
- package/plugin/agents/sprint-master.md +17 -0
- package/plugin/agents/tester.md +10 -0
- package/plugin/agents/ui-ux-designer.md +12 -0
- package/plugin/agents/vulnerability-scanner.md +6 -0
- package/plugin/commands/data/pipeline.md +47 -0
- package/plugin/commands/data/quality.md +49 -0
- package/plugin/commands/domain/analyze.md +34 -0
- package/plugin/commands/domain/map.md +41 -0
- package/plugin/commands/game/balance.md +56 -0
- package/plugin/commands/game/optimize.md +62 -0
- package/plugin/commands/iot/provision.md +58 -0
- package/plugin/commands/ml/evaluate.md +47 -0
- package/plugin/commands/ml/train.md +48 -0
- package/plugin/commands/perf/benchmark.md +54 -0
- package/plugin/commands/perf/profile.md +49 -0
- package/plugin/commands/platform/blueprint.md +56 -0
- package/plugin/commands/security/audit.md +54 -0
- package/plugin/commands/security/scan.md +55 -0
- package/plugin/commands/sre/dashboard.md +53 -0
- package/plugin/registry.yaml +787 -0
- package/plugin/skills/ai-ml/experiment-tracking/SKILL.md +338 -0
- package/plugin/skills/ai-ml/feature-stores/SKILL.md +340 -0
- package/plugin/skills/ai-ml/llm-ops/SKILL.md +454 -0
- package/plugin/skills/ai-ml/ml-pipelines/SKILL.md +390 -0
- package/plugin/skills/ai-ml/model-monitoring/SKILL.md +398 -0
- package/plugin/skills/ai-ml/model-serving/SKILL.md +386 -0
- package/plugin/skills/event-driven/cqrs-patterns/SKILL.md +348 -0
- package/plugin/skills/event-driven/event-sourcing/SKILL.md +334 -0
- package/plugin/skills/event-driven/kafka-deep/SKILL.md +252 -0
- package/plugin/skills/event-driven/saga-orchestration/SKILL.md +335 -0
- package/plugin/skills/event-driven/schema-registry/SKILL.md +328 -0
- package/plugin/skills/event-driven/stream-processing/SKILL.md +313 -0
- package/plugin/skills/game/game-audio/SKILL.md +446 -0
- package/plugin/skills/game/game-networking/SKILL.md +490 -0
- package/plugin/skills/game/godot-patterns/SKILL.md +413 -0
- package/plugin/skills/game/shader-programming/SKILL.md +492 -0
- package/plugin/skills/game/unity-patterns/SKILL.md +488 -0
- package/plugin/skills/iot/device-provisioning/SKILL.md +405 -0
- package/plugin/skills/iot/edge-computing/SKILL.md +369 -0
- package/plugin/skills/iot/industrial-protocols/SKILL.md +438 -0
- package/plugin/skills/iot/mqtt-deep/SKILL.md +418 -0
- package/plugin/skills/iot/ota-updates/SKILL.md +426 -0
- package/plugin/skills/microservices/api-gateway-patterns/SKILL.md +201 -0
- package/plugin/skills/microservices/circuit-breaker-patterns/SKILL.md +246 -0
- package/plugin/skills/microservices/contract-testing/SKILL.md +284 -0
- package/plugin/skills/microservices/distributed-tracing/SKILL.md +246 -0
- package/plugin/skills/microservices/service-discovery/SKILL.md +304 -0
- package/plugin/skills/microservices/service-mesh/SKILL.md +181 -0
- package/plugin/skills/mobile-advanced/mobile-ci-cd/SKILL.md +407 -0
- package/plugin/skills/mobile-advanced/mobile-security/SKILL.md +403 -0
- package/plugin/skills/mobile-advanced/offline-first/SKILL.md +473 -0
- package/plugin/skills/mobile-advanced/push-notifications/SKILL.md +494 -0
- package/plugin/skills/mobile-advanced/react-native-deep/SKILL.md +374 -0
- package/plugin/skills/simulation/numerical-methods/SKILL.md +434 -0
- package/plugin/skills/simulation/parallel-computing/SKILL.md +382 -0
- package/plugin/skills/simulation/physics-engines/SKILL.md +377 -0
- package/plugin/skills/simulation/validation-verification/SKILL.md +479 -0
- package/plugin/skills/simulation/visualization-scientific/SKILL.md +365 -0
- package/plugin/stdrules/ALIGNMENT_PRINCIPLE.md +240 -0
- package/plugin/workflows/ai-engineering/agent-development.md +3 -3
- package/plugin/workflows/ai-engineering/fine-tuning.md +3 -3
- package/plugin/workflows/ai-engineering/model-evaluation.md +3 -3
- package/plugin/workflows/ai-engineering/prompt-engineering.md +2 -2
- package/plugin/workflows/ai-engineering/rag-development.md +4 -4
- package/plugin/workflows/ai-ml/data-pipeline.md +188 -0
- package/plugin/workflows/ai-ml/experiment-cycle.md +203 -0
- package/plugin/workflows/ai-ml/feature-engineering.md +208 -0
- package/plugin/workflows/ai-ml/model-deployment.md +199 -0
- package/plugin/workflows/ai-ml/monitoring-setup.md +227 -0
- package/plugin/workflows/api/api-design.md +1 -1
- package/plugin/workflows/api/api-testing.md +2 -2
- package/plugin/workflows/content/technical-docs.md +1 -1
- package/plugin/workflows/database/migration.md +1 -1
- package/plugin/workflows/database/optimization.md +1 -1
- package/plugin/workflows/database/schema-design.md +3 -3
- package/plugin/workflows/development/bug-fix.md +3 -3
- package/plugin/workflows/development/code-review.md +2 -1
- package/plugin/workflows/development/feature.md +3 -3
- package/plugin/workflows/development/refactor.md +2 -2
- package/plugin/workflows/event-driven/consumer-groups.md +190 -0
- package/plugin/workflows/event-driven/event-storming.md +172 -0
- package/plugin/workflows/event-driven/replay-testing.md +186 -0
- package/plugin/workflows/event-driven/saga-implementation.md +206 -0
- package/plugin/workflows/event-driven/schema-evolution.md +173 -0
- package/plugin/workflows/fullstack/authentication.md +4 -4
- package/plugin/workflows/fullstack/full-feature.md +4 -4
- package/plugin/workflows/game-dev/content-pipeline.md +218 -0
- package/plugin/workflows/game-dev/platform-submission.md +263 -0
- package/plugin/workflows/game-dev/playtesting.md +237 -0
- package/plugin/workflows/game-dev/prototype-to-production.md +205 -0
- package/plugin/workflows/microservices/contract-first.md +151 -0
- package/plugin/workflows/microservices/distributed-tracing.md +166 -0
- package/plugin/workflows/microservices/domain-decomposition.md +123 -0
- package/plugin/workflows/microservices/integration-testing.md +149 -0
- package/plugin/workflows/microservices/service-mesh-setup.md +153 -0
- package/plugin/workflows/microservices/service-scaffolding.md +151 -0
- package/plugin/workflows/omega/1000x-innovation.md +2 -2
- package/plugin/workflows/omega/100x-architecture.md +2 -2
- package/plugin/workflows/omega/10x-improvement.md +2 -2
- package/plugin/workflows/quality/performance-optimization.md +2 -2
- package/plugin/workflows/research/best-practices.md +1 -1
- package/plugin/workflows/research/technology-research.md +1 -1
- package/plugin/workflows/security/penetration-testing.md +3 -3
- package/plugin/workflows/security/security-audit.md +3 -3
- package/plugin/workflows/sprint/sprint-execution.md +2 -2
- package/plugin/workflows/sprint/sprint-retrospective.md +1 -1
- package/plugin/workflows/sprint/sprint-setup.md +1 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Push Notifications
|
|
2
|
+
|
|
3
|
+
FCM/APNs integration, notification handling, rich notifications, and notification scheduling.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Push notifications enable real-time communication with users through platform-native notification systems (APNs for iOS, FCM for Android).
|
|
8
|
+
|
|
9
|
+
## Core Concepts
|
|
10
|
+
|
|
11
|
+
### Notification Types
|
|
12
|
+
- **Alert**: Visual/audio notification
|
|
13
|
+
- **Silent**: Background data update
|
|
14
|
+
- **Rich**: Images, buttons, custom UI
|
|
15
|
+
- **Scheduled**: Time-based local notifications
|
|
16
|
+
|
|
17
|
+
### Delivery Flow
|
|
18
|
+
```
|
|
19
|
+
Server → FCM/APNs → Device → App
|
|
20
|
+
↓
|
|
21
|
+
Token Registration
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## React Native Setup
|
|
25
|
+
|
|
26
|
+
### Firebase Messaging
|
|
27
|
+
```typescript
|
|
28
|
+
import messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
|
29
|
+
import notifee, { AndroidImportance, EventType } from '@notifee/react-native';
|
|
30
|
+
|
|
31
|
+
class PushNotificationService {
|
|
32
|
+
async initialize(): Promise<void> {
|
|
33
|
+
// Request permission (iOS)
|
|
34
|
+
const authStatus = await messaging().requestPermission();
|
|
35
|
+
const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
|
|
36
|
+
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
|
|
37
|
+
|
|
38
|
+
if (!enabled) {
|
|
39
|
+
console.log('Notification permission denied');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get FCM token
|
|
44
|
+
const token = await messaging().getToken();
|
|
45
|
+
await this.registerToken(token);
|
|
46
|
+
|
|
47
|
+
// Listen for token refresh
|
|
48
|
+
messaging().onTokenRefresh(async newToken => {
|
|
49
|
+
await this.registerToken(newToken);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Create Android channel
|
|
53
|
+
await notifee.createChannel({
|
|
54
|
+
id: 'default',
|
|
55
|
+
name: 'Default Channel',
|
|
56
|
+
importance: AndroidImportance.HIGH,
|
|
57
|
+
sound: 'default',
|
|
58
|
+
vibration: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private async registerToken(token: string): Promise<void> {
|
|
63
|
+
await api.post('/notifications/register', {
|
|
64
|
+
token,
|
|
65
|
+
platform: Platform.OS,
|
|
66
|
+
deviceId: getDeviceId()
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Foreground message handler
|
|
71
|
+
setupForegroundHandler(): () => void {
|
|
72
|
+
return messaging().onMessage(async remoteMessage => {
|
|
73
|
+
await this.displayNotification(remoteMessage);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Background/quit message handler
|
|
78
|
+
static setupBackgroundHandler(): void {
|
|
79
|
+
messaging().setBackgroundMessageHandler(async remoteMessage => {
|
|
80
|
+
console.log('Background message:', remoteMessage);
|
|
81
|
+
// Handle background message
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private async displayNotification(
|
|
86
|
+
message: FirebaseMessagingTypes.RemoteMessage
|
|
87
|
+
): Promise<void> {
|
|
88
|
+
await notifee.displayNotification({
|
|
89
|
+
title: message.notification?.title,
|
|
90
|
+
body: message.notification?.body,
|
|
91
|
+
data: message.data,
|
|
92
|
+
android: {
|
|
93
|
+
channelId: 'default',
|
|
94
|
+
pressAction: { id: 'default' },
|
|
95
|
+
smallIcon: 'ic_notification',
|
|
96
|
+
largeIcon: message.data?.imageUrl
|
|
97
|
+
},
|
|
98
|
+
ios: {
|
|
99
|
+
sound: 'default',
|
|
100
|
+
foregroundPresentationOptions: {
|
|
101
|
+
badge: true,
|
|
102
|
+
sound: true,
|
|
103
|
+
banner: true,
|
|
104
|
+
list: true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Notification Actions
|
|
113
|
+
```typescript
|
|
114
|
+
import notifee, { EventType } from '@notifee/react-native';
|
|
115
|
+
|
|
116
|
+
class NotificationActionHandler {
|
|
117
|
+
setupActionHandlers(): void {
|
|
118
|
+
// Foreground event handler
|
|
119
|
+
notifee.onForegroundEvent(({ type, detail }) => {
|
|
120
|
+
this.handleNotificationEvent(type, detail);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Background event handler
|
|
124
|
+
notifee.onBackgroundEvent(async ({ type, detail }) => {
|
|
125
|
+
await this.handleNotificationEvent(type, detail);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private async handleNotificationEvent(
|
|
130
|
+
type: EventType,
|
|
131
|
+
detail: any
|
|
132
|
+
): Promise<void> {
|
|
133
|
+
const { notification, pressAction } = detail;
|
|
134
|
+
|
|
135
|
+
switch (type) {
|
|
136
|
+
case EventType.DISMISSED:
|
|
137
|
+
console.log('Notification dismissed:', notification?.id);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case EventType.PRESS:
|
|
141
|
+
// Navigate to relevant screen
|
|
142
|
+
await this.handleNotificationPress(notification);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case EventType.ACTION_PRESS:
|
|
146
|
+
// Handle action button press
|
|
147
|
+
await this.handleActionPress(pressAction?.id, notification);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async handleNotificationPress(notification: any): Promise<void> {
|
|
153
|
+
const { data } = notification;
|
|
154
|
+
|
|
155
|
+
switch (data?.type) {
|
|
156
|
+
case 'message':
|
|
157
|
+
navigation.navigate('Chat', { conversationId: data.conversationId });
|
|
158
|
+
break;
|
|
159
|
+
case 'order':
|
|
160
|
+
navigation.navigate('OrderDetails', { orderId: data.orderId });
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
navigation.navigate('Home');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private async handleActionPress(
|
|
168
|
+
actionId: string | undefined,
|
|
169
|
+
notification: any
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
switch (actionId) {
|
|
172
|
+
case 'reply':
|
|
173
|
+
// Open quick reply
|
|
174
|
+
break;
|
|
175
|
+
case 'mark-read':
|
|
176
|
+
await api.post(`/messages/${notification.data?.messageId}/read`);
|
|
177
|
+
break;
|
|
178
|
+
case 'dismiss':
|
|
179
|
+
await notifee.cancelNotification(notification.id);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Rich Notifications
|
|
187
|
+
|
|
188
|
+
### Image Notifications
|
|
189
|
+
```typescript
|
|
190
|
+
async function displayImageNotification(
|
|
191
|
+
title: string,
|
|
192
|
+
body: string,
|
|
193
|
+
imageUrl: string
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
await notifee.displayNotification({
|
|
196
|
+
title,
|
|
197
|
+
body,
|
|
198
|
+
android: {
|
|
199
|
+
channelId: 'default',
|
|
200
|
+
largeIcon: imageUrl,
|
|
201
|
+
style: {
|
|
202
|
+
type: AndroidStyle.BIGPICTURE,
|
|
203
|
+
picture: imageUrl
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
ios: {
|
|
207
|
+
attachments: [{ url: imageUrl }]
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Action Buttons
|
|
214
|
+
```typescript
|
|
215
|
+
async function displayActionableNotification(
|
|
216
|
+
title: string,
|
|
217
|
+
body: string,
|
|
218
|
+
data: Record<string, string>
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
await notifee.displayNotification({
|
|
221
|
+
title,
|
|
222
|
+
body,
|
|
223
|
+
data,
|
|
224
|
+
android: {
|
|
225
|
+
channelId: 'default',
|
|
226
|
+
actions: [
|
|
227
|
+
{
|
|
228
|
+
title: 'Reply',
|
|
229
|
+
pressAction: { id: 'reply' },
|
|
230
|
+
input: {
|
|
231
|
+
placeholder: 'Type your reply...',
|
|
232
|
+
allowFreeFormInput: true
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
title: 'Mark as Read',
|
|
237
|
+
pressAction: { id: 'mark-read' }
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
ios: {
|
|
242
|
+
categoryId: 'message',
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// iOS category setup (in AppDelegate)
|
|
248
|
+
async function setupIOSCategories(): Promise<void> {
|
|
249
|
+
await notifee.setNotificationCategories([
|
|
250
|
+
{
|
|
251
|
+
id: 'message',
|
|
252
|
+
actions: [
|
|
253
|
+
{
|
|
254
|
+
id: 'reply',
|
|
255
|
+
title: 'Reply',
|
|
256
|
+
input: true
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'mark-read',
|
|
260
|
+
title: 'Mark as Read'
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
]);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Server-Side (Node.js)
|
|
269
|
+
|
|
270
|
+
### Firebase Admin SDK
|
|
271
|
+
```typescript
|
|
272
|
+
import * as admin from 'firebase-admin';
|
|
273
|
+
|
|
274
|
+
admin.initializeApp({
|
|
275
|
+
credential: admin.credential.cert(serviceAccount)
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
interface NotificationPayload {
|
|
279
|
+
title: string;
|
|
280
|
+
body: string;
|
|
281
|
+
data?: Record<string, string>;
|
|
282
|
+
imageUrl?: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
class NotificationService {
|
|
286
|
+
async sendToDevice(
|
|
287
|
+
token: string,
|
|
288
|
+
payload: NotificationPayload
|
|
289
|
+
): Promise<string> {
|
|
290
|
+
const message: admin.messaging.Message = {
|
|
291
|
+
token,
|
|
292
|
+
notification: {
|
|
293
|
+
title: payload.title,
|
|
294
|
+
body: payload.body,
|
|
295
|
+
imageUrl: payload.imageUrl
|
|
296
|
+
},
|
|
297
|
+
data: payload.data,
|
|
298
|
+
android: {
|
|
299
|
+
priority: 'high',
|
|
300
|
+
notification: {
|
|
301
|
+
channelId: 'default',
|
|
302
|
+
sound: 'default',
|
|
303
|
+
clickAction: 'FLUTTER_NOTIFICATION_CLICK'
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
apns: {
|
|
307
|
+
payload: {
|
|
308
|
+
aps: {
|
|
309
|
+
sound: 'default',
|
|
310
|
+
badge: 1,
|
|
311
|
+
'mutable-content': 1
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return await admin.messaging().send(message);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async sendToTopic(
|
|
321
|
+
topic: string,
|
|
322
|
+
payload: NotificationPayload
|
|
323
|
+
): Promise<string> {
|
|
324
|
+
const message: admin.messaging.Message = {
|
|
325
|
+
topic,
|
|
326
|
+
notification: {
|
|
327
|
+
title: payload.title,
|
|
328
|
+
body: payload.body
|
|
329
|
+
},
|
|
330
|
+
data: payload.data
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
return await admin.messaging().send(message);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async sendToMultiple(
|
|
337
|
+
tokens: string[],
|
|
338
|
+
payload: NotificationPayload
|
|
339
|
+
): Promise<admin.messaging.BatchResponse> {
|
|
340
|
+
const message: admin.messaging.MulticastMessage = {
|
|
341
|
+
tokens,
|
|
342
|
+
notification: {
|
|
343
|
+
title: payload.title,
|
|
344
|
+
body: payload.body
|
|
345
|
+
},
|
|
346
|
+
data: payload.data
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const response = await admin.messaging().sendEachForMulticast(message);
|
|
350
|
+
|
|
351
|
+
// Handle failed tokens
|
|
352
|
+
if (response.failureCount > 0) {
|
|
353
|
+
const failedTokens: string[] = [];
|
|
354
|
+
response.responses.forEach((resp, idx) => {
|
|
355
|
+
if (!resp.success) {
|
|
356
|
+
failedTokens.push(tokens[idx]);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
await this.removeInvalidTokens(failedTokens);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return response;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async subscribeToTopic(tokens: string[], topic: string): Promise<void> {
|
|
366
|
+
await admin.messaging().subscribeToTopic(tokens, topic);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async unsubscribeFromTopic(tokens: string[], topic: string): Promise<void> {
|
|
370
|
+
await admin.messaging().unsubscribeFromTopic(tokens, topic);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Local Notifications
|
|
376
|
+
|
|
377
|
+
### Scheduled Notifications
|
|
378
|
+
```typescript
|
|
379
|
+
import notifee, { TimestampTrigger, TriggerType } from '@notifee/react-native';
|
|
380
|
+
|
|
381
|
+
async function scheduleReminder(
|
|
382
|
+
title: string,
|
|
383
|
+
body: string,
|
|
384
|
+
date: Date
|
|
385
|
+
): Promise<string> {
|
|
386
|
+
const trigger: TimestampTrigger = {
|
|
387
|
+
type: TriggerType.TIMESTAMP,
|
|
388
|
+
timestamp: date.getTime()
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
return await notifee.createTriggerNotification(
|
|
392
|
+
{
|
|
393
|
+
title,
|
|
394
|
+
body,
|
|
395
|
+
android: { channelId: 'reminders' },
|
|
396
|
+
ios: { sound: 'default' }
|
|
397
|
+
},
|
|
398
|
+
trigger
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function scheduleRepeating(
|
|
403
|
+
title: string,
|
|
404
|
+
body: string,
|
|
405
|
+
hour: number,
|
|
406
|
+
minute: number
|
|
407
|
+
): Promise<string> {
|
|
408
|
+
const trigger: TimestampTrigger = {
|
|
409
|
+
type: TriggerType.TIMESTAMP,
|
|
410
|
+
timestamp: getNextOccurrence(hour, minute),
|
|
411
|
+
repeatFrequency: RepeatFrequency.DAILY
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
return await notifee.createTriggerNotification(
|
|
415
|
+
{ title, body, android: { channelId: 'reminders' } },
|
|
416
|
+
trigger
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function cancelScheduled(notificationId: string): Promise<void> {
|
|
421
|
+
await notifee.cancelTriggerNotification(notificationId);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function getScheduledNotifications(): Promise<TriggerNotification[]> {
|
|
425
|
+
return await notifee.getTriggerNotifications();
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Badge Management
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import notifee from '@notifee/react-native';
|
|
433
|
+
|
|
434
|
+
class BadgeManager {
|
|
435
|
+
async setBadgeCount(count: number): Promise<void> {
|
|
436
|
+
await notifee.setBadgeCount(count);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async incrementBadge(): Promise<void> {
|
|
440
|
+
const current = await notifee.getBadgeCount();
|
|
441
|
+
await notifee.setBadgeCount(current + 1);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async clearBadge(): Promise<void> {
|
|
445
|
+
await notifee.setBadgeCount(0);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Best Practices
|
|
451
|
+
|
|
452
|
+
1. **Request Permission Wisely**: Explain value before asking
|
|
453
|
+
2. **Personalize Content**: Use user's name, relevant data
|
|
454
|
+
3. **Respect Quiet Hours**: Don't notify at night
|
|
455
|
+
4. **Group Notifications**: Consolidate similar notifications
|
|
456
|
+
5. **Handle Token Refresh**: Always update server tokens
|
|
457
|
+
|
|
458
|
+
## Notification Checklist
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
□ Permission request with context
|
|
462
|
+
□ FCM/APNs token registration
|
|
463
|
+
□ Token refresh handling
|
|
464
|
+
□ Foreground notification display
|
|
465
|
+
□ Background message handling
|
|
466
|
+
□ Deep linking from notifications
|
|
467
|
+
□ Action button handlers
|
|
468
|
+
□ Badge count management
|
|
469
|
+
□ Invalid token cleanup
|
|
470
|
+
□ Analytics tracking
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Anti-Patterns
|
|
474
|
+
|
|
475
|
+
- Sending too many notifications
|
|
476
|
+
- Generic, non-personalized messages
|
|
477
|
+
- Not handling notification tap
|
|
478
|
+
- Ignoring token refresh
|
|
479
|
+
- No unsubscribe option
|
|
480
|
+
|
|
481
|
+
## When to Use
|
|
482
|
+
|
|
483
|
+
- Real-time updates needed
|
|
484
|
+
- Time-sensitive information
|
|
485
|
+
- Re-engagement campaigns
|
|
486
|
+
- Transaction confirmations
|
|
487
|
+
- Chat/messaging apps
|
|
488
|
+
|
|
489
|
+
## When NOT to Use
|
|
490
|
+
|
|
491
|
+
- Excessive marketing
|
|
492
|
+
- Non-urgent updates
|
|
493
|
+
- Information available in-app
|
|
494
|
+
- Users opted out
|