be-components 7.4.3 → 7.4.6

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.
Files changed (45) hide show
  1. package/lib/commonjs/ApiOverrides/index.js +0 -2
  2. package/lib/commonjs/ApiOverrides/index.js.map +1 -1
  3. package/lib/commonjs/NotificationManager/api/index.js +448 -0
  4. package/lib/commonjs/NotificationManager/api/index.js.map +1 -0
  5. package/lib/commonjs/NotificationManager/index.js +1177 -0
  6. package/lib/commonjs/NotificationManager/index.js.map +1 -0
  7. package/lib/commonjs/index.js +20 -0
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/types.d.js.map +1 -1
  10. package/lib/module/ApiOverrides/index.js +0 -2
  11. package/lib/module/ApiOverrides/index.js.map +1 -1
  12. package/lib/module/NotificationManager/api/index.js +441 -0
  13. package/lib/module/NotificationManager/api/index.js.map +1 -0
  14. package/lib/module/NotificationManager/index.js +1158 -0
  15. package/lib/module/NotificationManager/index.js.map +1 -0
  16. package/lib/module/index.js +3 -1
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/types.d.js.map +1 -1
  19. package/lib/typescript/lib/commonjs/ApiOverrides/index.d.ts.map +1 -1
  20. package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts +804 -0
  21. package/lib/typescript/lib/commonjs/NotificationManager/api/index.d.ts.map +1 -0
  22. package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts +818 -0
  23. package/lib/typescript/lib/commonjs/NotificationManager/index.d.ts.map +1 -0
  24. package/lib/typescript/lib/commonjs/index.d.ts +803 -0
  25. package/lib/typescript/lib/commonjs/index.d.ts.map +1 -1
  26. package/lib/typescript/lib/module/ApiOverrides/index.d.ts.map +1 -1
  27. package/lib/typescript/lib/module/NotificationManager/api/index.d.ts +803 -0
  28. package/lib/typescript/lib/module/NotificationManager/api/index.d.ts.map +1 -0
  29. package/lib/typescript/lib/module/NotificationManager/index.d.ts +19 -0
  30. package/lib/typescript/lib/module/NotificationManager/index.d.ts.map +1 -0
  31. package/lib/typescript/lib/module/index.d.ts +4 -1
  32. package/lib/typescript/lib/module/index.d.ts.map +1 -1
  33. package/lib/typescript/src/ApiOverrides/index.d.ts.map +1 -1
  34. package/lib/typescript/src/NotificationManager/api/index.d.ts +822 -0
  35. package/lib/typescript/src/NotificationManager/api/index.d.ts.map +1 -0
  36. package/lib/typescript/src/NotificationManager/index.d.ts +21 -0
  37. package/lib/typescript/src/NotificationManager/index.d.ts.map +1 -0
  38. package/lib/typescript/src/index.d.ts +3 -1
  39. package/lib/typescript/src/index.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/ApiOverrides/index.ts +0 -2
  42. package/src/NotificationManager/api/index.ts +134 -0
  43. package/src/NotificationManager/index.tsx +917 -0
  44. package/src/index.tsx +6 -1
  45. package/src/types.d.ts +70 -0
@@ -0,0 +1,1158 @@
1
+ import React, { useEffect, useState, useMemo } from 'react';
2
+ import { FlatList, TouchableOpacity, ActivityIndicator } from 'react-native';
3
+ import { View, Text, Button, TextInput } from '../Components/Themed';
4
+ import { useColors } from '../constants/useColors';
5
+ import { NotificationApi, NotificationHelpers } from './api';
6
+ import { Icons } from '../Components';
7
+ import DropDown from '../Components/Dropdown';
8
+ import { showConfirmAlert } from '../Components/ConfirmAlert';
9
+ const notification_types = [{
10
+ label: 'Order Notifications',
11
+ value: 'order_notifications'
12
+ }, {
13
+ label: 'Competition Notifications',
14
+ value: 'competition_notifications'
15
+ }, {
16
+ label: 'Social Notifications',
17
+ value: 'social_notifications'
18
+ }];
19
+ const sections = ['header', 'top_row', 'preview', 'second_row'];
20
+ const NotificationManager = ({
21
+ player_ids,
22
+ me,
23
+ float,
24
+ header_style,
25
+ footer_style,
26
+ notification_type,
27
+ default_path_name,
28
+ onFocusPosition,
29
+ default_params,
30
+ onComplete,
31
+ onClose
32
+ }) => {
33
+ const Colors = useColors();
34
+ const [loading, setLoading] = useState(false);
35
+ const [loadingGroups, setLoadingGroups] = useState(false);
36
+ const [notificationGroups, setNotificationGroups] = useState([]);
37
+ const [selectedGroup, setSelectedGroup] = useState(null);
38
+ const [showGroupsModal, setShowGroupsModal] = useState(false);
39
+ const [groupMemberIds, setGroupMemberIds] = useState([]);
40
+ const [showEditCustomGroupModal, setShowEditCustomGroupModal] = useState(false);
41
+ const [customGroupCsv, setCustomGroupCsv] = useState('');
42
+ const [sendingProgress, setSendingProgress] = useState(null);
43
+
44
+ // Create a custom group (always available, even if player_ids is empty)
45
+ const customGroup = {
46
+ notification_group_id: 'custom_group',
47
+ name: 'Custom Recipients',
48
+ description: player_ids.length > 0 ? `${player_ids.length} selected recipients` : 'Create custom recipient list',
49
+ status: 'active',
50
+ grouping_function: '',
51
+ player_count: player_ids.length,
52
+ create_datetime: new Date().toISOString(),
53
+ last_update_datetime: new Date().toISOString()
54
+ };
55
+
56
+ // Initialize selectedPath based on default_path_name if provided
57
+ const initialPath = default_path_name ? NotificationHelpers.app_paths.find(p => p.path_name === default_path_name) || NotificationHelpers.app_paths[0] : NotificationHelpers.app_paths[0];
58
+ const [selectedPath, setSelectedPath] = useState(initialPath);
59
+ const [customPathName, setCustomPathName] = useState('');
60
+ const [pathParams, setPathParams] = useState(default_params || {});
61
+ const [pathSearch, setPathSearch] = useState('');
62
+ const [notification, setNotification] = useState({
63
+ type: notification_type || 'order_notifications',
64
+ notify_body_type: 'custom',
65
+ title: '',
66
+ body: '',
67
+ status: 'pending',
68
+ expire_time: Date.now() + 7 * 24 * 60 * 60 * 1000 // 7 days from now
69
+ });
70
+ useEffect(() => {
71
+ startUp();
72
+ // Auto-select custom group if initial player_ids provided
73
+ if (player_ids.length > 0) {
74
+ setSelectedGroup(customGroup);
75
+ setGroupMemberIds(player_ids);
76
+ }
77
+ }, []);
78
+ useEffect(() => {
79
+ // Initialize pathParams when selectedPath changes, merging with default_params
80
+ const mergedParams = {
81
+ ...selectedPath?.params,
82
+ ...(default_params || {})
83
+ };
84
+ setPathParams(mergedParams);
85
+ }, [selectedPath]);
86
+ const startUp = () => {
87
+ NotificationApi.setEnvironment();
88
+ };
89
+ const loadNotificationGroups = async () => {
90
+ setLoadingGroups(true);
91
+ const groups = await NotificationApi.getActiveNotificationGroups();
92
+ setNotificationGroups(groups);
93
+ setLoadingGroups(false);
94
+ };
95
+ const handleSelectGroup = async group_id => {
96
+ setLoadingGroups(true);
97
+
98
+ // Check if it's the custom group
99
+ if (group_id === 'custom_group') {
100
+ // If custom group already selected with edited IDs, keep those IDs
101
+ // Otherwise use the initial player_ids
102
+ if (selectedGroup?.notification_group_id === 'custom_group' && groupMemberIds.length > 0) {
103
+ // Keep existing edited IDs
104
+ setSelectedGroup({
105
+ ...customGroup,
106
+ description: `${groupMemberIds.length} selected recipients`,
107
+ player_count: groupMemberIds.length
108
+ });
109
+ } else {
110
+ setSelectedGroup(customGroup);
111
+ setGroupMemberIds(player_ids);
112
+ }
113
+ setLoadingGroups(false);
114
+ setShowGroupsModal(false);
115
+ return;
116
+ }
117
+ const group = notificationGroups.find(g => g.notification_group_id === group_id);
118
+ setSelectedGroup(group || null);
119
+
120
+ // Fetch all members for this group
121
+ const members = await NotificationApi.getNotificationGroupMembersByGroup(group_id);
122
+
123
+ // Flatten all player_ids arrays from all member objects
124
+ const memberPlayerIds = members.flatMap(m => m.player_ids);
125
+ setGroupMemberIds(memberPlayerIds);
126
+ setLoadingGroups(false);
127
+ setShowGroupsModal(false);
128
+ };
129
+ const handleRemoveGroup = () => {
130
+ setSelectedGroup(null);
131
+ setGroupMemberIds([]);
132
+ };
133
+ const handleOpenEditCustomGroup = () => {
134
+ // Populate CSV with current groupMemberIds
135
+ setCustomGroupCsv(groupMemberIds.join(','));
136
+ setShowEditCustomGroupModal(true);
137
+ };
138
+ const handleSaveCustomGroup = () => {
139
+ // Parse CSV and update groupMemberIds
140
+ const ids = customGroupCsv.split(',').map(id => id.trim()).filter(id => id.length > 0);
141
+ setGroupMemberIds(ids);
142
+
143
+ // Update the custom group description
144
+ if (customGroup && selectedGroup?.notification_group_id === 'custom_group') {
145
+ const updatedGroup = {
146
+ ...customGroup,
147
+ description: `${ids.length} selected recipients`,
148
+ player_count: ids.length
149
+ };
150
+ setSelectedGroup(updatedGroup);
151
+ }
152
+ setShowEditCustomGroupModal(false);
153
+ };
154
+
155
+ // Filter and sort paths: primary first (alphabetically), then others (alphabetically), limited by search
156
+ const filteredPaths = useMemo(() => {
157
+ let filtered = NotificationHelpers.app_paths;
158
+
159
+ // Apply search filter
160
+ if (pathSearch) {
161
+ filtered = filtered.filter(path => path.label.toLowerCase().includes(pathSearch.toLowerCase()) || path.path_name.toLowerCase().includes(pathSearch.toLowerCase()));
162
+ }
163
+
164
+ // Sort: primary paths first (alphabetically), then non-primary (alphabetically)
165
+ const sorted = [...filtered].sort((a, b) => {
166
+ if (a.primary && !b.primary) return -1;
167
+ if (!a.primary && b.primary) return 1;
168
+ return a.label.localeCompare(b.label);
169
+ });
170
+
171
+ // Limit to 15 results
172
+ return sorted.slice(0, 15);
173
+ }, [pathSearch]);
174
+ const isValid = () => {
175
+ const errors = [];
176
+ if (!notification.title || notification.title.trim() === '') {
177
+ errors.push('Notification title is required');
178
+ }
179
+ if (!notification.body || notification.body.trim() === '') {
180
+ errors.push('Notification body is required');
181
+ }
182
+ if (groupMemberIds.length === 0) {
183
+ errors.push('At least one recipient is required');
184
+ }
185
+
186
+ // Check required params
187
+ if (selectedPath?.requiredParams && selectedPath.requiredParams.length > 0) {
188
+ selectedPath.requiredParams.forEach(paramKey => {
189
+ if (!pathParams[paramKey] || pathParams[paramKey].trim() === '') {
190
+ errors.push(`Required parameter "${paramKey}" is missing`);
191
+ }
192
+ });
193
+ }
194
+
195
+ // Check custom path name
196
+ if (selectedPath?.path_name === 'custom' && (!customPathName || customPathName.trim() === '')) {
197
+ errors.push('Custom path name is required');
198
+ }
199
+ return errors;
200
+ };
201
+ const confirmSendNotification = options => {
202
+ const errors = isValid();
203
+ if (errors.length > 0) {
204
+ const errorMessage = 'Please fix the following errors:\n\n' + errors.map((err, idx) => `${idx + 1}. ${err}`).join('\n');
205
+ alert(errorMessage);
206
+ return;
207
+ }
208
+ showConfirmAlert('Are you sure?', options?.test ? 'This will send a test notification to you only' : 'This will send a notification to all users selected', () => handleSendNotification(options), () => console.log('Done'));
209
+ };
210
+ const handleSendNotification = async options => {
211
+ // Use only group member IDs (no individual player_ids)
212
+ let player_ids_to_send = [...groupMemberIds];
213
+ if (options?.test) {
214
+ //Change to the admin
215
+ player_ids_to_send = [me.player_id];
216
+ }
217
+ setLoading(true);
218
+ try {
219
+ // Determine final path_name
220
+ const finalPathName = selectedPath?.path_name === 'custom' ? customPathName : selectedPath?.path_name;
221
+
222
+ // Construct the complete notification object with all required fields
223
+ const completeNotification = {
224
+ player_notification_id: '',
225
+ // Will be generated by backend
226
+ player_id: me.player_id,
227
+ type: notification.type,
228
+ notify_body_type: notification.notify_body_type || 'custom',
229
+ title: notification.title,
230
+ body: notification.body,
231
+ options: {
232
+ id: Date.now().toString(),
233
+ body: notification.body,
234
+ icon: 'default',
235
+ type: notification.type || 'order_notifications',
236
+ data: {
237
+ id: Date.now().toString(),
238
+ player_id: me.player_id,
239
+ pageStack: 'MainStack',
240
+ page: 'Notifications',
241
+ pageParams: {},
242
+ url: notification.link_override,
243
+ path_name: finalPathName,
244
+ params: pathParams
245
+ }
246
+ },
247
+ status: 'unread',
248
+ link_override: notification.link_override,
249
+ expire_time: 0,
250
+ create_datetime: new Date().toISOString(),
251
+ last_update_datetime: new Date().toISOString()
252
+ };
253
+
254
+ // Split into batches of 500
255
+ const BATCH_SIZE = 500;
256
+ const batches = [];
257
+ for (let i = 0; i < player_ids_to_send.length; i += BATCH_SIZE) {
258
+ batches.push(player_ids_to_send.slice(i, i + BATCH_SIZE));
259
+ }
260
+
261
+ // Send notifications in batches
262
+ for (let i = 0; i < batches.length; i++) {
263
+ const batch = batches[i];
264
+ if (!batch) continue;
265
+ setSendingProgress({
266
+ current: i + 1,
267
+ total: batches.length
268
+ });
269
+ await NotificationApi.broadcastNotifications(completeNotification, batch);
270
+ }
271
+ setSendingProgress(null);
272
+ if (onComplete) {
273
+ onComplete(completeNotification);
274
+ }
275
+
276
+ // Only reset form if not a test send
277
+ if (!options?.test) {
278
+ setNotification({
279
+ type: 'order_notifications',
280
+ notify_body_type: 'custom',
281
+ title: '',
282
+ body: '',
283
+ status: 'pending',
284
+ expire_time: Date.now() + 7 * 24 * 60 * 60 * 1000
285
+ });
286
+ }
287
+ const recipientCount = options?.test ? 1 : groupMemberIds.length;
288
+ const recipientMessage = options?.test ? 'Test notification sent to you!' : `Notification sent successfully to ${recipientCount} user${recipientCount !== 1 ? 's' : ''}!`;
289
+ alert(recipientMessage);
290
+ } catch (error) {
291
+ console.error('Error sending notification:', error);
292
+ alert('Failed to send notification. Please try again.');
293
+ }
294
+ setLoading(false);
295
+ };
296
+ const renderSections = data => {
297
+ switch (data.item) {
298
+ case 'header':
299
+ const headerTotalRecipients = groupMemberIds.length;
300
+ return /*#__PURE__*/React.createElement(View, {
301
+ type: "header",
302
+ style: {
303
+ flexDirection: 'row',
304
+ alignItems: 'center',
305
+ padding: 10,
306
+ ...header_style
307
+ }
308
+ }, /*#__PURE__*/React.createElement(View, {
309
+ transparent: true,
310
+ style: {
311
+ flex: 1
312
+ }
313
+ }, /*#__PURE__*/React.createElement(Text, {
314
+ theme: "h1"
315
+ }, "Send Notification"), /*#__PURE__*/React.createElement(Text, {
316
+ theme: "description",
317
+ style: {
318
+ marginTop: 3
319
+ }
320
+ }, "Sending to ", headerTotalRecipients, " users")), onClose && /*#__PURE__*/React.createElement(Button, {
321
+ transparent: true,
322
+ onPress: onClose
323
+ }, /*#__PURE__*/React.createElement(Icons.CloseIcon, {
324
+ size: 16,
325
+ color: Colors.text.h1
326
+ })));
327
+ case 'preview':
328
+ return /*#__PURE__*/React.createElement(View, {
329
+ float: true,
330
+ style: {
331
+ margin: 5
332
+ }
333
+ }, /*#__PURE__*/React.createElement(View, {
334
+ type: "header",
335
+ style: {
336
+ flexDirection: 'row',
337
+ alignItems: 'center',
338
+ padding: 10,
339
+ borderTopRightRadius: 8,
340
+ borderTopLeftRadius: 8
341
+ }
342
+ }, /*#__PURE__*/React.createElement(View, {
343
+ transparent: true,
344
+ style: {
345
+ flex: 1
346
+ }
347
+ }, /*#__PURE__*/React.createElement(Text, {
348
+ theme: "h1"
349
+ }, "Preview"), /*#__PURE__*/React.createElement(Text, {
350
+ theme: "description",
351
+ style: {
352
+ marginTop: 3
353
+ }
354
+ }, "What will this notification look like?"))), /*#__PURE__*/React.createElement(View, {
355
+ float: true,
356
+ style: {
357
+ padding: 15,
358
+ margin: 10,
359
+ borderRadius: 8,
360
+ borderWidth: 1,
361
+ borderColor: Colors.borders.light
362
+ }
363
+ }, /*#__PURE__*/React.createElement(View, {
364
+ transparent: true,
365
+ style: {
366
+ flexDirection: 'row',
367
+ alignItems: 'center'
368
+ }
369
+ }, /*#__PURE__*/React.createElement(View, {
370
+ style: {
371
+ width: 40,
372
+ height: 40,
373
+ borderRadius: 8,
374
+ justifyContent: 'center',
375
+ alignItems: 'center',
376
+ marginRight: 12
377
+ }
378
+ }, /*#__PURE__*/React.createElement(Icons.NotificationIcon, {
379
+ size: 20,
380
+ color: Colors.text.h1
381
+ })), /*#__PURE__*/React.createElement(View, {
382
+ transparent: true,
383
+ style: {
384
+ flex: 1
385
+ }
386
+ }, /*#__PURE__*/React.createElement(Text, {
387
+ theme: "h1"
388
+ }, notification.title || 'Notification Title'), /*#__PURE__*/React.createElement(Text, {
389
+ theme: "description",
390
+ style: {
391
+ marginTop: 4
392
+ }
393
+ }, notification.body || 'Notification message will appear here')))));
394
+ case 'top_row':
395
+ const totalRecipients = groupMemberIds.length;
396
+ return /*#__PURE__*/React.createElement(View, {
397
+ style: {
398
+ flexDirection: 'row',
399
+ flexWrap: 'wrap'
400
+ }
401
+ }, /*#__PURE__*/React.createElement(View, {
402
+ float: true,
403
+ style: {
404
+ flexGrow: 1,
405
+ minWidth: 300,
406
+ padding: 10,
407
+ margin: 5
408
+ }
409
+ }, /*#__PURE__*/React.createElement(View, {
410
+ transparent: true,
411
+ style: {
412
+ flexDirection: 'row',
413
+ alignItems: 'center',
414
+ justifyContent: 'space-between',
415
+ marginBottom: 10
416
+ }
417
+ }, /*#__PURE__*/React.createElement(View, {
418
+ transparent: true,
419
+ style: {
420
+ flex: 1
421
+ }
422
+ }, /*#__PURE__*/React.createElement(Text, {
423
+ theme: "h2"
424
+ }, "Recipients (", totalRecipients, ")"), /*#__PURE__*/React.createElement(Text, {
425
+ theme: "description",
426
+ style: {
427
+ marginTop: 4
428
+ }
429
+ }, totalRecipients === 0 ? 'No recipients selected' : 'Selected group')), /*#__PURE__*/React.createElement(Button, {
430
+ type: "action",
431
+ onPress: () => {
432
+ loadNotificationGroups();
433
+ setShowGroupsModal(true);
434
+ },
435
+ style: {
436
+ padding: 8
437
+ }
438
+ }, /*#__PURE__*/React.createElement(Text, {
439
+ theme: "h1",
440
+ color: Colors.text.white
441
+ }, "Select Group"))), selectedGroup && /*#__PURE__*/React.createElement(View, {
442
+ float: true,
443
+ style: {
444
+ flexDirection: 'row',
445
+ alignItems: 'center',
446
+ justifyContent: 'space-between',
447
+ padding: 12,
448
+ borderRadius: 8,
449
+ marginTop: 5
450
+ }
451
+ }, /*#__PURE__*/React.createElement(View, {
452
+ transparent: true,
453
+ style: {
454
+ flex: 1
455
+ }
456
+ }, /*#__PURE__*/React.createElement(Text, {
457
+ theme: "h1"
458
+ }, selectedGroup.name), /*#__PURE__*/React.createElement(Text, {
459
+ theme: "description",
460
+ style: {
461
+ marginTop: 2
462
+ }
463
+ }, groupMemberIds.length, " members")), /*#__PURE__*/React.createElement(View, {
464
+ transparent: true,
465
+ style: {
466
+ flexDirection: 'row'
467
+ }
468
+ }, selectedGroup.notification_group_id === 'custom_group' && /*#__PURE__*/React.createElement(Button, {
469
+ type: "action",
470
+ onPress: handleOpenEditCustomGroup,
471
+ style: {
472
+ padding: 8,
473
+ marginRight: 8
474
+ }
475
+ }, /*#__PURE__*/React.createElement(Icons.EditIcon, {
476
+ size: 16,
477
+ color: Colors.text.white
478
+ })), /*#__PURE__*/React.createElement(Button, {
479
+ type: "error",
480
+ onPress: handleRemoveGroup,
481
+ style: {
482
+ padding: 8
483
+ }
484
+ }, /*#__PURE__*/React.createElement(Icons.CloseIcon, {
485
+ size: 16,
486
+ color: Colors.text.white
487
+ }))))));
488
+ case 'second_row':
489
+ return /*#__PURE__*/React.createElement(View, {
490
+ style: {
491
+ flexDirection: 'row',
492
+ flexWrap: 'wrap'
493
+ }
494
+ }, /*#__PURE__*/React.createElement(View, {
495
+ float: true,
496
+ style: {
497
+ minWidth: 300,
498
+ flexGrow: 1,
499
+ margin: 5
500
+ }
501
+ }, /*#__PURE__*/React.createElement(View, {
502
+ type: "header",
503
+ style: {
504
+ flexDirection: 'row',
505
+ alignItems: 'center',
506
+ padding: 10,
507
+ borderTopRightRadius: 8,
508
+ borderTopLeftRadius: 8
509
+ }
510
+ }, /*#__PURE__*/React.createElement(View, {
511
+ transparent: true,
512
+ style: {
513
+ flex: 1
514
+ }
515
+ }, /*#__PURE__*/React.createElement(Text, {
516
+ theme: "h1"
517
+ }, "Notification Message"), /*#__PURE__*/React.createElement(Text, {
518
+ theme: "description",
519
+ style: {
520
+ marginTop: 3
521
+ }
522
+ }, "Update notification message below"))), /*#__PURE__*/React.createElement(View, {
523
+ transparent: true,
524
+ type: "body"
525
+ }, /*#__PURE__*/React.createElement(View, {
526
+ transparent: true,
527
+ style: {
528
+ padding: 10
529
+ }
530
+ }, /*#__PURE__*/React.createElement(Text, {
531
+ theme: "h2",
532
+ style: {
533
+ marginBottom: 10
534
+ }
535
+ }, "Notification Title"), /*#__PURE__*/React.createElement(TextInput, {
536
+ value: notification.title,
537
+ onFocusPosition: onFocusPosition,
538
+ placeholder: "Enter notification title",
539
+ onChangeText: title => setNotification({
540
+ ...notification,
541
+ title
542
+ })
543
+ })), /*#__PURE__*/React.createElement(View, {
544
+ transparent: true,
545
+ style: {
546
+ padding: 10
547
+ }
548
+ }, /*#__PURE__*/React.createElement(Text, {
549
+ theme: "h2",
550
+ style: {
551
+ marginBottom: 10
552
+ }
553
+ }, "Notification Body"), /*#__PURE__*/React.createElement(TextInput, {
554
+ value: notification.body,
555
+ onFocusPosition: onFocusPosition,
556
+ placeholder: "Enter notification message",
557
+ onChangeText: body => setNotification({
558
+ ...notification,
559
+ body
560
+ }),
561
+ multiline: true,
562
+ numberOfLines: 6,
563
+ style: {
564
+ padding: 12,
565
+ borderRadius: 8,
566
+ minHeight: 120
567
+ }
568
+ })))), /*#__PURE__*/React.createElement(View, {
569
+ float: true,
570
+ style: {
571
+ flexGrow: 1,
572
+ minWidth: 300,
573
+ margin: 5
574
+ }
575
+ }, /*#__PURE__*/React.createElement(View, {
576
+ type: "header",
577
+ style: {
578
+ flexDirection: 'row',
579
+ alignItems: 'center',
580
+ padding: 10,
581
+ borderTopRightRadius: 8,
582
+ borderTopLeftRadius: 8
583
+ }
584
+ }, /*#__PURE__*/React.createElement(View, {
585
+ transparent: true,
586
+ style: {
587
+ flex: 1
588
+ }
589
+ }, /*#__PURE__*/React.createElement(Text, {
590
+ theme: "h1"
591
+ }, "Notification Settings"), /*#__PURE__*/React.createElement(Text, {
592
+ theme: "description",
593
+ style: {
594
+ marginTop: 3
595
+ }
596
+ }, "Manage settigns below"))), /*#__PURE__*/React.createElement(View, {
597
+ transparent: true,
598
+ type: "body"
599
+ }, /*#__PURE__*/React.createElement(View, {
600
+ type: "row",
601
+ style: {
602
+ padding: 10
603
+ }
604
+ }, /*#__PURE__*/React.createElement(Text, {
605
+ theme: "h2",
606
+ style: {
607
+ flex: 1
608
+ }
609
+ }, "Notification Type"), /*#__PURE__*/React.createElement(DropDown, {
610
+ selected_value: notification_types.find(t => t.value === notification.type)?.label ?? '',
611
+ dropdown_options: [{
612
+ value: 'type',
613
+ eligible_options: notification_types.map(t => t.label)
614
+ }],
615
+ onOptionSelect: selected => {
616
+ const type = notification_types.find(t => t.label === selected)?.value;
617
+ setNotification({
618
+ ...notification,
619
+ type: type
620
+ });
621
+ }
622
+ })), /*#__PURE__*/React.createElement(View, {
623
+ transparent: true,
624
+ style: {
625
+ padding: 10
626
+ }
627
+ }, /*#__PURE__*/React.createElement(Text, {
628
+ theme: "h2",
629
+ style: {
630
+ marginBottom: 10
631
+ }
632
+ }, "Link Override (Optional)"), /*#__PURE__*/React.createElement(TextInput, {
633
+ value: notification.link_override || '',
634
+ onFocusPosition: onFocusPosition,
635
+ placeholder: "https://example.com",
636
+ onChangeText: link_override => setNotification({
637
+ ...notification,
638
+ link_override
639
+ }),
640
+ style: {
641
+ padding: 12,
642
+ borderRadius: 8
643
+ }
644
+ })), /*#__PURE__*/React.createElement(View, {
645
+ transparent: true,
646
+ style: {
647
+ padding: 10
648
+ }
649
+ }, /*#__PURE__*/React.createElement(Text, {
650
+ theme: "h2",
651
+ style: {
652
+ marginBottom: 10
653
+ }
654
+ }, "Deep Link Path"), /*#__PURE__*/React.createElement(View, {
655
+ type: "header",
656
+ style: {
657
+ padding: 12,
658
+ borderRadius: 8,
659
+ marginBottom: 8,
660
+ flexDirection: 'row',
661
+ alignItems: 'center',
662
+ justifyContent: 'space-between'
663
+ }
664
+ }, /*#__PURE__*/React.createElement(View, {
665
+ transparent: true,
666
+ style: {
667
+ flex: 1
668
+ }
669
+ }, /*#__PURE__*/React.createElement(Text, {
670
+ theme: "h1",
671
+ style: {
672
+ marginBottom: 2
673
+ }
674
+ }, selectedPath?.label), /*#__PURE__*/React.createElement(Text, {
675
+ theme: "description",
676
+ style: {
677
+ fontSize: 12
678
+ }
679
+ }, selectedPath?.path_name)), selectedPath?.primary && /*#__PURE__*/React.createElement(View, {
680
+ transparent: true,
681
+ style: {
682
+ paddingHorizontal: 8,
683
+ paddingVertical: 4,
684
+ borderRadius: 4
685
+ }
686
+ }, /*#__PURE__*/React.createElement(Text, {
687
+ theme: "description",
688
+ color: Colors.text.white,
689
+ style: {
690
+ fontSize: 10
691
+ }
692
+ }, "Primary"))), /*#__PURE__*/React.createElement(TextInput, {
693
+ value: pathSearch,
694
+ placeholder: "Search paths to change...",
695
+ onFocusPosition: onFocusPosition,
696
+ onChangeText: setPathSearch,
697
+ style: {
698
+ padding: 12,
699
+ borderRadius: 8,
700
+ marginBottom: 8
701
+ }
702
+ }), pathSearch && /*#__PURE__*/React.createElement(View, {
703
+ float: true,
704
+ style: {
705
+ maxHeight: 300,
706
+ borderRadius: 8,
707
+ borderWidth: 1,
708
+ borderColor: Colors.borders.light
709
+ }
710
+ }, /*#__PURE__*/React.createElement(FlatList, {
711
+ data: filteredPaths,
712
+ keyExtractor: item => item.path_name,
713
+ renderItem: ({
714
+ item
715
+ }) => /*#__PURE__*/React.createElement(TouchableOpacity, {
716
+ onPress: () => {
717
+ setSelectedPath(item);
718
+ setPathSearch('');
719
+ },
720
+ style: {
721
+ padding: 12,
722
+ borderBottomWidth: 1,
723
+ borderColor: Colors.borders.light
724
+ }
725
+ }, /*#__PURE__*/React.createElement(View, {
726
+ transparent: true,
727
+ style: {
728
+ flexDirection: 'row',
729
+ alignItems: 'center',
730
+ justifyContent: 'space-between'
731
+ }
732
+ }, /*#__PURE__*/React.createElement(View, {
733
+ transparent: true,
734
+ style: {
735
+ flex: 1
736
+ }
737
+ }, /*#__PURE__*/React.createElement(Text, {
738
+ theme: "h2",
739
+ style: {
740
+ marginBottom: 2
741
+ }
742
+ }, item.label), /*#__PURE__*/React.createElement(Text, {
743
+ theme: "description",
744
+ style: {
745
+ fontSize: 11
746
+ }
747
+ }, item.path_name)), item.primary && /*#__PURE__*/React.createElement(View, {
748
+ transparent: true,
749
+ style: {
750
+ paddingHorizontal: 6,
751
+ paddingVertical: 2,
752
+ borderRadius: 4,
753
+ marginLeft: 8
754
+ }
755
+ }, /*#__PURE__*/React.createElement(Text, {
756
+ theme: "description",
757
+ color: Colors.text.white,
758
+ style: {
759
+ fontSize: 9
760
+ }
761
+ }, "Primary")))),
762
+ ListEmptyComponent: /*#__PURE__*/React.createElement(View, {
763
+ style: {
764
+ padding: 20,
765
+ alignItems: 'center'
766
+ }
767
+ }, /*#__PURE__*/React.createElement(Text, {
768
+ theme: "description"
769
+ }, "No paths found"))
770
+ }))), Object.keys(selectedPath?.params ?? {}).length > 0 && /*#__PURE__*/React.createElement(View, {
771
+ style: {
772
+ padding: 10
773
+ }
774
+ }, /*#__PURE__*/React.createElement(Text, {
775
+ theme: "h2",
776
+ style: {
777
+ marginBottom: 8
778
+ }
779
+ }, "Path Parameters"), /*#__PURE__*/React.createElement(View, {
780
+ type: "header",
781
+ style: {
782
+ padding: 12,
783
+ borderRadius: 8
784
+ }
785
+ }, Object.keys(selectedPath?.params ?? {}).map(paramKey => {
786
+ const isRequired = selectedPath?.requiredParams.includes(paramKey);
787
+ return /*#__PURE__*/React.createElement(View, {
788
+ key: paramKey,
789
+ transparent: true,
790
+ style: {
791
+ marginBottom: 12
792
+ }
793
+ }, /*#__PURE__*/React.createElement(View, {
794
+ transparent: true,
795
+ style: {
796
+ flexDirection: 'row',
797
+ alignItems: 'center',
798
+ marginBottom: 4
799
+ }
800
+ }, /*#__PURE__*/React.createElement(Text, {
801
+ theme: "description"
802
+ }, paramKey), isRequired ? /*#__PURE__*/React.createElement(View, {
803
+ transparent: true,
804
+ style: {
805
+ marginLeft: 4,
806
+ paddingHorizontal: 6,
807
+ paddingVertical: 2,
808
+ backgroundColor: Colors.text.error,
809
+ borderRadius: 4
810
+ }
811
+ }, /*#__PURE__*/React.createElement(Text, {
812
+ theme: "description",
813
+ color: Colors.text.white,
814
+ style: {
815
+ fontSize: 10
816
+ }
817
+ }, "Required")) : /*#__PURE__*/React.createElement(View, {
818
+ transparent: true,
819
+ style: {
820
+ marginLeft: 4,
821
+ paddingHorizontal: 6,
822
+ paddingVertical: 2,
823
+ backgroundColor: Colors.borders.light,
824
+ borderRadius: 4
825
+ }
826
+ }, /*#__PURE__*/React.createElement(Text, {
827
+ theme: "description",
828
+ style: {
829
+ fontSize: 10
830
+ }
831
+ }, "Optional"))), /*#__PURE__*/React.createElement(TextInput, {
832
+ value: pathParams[paramKey] || '',
833
+ onFocusPosition: onFocusPosition,
834
+ placeholder: `Enter ${paramKey}${isRequired ? ' (required)' : ' (optional)'}`,
835
+ onChangeText: value => setPathParams({
836
+ ...pathParams,
837
+ [paramKey]: value
838
+ }),
839
+ style: {
840
+ padding: 10,
841
+ borderRadius: 8,
842
+ borderWidth: isRequired ? 2 : 1,
843
+ borderColor: isRequired && !pathParams[paramKey] ? Colors.text.error : Colors.borders.light
844
+ }
845
+ }));
846
+ })))), selectedPath?.path_name === 'custom' && /*#__PURE__*/React.createElement(View, {
847
+ style: {
848
+ marginBottom: 20
849
+ }
850
+ }, /*#__PURE__*/React.createElement(Text, {
851
+ theme: "h2",
852
+ style: {
853
+ marginBottom: 8
854
+ }
855
+ }, "Custom Path Name"), /*#__PURE__*/React.createElement(TextInput, {
856
+ value: customPathName,
857
+ onFocusPosition: onFocusPosition,
858
+ placeholder: "/your/custom/path",
859
+ onChangeText: setCustomPathName,
860
+ style: {
861
+ padding: 12,
862
+ borderRadius: 8
863
+ }
864
+ }))));
865
+ default:
866
+ return /*#__PURE__*/React.createElement(React.Fragment, null);
867
+ }
868
+ };
869
+ return /*#__PURE__*/React.createElement(View, {
870
+ float: float,
871
+ style: {
872
+ flex: 1
873
+ }
874
+ }, /*#__PURE__*/React.createElement(View, {
875
+ transparent: true,
876
+ style: {
877
+ flex: 1
878
+ }
879
+ }, /*#__PURE__*/React.createElement(FlatList, {
880
+ data: sections,
881
+ keyExtractor: item => item,
882
+ key: 'notification_manager_list',
883
+ renderItem: renderSections
884
+ })), /*#__PURE__*/React.createElement(View, {
885
+ type: "footer",
886
+ style: {
887
+ flexDirection: 'row',
888
+ alignItems: 'center',
889
+ padding: 10,
890
+ ...footer_style
891
+ }
892
+ }, /*#__PURE__*/React.createElement(Button, {
893
+ type: "action",
894
+ disabled: loading,
895
+ loading: loading,
896
+ onPress: () => confirmSendNotification({
897
+ test: true
898
+ }),
899
+ style: {
900
+ flex: 1,
901
+ opacity: loading || isValid().length > 0 ? 0.5 : 1
902
+ }
903
+ }, /*#__PURE__*/React.createElement(Text, {
904
+ textAlign: "center",
905
+ theme: "h1",
906
+ color: Colors.text.white
907
+ }, loading ? sendingProgress ? `Sending ${sendingProgress.current}/${sendingProgress.total}` : 'Sending...' : 'Send Test')), /*#__PURE__*/React.createElement(Button, {
908
+ type: "success",
909
+ disabled: loading,
910
+ loading: loading,
911
+ onPress: () => confirmSendNotification(),
912
+ style: {
913
+ flex: 2,
914
+ marginLeft: 5,
915
+ opacity: loading || isValid().length > 0 ? 0.5 : 1
916
+ }
917
+ }, /*#__PURE__*/React.createElement(Text, {
918
+ textAlign: "center",
919
+ theme: "h1",
920
+ color: Colors.text.white
921
+ }, loading ? sendingProgress ? `Sending batch ${sendingProgress.current} of ${sendingProgress.total}` : 'Sending...' : 'Send Notification'))), showGroupsModal && /*#__PURE__*/React.createElement(View, {
922
+ type: "blur",
923
+ style: {
924
+ position: 'absolute',
925
+ top: 0,
926
+ left: 0,
927
+ right: 0,
928
+ bottom: 0,
929
+ padding: 20
930
+ }
931
+ }, /*#__PURE__*/React.createElement(View, {
932
+ float: true,
933
+ style: {
934
+ flex: 1
935
+ }
936
+ }, /*#__PURE__*/React.createElement(View, {
937
+ type: "header",
938
+ style: {
939
+ flexDirection: 'row',
940
+ alignItems: 'center',
941
+ padding: 10,
942
+ borderTopRightRadius: 8,
943
+ borderTopLeftRadius: 8
944
+ }
945
+ }, /*#__PURE__*/React.createElement(View, {
946
+ transparent: true,
947
+ style: {
948
+ flex: 1
949
+ }
950
+ }, /*#__PURE__*/React.createElement(Text, {
951
+ theme: "h1"
952
+ }, "Notification Groups"), /*#__PURE__*/React.createElement(Text, {
953
+ theme: "description",
954
+ style: {
955
+ marginTop: 3
956
+ }
957
+ }, "Select a group to send to"))), /*#__PURE__*/React.createElement(View, {
958
+ style: {
959
+ flex: 1
960
+ }
961
+ }, loadingGroups ? /*#__PURE__*/React.createElement(View, {
962
+ style: {
963
+ padding: 40,
964
+ alignItems: 'center'
965
+ }
966
+ }, /*#__PURE__*/React.createElement(ActivityIndicator, {
967
+ size: "large",
968
+ color: Colors.text.action
969
+ })) : /*#__PURE__*/React.createElement(FlatList, {
970
+ data: [
971
+ // Show custom group with current member count
972
+ selectedGroup?.notification_group_id === 'custom_group' && groupMemberIds.length > 0 ? {
973
+ ...customGroup,
974
+ description: `${groupMemberIds.length} selected recipients`,
975
+ player_count: groupMemberIds.length
976
+ } : customGroup, ...notificationGroups],
977
+ keyExtractor: item => item.notification_group_id,
978
+ renderItem: ({
979
+ item
980
+ }) => /*#__PURE__*/React.createElement(TouchableOpacity, {
981
+ onPress: () => handleSelectGroup(item.notification_group_id),
982
+ style: {
983
+ padding: 15,
984
+ borderBottomWidth: 1,
985
+ borderColor: Colors.borders.light
986
+ }
987
+ }, /*#__PURE__*/React.createElement(View, {
988
+ transparent: true
989
+ }, /*#__PURE__*/React.createElement(View, {
990
+ transparent: true,
991
+ style: {
992
+ flexDirection: 'row',
993
+ alignItems: 'center',
994
+ justifyContent: 'space-between'
995
+ }
996
+ }, /*#__PURE__*/React.createElement(View, {
997
+ transparent: true,
998
+ style: {
999
+ flexDirection: 'row',
1000
+ alignItems: 'center',
1001
+ flex: 1
1002
+ }
1003
+ }, /*#__PURE__*/React.createElement(Text, {
1004
+ theme: "h1"
1005
+ }, item.name), item.notification_group_id === 'custom_group' && /*#__PURE__*/React.createElement(View, {
1006
+ transparent: true,
1007
+ style: {
1008
+ marginLeft: 8,
1009
+ paddingHorizontal: 6,
1010
+ paddingVertical: 2,
1011
+ backgroundColor: Colors.buttons.background.action,
1012
+ borderRadius: 4
1013
+ }
1014
+ }, /*#__PURE__*/React.createElement(Text, {
1015
+ theme: "description",
1016
+ color: Colors.text.white,
1017
+ style: {
1018
+ fontSize: 10
1019
+ }
1020
+ }, "CUSTOM"))), item.player_count !== undefined && /*#__PURE__*/React.createElement(View, {
1021
+ transparent: true,
1022
+ style: {
1023
+ marginLeft: 8,
1024
+ paddingHorizontal: 8,
1025
+ paddingVertical: 4,
1026
+ backgroundColor: Colors.views.header,
1027
+ borderRadius: 4
1028
+ }
1029
+ }, /*#__PURE__*/React.createElement(Text, {
1030
+ theme: "description",
1031
+ style: {
1032
+ fontSize: 12,
1033
+ fontWeight: '600'
1034
+ }
1035
+ }, item.player_count.toLocaleString(), " users"))), item.description && /*#__PURE__*/React.createElement(Text, {
1036
+ theme: "description",
1037
+ style: {
1038
+ marginTop: 4
1039
+ }
1040
+ }, item.description))),
1041
+ ListEmptyComponent: /*#__PURE__*/React.createElement(View, {
1042
+ style: {
1043
+ padding: 40,
1044
+ alignItems: 'center'
1045
+ }
1046
+ }, /*#__PURE__*/React.createElement(Text, {
1047
+ theme: "description"
1048
+ }, "No groups found"))
1049
+ })), /*#__PURE__*/React.createElement(View, {
1050
+ type: "footer",
1051
+ style: {
1052
+ flexDirection: 'row',
1053
+ alignItems: 'center',
1054
+ padding: 10,
1055
+ borderBottomRightRadius: 8,
1056
+ borderBottomLeftRadius: 8
1057
+ }
1058
+ }, /*#__PURE__*/React.createElement(Button, {
1059
+ style: {
1060
+ flex: 1
1061
+ },
1062
+ type: "close",
1063
+ title: "CLOSE",
1064
+ onPress: () => setShowGroupsModal(false)
1065
+ })))), showEditCustomGroupModal && /*#__PURE__*/React.createElement(View, {
1066
+ type: "blur",
1067
+ style: {
1068
+ position: 'absolute',
1069
+ top: 0,
1070
+ left: 0,
1071
+ right: 0,
1072
+ bottom: 0,
1073
+ padding: 20
1074
+ }
1075
+ }, /*#__PURE__*/React.createElement(View, {
1076
+ float: true,
1077
+ style: {
1078
+ maxHeight: '80%',
1079
+ minHeight: 400
1080
+ }
1081
+ }, /*#__PURE__*/React.createElement(View, {
1082
+ type: "header",
1083
+ style: {
1084
+ flexDirection: 'row',
1085
+ alignItems: 'center',
1086
+ padding: 10,
1087
+ borderTopRightRadius: 8,
1088
+ borderTopLeftRadius: 8
1089
+ }
1090
+ }, /*#__PURE__*/React.createElement(View, {
1091
+ transparent: true,
1092
+ style: {
1093
+ flex: 1
1094
+ }
1095
+ }, /*#__PURE__*/React.createElement(Text, {
1096
+ theme: "h1"
1097
+ }, "Edit Custom Recipients"), /*#__PURE__*/React.createElement(Text, {
1098
+ theme: "description",
1099
+ style: {
1100
+ marginTop: 3
1101
+ }
1102
+ }, "Paste comma-separated player IDs"))), /*#__PURE__*/React.createElement(View, {
1103
+ style: {
1104
+ flex: 1,
1105
+ padding: 15
1106
+ }
1107
+ }, /*#__PURE__*/React.createElement(Text, {
1108
+ theme: "h2",
1109
+ style: {
1110
+ marginBottom: 10
1111
+ }
1112
+ }, "Player IDs (CSV format)"), /*#__PURE__*/React.createElement(TextInput, {
1113
+ value: customGroupCsv,
1114
+ onFocusPosition: onFocusPosition,
1115
+ placeholder: "98,100,2485,etc",
1116
+ onChangeText: setCustomGroupCsv,
1117
+ multiline: true,
1118
+ numberOfLines: 10,
1119
+ style: {
1120
+ padding: 12,
1121
+ borderRadius: 8,
1122
+ minHeight: 200,
1123
+ textAlignVertical: 'top'
1124
+ }
1125
+ }), /*#__PURE__*/React.createElement(Text, {
1126
+ theme: "description",
1127
+ style: {
1128
+ marginTop: 8
1129
+ }
1130
+ }, "Current count: ", customGroupCsv.split(',').filter(id => id.trim().length > 0).length, " IDs")), /*#__PURE__*/React.createElement(View, {
1131
+ type: "footer",
1132
+ style: {
1133
+ flexDirection: 'row',
1134
+ alignItems: 'center',
1135
+ padding: 10,
1136
+ borderBottomRightRadius: 8,
1137
+ borderBottomLeftRadius: 8
1138
+ }
1139
+ }, /*#__PURE__*/React.createElement(Button, {
1140
+ style: {
1141
+ flex: 1,
1142
+ marginRight: 5
1143
+ },
1144
+ type: "close",
1145
+ title: "CANCEL",
1146
+ onPress: () => setShowEditCustomGroupModal(false)
1147
+ }), /*#__PURE__*/React.createElement(Button, {
1148
+ style: {
1149
+ flex: 1
1150
+ },
1151
+ type: "success",
1152
+ title: "SAVE",
1153
+ onPress: handleSaveCustomGroup
1154
+ })))));
1155
+ };
1156
+ export default NotificationManager;
1157
+ export { NotificationApi, NotificationHelpers };
1158
+ //# sourceMappingURL=index.js.map