@controleonline/ui-common 1.2.70

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 (203) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.github/workflows/npmjs.yml +35 -0
  6. package/.github/workflows/pull-request-checks.yml +19 -0
  7. package/.scrutinizer.yml +24 -0
  8. package/FUNDING.yml +1 -0
  9. package/package.json +28 -0
  10. package/src/api/fetch.js +74 -0
  11. package/src/api/index.js +155 -0
  12. package/src/api/localDB.js +131 -0
  13. package/src/api/queue.js +66 -0
  14. package/src/react/assets/tap.mp3 +0 -0
  15. package/src/react/components/AddImportModal.js +157 -0
  16. package/src/react/components/AddImportModal.styles.js +81 -0
  17. package/src/react/components/BackgroundRuntimeBridge.js +219 -0
  18. package/src/react/components/BottomNavigationBar.config.js +238 -0
  19. package/src/react/components/BottomNavigationBar.js +120 -0
  20. package/src/react/components/BottomNavigationBar.styles.js +110 -0
  21. package/src/react/components/CategoryForm.js +572 -0
  22. package/src/react/components/CategoryForm.styles.js +334 -0
  23. package/src/react/components/ConfirmModal.js +24 -0
  24. package/src/react/components/ConfirmModal.styles.js +43 -0
  25. package/src/react/components/ContextHelpButton.js +69 -0
  26. package/src/react/components/ContextHelpButton.styles.js +62 -0
  27. package/src/react/components/DefaultProvider.native.js +848 -0
  28. package/src/react/components/DefaultProvider.styles.js +21 -0
  29. package/src/react/components/DefaultProvider.web.js +909 -0
  30. package/src/react/components/DeliveryPushBridge.native.js +314 -0
  31. package/src/react/components/DeliveryPushBridge.web.js +5 -0
  32. package/src/react/components/DeviceAlertSoundService.js +499 -0
  33. package/src/react/components/EntityLogContent.js +909 -0
  34. package/src/react/components/EntityLogContent.styles.js +400 -0
  35. package/src/react/components/EventBus.js +12 -0
  36. package/src/react/components/KioskModeBridge.js +65 -0
  37. package/src/react/components/LauncherModeBridge.js +47 -0
  38. package/src/react/components/LinkedOrderProductsTab.js +388 -0
  39. package/src/react/components/LinkedOrderProductsTab.styles.js +180 -0
  40. package/src/react/components/ManagerPushBridge.native.js +345 -0
  41. package/src/react/components/MessageService.js +314 -0
  42. package/src/react/components/Paywall/Google.js +89 -0
  43. package/src/react/components/PrintService.js +778 -0
  44. package/src/react/components/ProductCatalogCacheService.js +45 -0
  45. package/src/react/components/RemoteCheckoutService.js +292 -0
  46. package/src/react/components/RuntimeBottomNavigationBar.js +151 -0
  47. package/src/react/components/RuntimeInfoFooter.js +214 -0
  48. package/src/react/components/RuntimeInfoFooter.styles.js +56 -0
  49. package/src/react/components/SystemErrorToast.js +29 -0
  50. package/src/react/components/SystemErrorToast.styles.js +38 -0
  51. package/src/react/components/TouchFeedbackProvider.js +65 -0
  52. package/src/react/components/UnifiedPaymentBar.js +155 -0
  53. package/src/react/components/UnifiedPaymentBar.styles.js +174 -0
  54. package/src/react/components/WebsocketListener.native.js +487 -0
  55. package/src/react/components/WebsocketListener.web.js +102 -0
  56. package/src/react/components/inputs/IdInput.js +49 -0
  57. package/src/react/components/inputs/IdInput.styles.js +26 -0
  58. package/src/react/components/inputs/InvoiceDueDateFilterInput.js +74 -0
  59. package/src/react/components/inputs/InvoiceDueDateFilterInput.styles.js +34 -0
  60. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.js +115 -0
  61. package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.styles.js +39 -0
  62. package/src/react/components/lists/CategoriesList.js +56 -0
  63. package/src/react/components/lists/PaymentTypeList.js +55 -0
  64. package/src/react/components/lists/ReceiverList.js +56 -0
  65. package/src/react/components/lists/SelectList.styles.js +18 -0
  66. package/src/react/components/lists/StatusList.js +55 -0
  67. package/src/react/components/lists/WalletList.js +54 -0
  68. package/src/react/components/radio/Spotify.js +144 -0
  69. package/src/react/components/radio/Spotify.styles.js +32 -0
  70. package/src/react/components/radio/YouTube.js +82 -0
  71. package/src/react/components/radio/YouTube.styles.js +16 -0
  72. package/src/react/components/toastConfig.js +16 -0
  73. package/src/react/config/deviceConfigBootstrap.js +571 -0
  74. package/src/react/index.js +4 -0
  75. package/src/react/localStorage.js +30 -0
  76. package/src/react/pages/EntityLogPage.js +124 -0
  77. package/src/react/pages/EntityLogPage.styles.js +17 -0
  78. package/src/react/pages/GenericLogPage.js +760 -0
  79. package/src/react/pages/GenericLogPage.styles.js +401 -0
  80. package/src/react/pages/Imports.js +361 -0
  81. package/src/react/pages/Imports.styles.js +223 -0
  82. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +462 -0
  83. package/src/react/pages/SettingsPage/index.js +1171 -0
  84. package/src/react/pages/SettingsPage/index.styles.js +122 -0
  85. package/src/react/print/jobs.js +147 -0
  86. package/src/react/print/providers/local.js +53 -0
  87. package/src/react/print/providers/remote.js +81 -0
  88. package/src/react/print/selection.js +296 -0
  89. package/src/react/print/usePrintButtonController.js +474 -0
  90. package/src/react/router/routes.js +42 -0
  91. package/src/react/services/NetworkPrinterService.native.js +391 -0
  92. package/src/react/services/NetworkPrinterService.web.js +15 -0
  93. package/src/react/stores/index.js +128 -0
  94. package/src/react/stores/storeErrorBridge.js +36 -0
  95. package/src/react/utils/backgroundRuntimeRegistration.js +69 -0
  96. package/src/react/utils/cashPayment.js +87 -0
  97. package/src/react/utils/commercialDocumentOrders.js +269 -0
  98. package/src/react/utils/dateRangeFilter.js +264 -0
  99. package/src/react/utils/deviceRuntime.js +193 -0
  100. package/src/react/utils/entityDisplay.js +231 -0
  101. package/src/react/utils/entityLog.js +649 -0
  102. package/src/react/utils/fileUrl.js +102 -0
  103. package/src/react/utils/frontendDebugLog.js +45 -0
  104. package/src/react/utils/googleMapsConfig.js +49 -0
  105. package/src/react/utils/importStatus.js +100 -0
  106. package/src/react/utils/invoiceDueDateMessages.js +14 -0
  107. package/src/react/utils/invoicePresentation.js +46 -0
  108. package/src/react/utils/logSettings.js +143 -0
  109. package/src/react/utils/maintenanceSettings.js +53 -0
  110. package/src/react/utils/managerOrderNotifications.js +523 -0
  111. package/src/react/utils/mapNavigation.js +76 -0
  112. package/src/react/utils/menuCatalogConfig.js +8 -0
  113. package/src/react/utils/menuCatalogDownload.js +157 -0
  114. package/src/react/utils/networkCameraDevices.js +175 -0
  115. package/src/react/utils/networkDeviceProfiles.js +135 -0
  116. package/src/react/utils/normalizedCatalogDownload.js +157 -0
  117. package/src/react/utils/notificationNavigation.js +94 -0
  118. package/src/react/utils/notificationSound.js +42 -0
  119. package/src/react/utils/paymentDevices.js +397 -0
  120. package/src/react/utils/paymentGatewayExecution.js +91 -0
  121. package/src/react/utils/paymentOptions.js +88 -0
  122. package/src/react/utils/peopleDisplay.js +39 -0
  123. package/src/react/utils/printerDevices.js +465 -0
  124. package/src/react/utils/remotePayment.js +61 -0
  125. package/src/react/utils/runtimeFooter.js +475 -0
  126. package/src/react/utils/runtimeLanguage.js +50 -0
  127. package/src/react/utils/runtimeMenu.js +224 -0
  128. package/src/react/utils/screenMetrics.js +30 -0
  129. package/src/react/utils/shopConfig.js +294 -0
  130. package/src/react/utils/shopFranchises.js +114 -0
  131. package/src/react/utils/socketRuntimePipeline.js +454 -0
  132. package/src/react/utils/spoolAckTracker.js +77 -0
  133. package/src/react/utils/storeColumns.js +427 -0
  134. package/src/react/utils/systemErrorChannel.js +109 -0
  135. package/src/react/utils/systemErrorMessage.js +67 -0
  136. package/src/react/utils/toastPresentation.js +53 -0
  137. package/src/react/utils/websocketSharedRuntime.js +826 -0
  138. package/src/store/acl/getters.js +4 -0
  139. package/src/store/acl/index.js +19 -0
  140. package/src/store/acl/mutation_types.js +4 -0
  141. package/src/store/acl/mutations.js +17 -0
  142. package/src/store/address/city.js +22 -0
  143. package/src/store/categories/category_file/index.js +25 -0
  144. package/src/store/categories/index.js +93 -0
  145. package/src/store/configs/customActions.js +131 -0
  146. package/src/store/configs/index.js +25 -0
  147. package/src/store/connections/customActions.js +26 -0
  148. package/src/store/connections/index.js +124 -0
  149. package/src/store/device/index.js +22 -0
  150. package/src/store/device_config/customActions.js +190 -0
  151. package/src/store/device_config/index.js +28 -0
  152. package/src/store/entity_log/customActions.js +63 -0
  153. package/src/store/entity_log/index.js +25 -0
  154. package/src/store/extra/data.js +85 -0
  155. package/src/store/extra/fields.js +88 -0
  156. package/src/store/file/actions.js +27 -0
  157. package/src/store/file/index.js +24 -0
  158. package/src/store/imports/index.js +88 -0
  159. package/src/store/print/customActions.js +251 -0
  160. package/src/store/print/index.js +28 -0
  161. package/src/store/printer/customActions.js +54 -0
  162. package/src/store/printer/index.js +26 -0
  163. package/src/store/runtime_debug/customActions.js +71 -0
  164. package/src/store/runtime_debug/index.js +30 -0
  165. package/src/store/status/index.js +65 -0
  166. package/src/store/timezones/index.js +43 -0
  167. package/src/store/websocket/customActions.js +24 -0
  168. package/src/store/websocket/index.js +28 -0
  169. package/src/tests/react/components/LauncherModeBridge.test.js +93 -0
  170. package/src/tests/react/config/deviceConfigBootstrap.test.js +337 -0
  171. package/src/tests/react/print/localPrintProvider.test.js +45 -0
  172. package/src/tests/react/utils/backgroundRuntimeRegistration.test.js +123 -0
  173. package/src/tests/react/utils/cashPayment.test.js +41 -0
  174. package/src/tests/react/utils/dateRangeFilter.test.mjs +29 -0
  175. package/src/tests/react/utils/entityDisplay.test.js +18 -0
  176. package/src/tests/react/utils/googleMapsConfig.test.js +32 -0
  177. package/src/tests/react/utils/importStatus.test.js +85 -0
  178. package/src/tests/react/utils/invoicePresentation.test.mjs +40 -0
  179. package/src/tests/react/utils/managerOrderNotifications.test.js +127 -0
  180. package/src/tests/react/utils/mapNavigation.test.js +58 -0
  181. package/src/tests/react/utils/notificationNavigation.test.js +39 -0
  182. package/src/tests/react/utils/notificationSound.test.js +27 -0
  183. package/src/tests/react/utils/paymentDevices.test.js +88 -0
  184. package/src/tests/react/utils/runtimeFooter.test.js +429 -0
  185. package/src/tests/react/utils/runtimeLanguage.test.js +80 -0
  186. package/src/tests/react/utils/runtimeMenu.test.js +174 -0
  187. package/src/tests/react/utils/shopFranchises.test.js +82 -0
  188. package/src/tests/react/utils/socketRuntimePipeline.test.js +124 -0
  189. package/src/tests/react/utils/spoolAckTracker.test.js +40 -0
  190. package/src/tests/react/utils/storeErrorBridge.test.js +66 -0
  191. package/src/tests/react/utils/systemErrorChannel.test.js +58 -0
  192. package/src/tests/react/utils/systemErrorMessage.test.js +59 -0
  193. package/src/tests/react/utils/toastPresentation.test.js +75 -0
  194. package/src/tests/react/utils/translate.test.mjs +396 -0
  195. package/src/tests/react/utils/websocketSharedRuntime.test.js +245 -0
  196. package/src/utils/apiEntryPoint.js +31 -0
  197. package/src/utils/appDomain.js +50 -0
  198. package/src/utils/config.js +14 -0
  199. package/src/utils/formatter.js +419 -0
  200. package/src/utils/integrationConfigs.js +79 -0
  201. package/src/utils/methods.js +23 -0
  202. package/src/utils/oauth.js +42 -0
  203. package/src/utils/translate.js +620 -0
@@ -0,0 +1,345 @@
1
+ import React, {useCallback, useEffect, useMemo, useRef} from 'react';
2
+ import {Platform} from 'react-native';
3
+ import * as Notifications from 'expo-notifications';
4
+ import {env as APP_ENV} from '@env';
5
+ import {useStore} from '@store';
6
+ import {
7
+ ensureManagerOrderNotificationPermission,
8
+ isManagerAppType,
9
+ resolveManagerOrderNotificationPreferences,
10
+ } from '@controleonline/ui-common/src/react/utils/managerOrderNotifications';
11
+ import {
12
+ queueNotificationNavigation,
13
+ } from '@controleonline/ui-common/src/react/utils/notificationNavigation';
14
+
15
+ const normalizeText = value => String(value || '').trim();
16
+
17
+ const isPlainObject = value =>
18
+ !!value && typeof value === 'object' && !Array.isArray(value);
19
+
20
+ const extractOrderIdFromResponse = response => {
21
+ const data = response?.notification?.request?.content?.data || {};
22
+ const candidate =
23
+ data?.orderId ||
24
+ data?.order_id ||
25
+ data?.order ||
26
+ data?.id ||
27
+ data?.orderDetailsId ||
28
+ '';
29
+
30
+ return normalizeText(candidate).replace(/\D+/g, '');
31
+ };
32
+
33
+ const extractManagerPushToken = metadata =>
34
+ normalizeText(
35
+ metadata?.pushTokens?.manager?.android?.deviceToken || '',
36
+ );
37
+
38
+ const resolvePersistableDeviceEntityId = deviceInfo =>
39
+ normalizeText(deviceInfo?.entityId || deviceInfo?.apiId || deviceInfo?.['@id'])
40
+ .replace(/\D+/g, '');
41
+
42
+ const mergeManagerPushTokenMetadata = (metadata, deviceToken) => {
43
+ const normalizedMetadata = isPlainObject(metadata) ? metadata : {};
44
+ const nextMetadata = {
45
+ ...normalizedMetadata,
46
+ };
47
+
48
+ const pushTokens = isPlainObject(normalizedMetadata.pushTokens)
49
+ ? {...normalizedMetadata.pushTokens}
50
+ : {};
51
+ const managerTokens = isPlainObject(pushTokens.manager)
52
+ ? {...pushTokens.manager}
53
+ : {};
54
+ const androidTokens = isPlainObject(managerTokens.android)
55
+ ? {...managerTokens.android}
56
+ : {};
57
+
58
+ if (normalizeText(deviceToken)) {
59
+ androidTokens.deviceToken = normalizeText(deviceToken);
60
+ managerTokens.android = androidTokens;
61
+ pushTokens.manager = managerTokens;
62
+ nextMetadata.pushTokens = pushTokens;
63
+ return nextMetadata;
64
+ }
65
+
66
+ delete androidTokens.deviceToken;
67
+ if (Object.keys(androidTokens).length > 0) {
68
+ managerTokens.android = androidTokens;
69
+ } else {
70
+ delete managerTokens.android;
71
+ }
72
+
73
+ if (Object.keys(managerTokens).length > 0) {
74
+ pushTokens.manager = managerTokens;
75
+ } else {
76
+ delete pushTokens.manager;
77
+ }
78
+
79
+ if (Object.keys(pushTokens).length > 0) {
80
+ nextMetadata.pushTokens = pushTokens;
81
+ } else {
82
+ delete nextMetadata.pushTokens;
83
+ }
84
+
85
+ return nextMetadata;
86
+ };
87
+
88
+ const ManagerPushBridge = ({device, setDevice}) => {
89
+ const authStore = useStore('auth');
90
+ const deviceStore = useStore('device');
91
+
92
+ const authGetters = authStore.getters;
93
+ const deviceActions = deviceStore.actions;
94
+
95
+ const {isLogged, sessionChecked, user} = authGetters;
96
+ const isManagerRuntime = isManagerAppType(APP_ENV?.APP_TYPE);
97
+ const pushPreferences = useMemo(
98
+ () => resolveManagerOrderNotificationPreferences(user),
99
+ [user],
100
+ );
101
+ const pushEnabled = isManagerRuntime && pushPreferences.pushEnabled;
102
+
103
+ const currentDeviceRef = useRef(device || {});
104
+ const pushTokenListenerRef = useRef(null);
105
+ const notificationResponseListenerRef = useRef(null);
106
+
107
+ const persistLocalDevice = useCallback(
108
+ nextDevice => {
109
+ if (!nextDevice?.id) {
110
+ return;
111
+ }
112
+
113
+ const currentSerializedDevice = JSON.stringify(
114
+ currentDeviceRef.current || {},
115
+ );
116
+ const nextSerializedDevice = JSON.stringify(nextDevice || {});
117
+ if (currentSerializedDevice === nextSerializedDevice) {
118
+ return;
119
+ }
120
+
121
+ currentDeviceRef.current = nextDevice;
122
+ setDevice(nextDevice);
123
+ localStorage.setItem('device', JSON.stringify(nextDevice));
124
+ deviceActions.setItem(nextDevice);
125
+ },
126
+ [deviceActions, setDevice],
127
+ );
128
+
129
+ const applyManagerPushToken = useCallback(
130
+ async (deviceToken, {persistImmediately = false} = {}) => {
131
+ const currentDevice = currentDeviceRef.current || {};
132
+ if (!currentDevice?.id) {
133
+ return false;
134
+ }
135
+
136
+ const normalizedToken = normalizeText(deviceToken);
137
+ const currentToken = extractManagerPushToken(currentDevice?.metadata);
138
+
139
+ if (
140
+ !persistImmediately &&
141
+ normalizedToken &&
142
+ currentToken === normalizedToken
143
+ ) {
144
+ return true;
145
+ }
146
+
147
+ if (!persistImmediately && !normalizedToken && !currentToken) {
148
+ return true;
149
+ }
150
+
151
+ if (persistImmediately && !normalizedToken && !currentToken) {
152
+ return true;
153
+ }
154
+
155
+ const nextMetadata = mergeManagerPushTokenMetadata(
156
+ currentDevice?.metadata,
157
+ normalizedToken,
158
+ );
159
+ const nextDevice = {
160
+ ...currentDevice,
161
+ metadata: nextMetadata,
162
+ };
163
+
164
+ persistLocalDevice(nextDevice);
165
+
166
+ if (!persistImmediately) {
167
+ return true;
168
+ }
169
+
170
+ const entityId = resolvePersistableDeviceEntityId(currentDevice);
171
+ if (!entityId) {
172
+ return true;
173
+ }
174
+
175
+ try {
176
+ await deviceActions.save({
177
+ id: entityId,
178
+ device: currentDevice.device || currentDevice.id,
179
+ alias: currentDevice.alias,
180
+ metadata: nextMetadata,
181
+ });
182
+ } catch (error) {
183
+ console.warn('Erro ao limpar push token do Gestor no logout', error);
184
+ }
185
+
186
+ return true;
187
+ },
188
+ [deviceActions, persistLocalDevice],
189
+ );
190
+
191
+ useEffect(() => {
192
+ currentDeviceRef.current = device || {};
193
+ }, [device]);
194
+
195
+ useEffect(() => {
196
+ if (Platform.OS !== 'android' || !isManagerRuntime) {
197
+ return;
198
+ }
199
+
200
+ global.clearManagerPushTokenOnLogout = () =>
201
+ applyManagerPushToken('', {persistImmediately: true});
202
+
203
+ return () => {
204
+ if (global.clearManagerPushTokenOnLogout) {
205
+ delete global.clearManagerPushTokenOnLogout;
206
+ }
207
+ };
208
+ }, [applyManagerPushToken]);
209
+
210
+ useEffect(() => {
211
+ if (
212
+ Platform.OS !== 'android' ||
213
+ !isManagerRuntime ||
214
+ !sessionChecked ||
215
+ !currentDeviceRef.current?.id
216
+ ) {
217
+ return;
218
+ }
219
+
220
+ let cancelled = false;
221
+
222
+ const configurePushSupport = async () => {
223
+ if (!isLogged) {
224
+ const currentToken = extractManagerPushToken(
225
+ currentDeviceRef.current?.metadata,
226
+ );
227
+
228
+ if (currentToken) {
229
+ await applyManagerPushToken('', {persistImmediately: true});
230
+ }
231
+
232
+ return;
233
+ }
234
+
235
+ const permissionStatus = await ensureManagerOrderNotificationPermission();
236
+
237
+ if (cancelled) {
238
+ return;
239
+ }
240
+
241
+ if (permissionStatus !== 'granted') {
242
+ await applyManagerPushToken('', {persistImmediately: true});
243
+ return;
244
+ }
245
+
246
+ if (!pushEnabled) {
247
+ const currentToken = extractManagerPushToken(
248
+ currentDeviceRef.current?.metadata,
249
+ );
250
+
251
+ if (currentToken) {
252
+ await applyManagerPushToken('', {persistImmediately: true});
253
+ }
254
+
255
+ return;
256
+ }
257
+
258
+ const devicePushToken = await Notifications.getDevicePushTokenAsync();
259
+ const nextToken = normalizeText(devicePushToken?.data);
260
+
261
+ if (cancelled) {
262
+ return;
263
+ }
264
+
265
+ if (!nextToken) {
266
+ await applyManagerPushToken('', {persistImmediately: true});
267
+ } else {
268
+ await applyManagerPushToken(nextToken, {persistImmediately: true});
269
+ }
270
+
271
+ if (!pushTokenListenerRef.current) {
272
+ pushTokenListenerRef.current = Notifications.addPushTokenListener(
273
+ token => {
274
+ const nextListenerToken = normalizeText(token?.data);
275
+ if (!nextListenerToken) {
276
+ return;
277
+ }
278
+
279
+ void applyManagerPushToken(nextListenerToken, {
280
+ persistImmediately: true,
281
+ });
282
+ },
283
+ );
284
+ }
285
+
286
+ if (!notificationResponseListenerRef.current) {
287
+ notificationResponseListenerRef.current =
288
+ Notifications.addNotificationResponseReceivedListener(response => {
289
+ const orderId = extractOrderIdFromResponse(response);
290
+ if (!orderId) {
291
+ return;
292
+ }
293
+
294
+ queueNotificationNavigation('OrderDetails', {id: orderId});
295
+
296
+ if (typeof Notifications.clearLastNotificationResponseAsync === 'function') {
297
+ Notifications.clearLastNotificationResponseAsync().catch(() => {});
298
+ }
299
+ });
300
+ }
301
+
302
+ if (typeof Notifications.getLastNotificationResponseAsync === 'function') {
303
+ const lastResponse = await Notifications.getLastNotificationResponseAsync();
304
+ const orderId = extractOrderIdFromResponse(lastResponse);
305
+
306
+ if (!cancelled && orderId) {
307
+ queueNotificationNavigation('OrderDetails', {id: orderId});
308
+
309
+ if (typeof Notifications.clearLastNotificationResponseAsync === 'function') {
310
+ Notifications.clearLastNotificationResponseAsync().catch(() => {});
311
+ }
312
+ }
313
+ }
314
+ };
315
+
316
+ configurePushSupport().catch(error => {
317
+ console.warn('Erro ao inicializar push nativo do Gestor', error);
318
+ });
319
+
320
+ return () => {
321
+ cancelled = true;
322
+
323
+ if (pushTokenListenerRef.current) {
324
+ pushTokenListenerRef.current.remove();
325
+ pushTokenListenerRef.current = null;
326
+ }
327
+
328
+ if (notificationResponseListenerRef.current) {
329
+ notificationResponseListenerRef.current.remove();
330
+ notificationResponseListenerRef.current = null;
331
+ }
332
+ };
333
+ }, [
334
+ applyManagerPushToken,
335
+ device?.id,
336
+ isLogged,
337
+ isManagerRuntime,
338
+ pushEnabled,
339
+ sessionChecked,
340
+ ]);
341
+
342
+ return null;
343
+ };
344
+
345
+ export default ManagerPushBridge;
@@ -0,0 +1,314 @@
1
+ import React, {createContext, useContext, useEffect, useState} from 'react';
2
+ import {Dimensions} from 'react-native';
3
+ import {
4
+ Portal,
5
+ Dialog,
6
+ Paragraph,
7
+ Button,
8
+ TextInput as PaperTextInput,
9
+ } from 'react-native-paper';
10
+ import {toast, ToastPosition} from '@backpackapp-io/react-native-toast';
11
+ import SystemErrorToast from './SystemErrorToast';
12
+ import {
13
+ TOAST_DEFAULT_BOTTOM_OFFSET,
14
+ TOAST_DEFAULT_DURATION,
15
+ TOAST_DEFAULT_TOP_OFFSET,
16
+ TOAST_MODAL_COUNT_KEY,
17
+ TOAST_PROVIDER_KEYS,
18
+ } from './toastConfig';
19
+ import {
20
+ withErrorToastStyles,
21
+ withWarningToastStyles,
22
+ } from '@controleonline/ui-common/src/react/utils/toastPresentation';
23
+ import {
24
+ publishSystemError,
25
+ subscribeSystemErrors,
26
+ } from '@controleonline/ui-common/src/react/utils/systemErrorChannel'
27
+
28
+ const MessageContext = createContext();
29
+
30
+ export const useMessage = () => useContext(MessageContext);
31
+
32
+ const normalizeMessage = message => {
33
+ if (message === undefined || message === null) return ''
34
+ return message
35
+ }
36
+
37
+ const getDefaultProviderKey = () => {
38
+ const modalDepth = Number(global?.[TOAST_MODAL_COUNT_KEY] || 0)
39
+ if (modalDepth > 0) {
40
+ return TOAST_PROVIDER_KEYS.PERSIST
41
+ }
42
+
43
+ return TOAST_PROVIDER_KEYS.ROOT
44
+ }
45
+
46
+ const normalizeOptions = (options = {}, fallbackPosition = 'top') => {
47
+ const {
48
+ duration = TOAST_DEFAULT_DURATION,
49
+ position = fallbackPosition,
50
+ offsetTop,
51
+ offsetBottom,
52
+ providerKey,
53
+ styles: stylesOverride,
54
+ ...rest
55
+ } = options
56
+
57
+ const pressableAdjustments = {}
58
+
59
+ if (position === 'center') {
60
+ const screenCenterOffset = Math.max(
61
+ 0,
62
+ Math.round(Dimensions.get('window').height * 0.45) -
63
+ TOAST_DEFAULT_TOP_OFFSET,
64
+ )
65
+ if (screenCenterOffset > 0) {
66
+ pressableAdjustments.marginTop = screenCenterOffset
67
+ }
68
+ }
69
+
70
+ if (position !== 'bottom' && typeof offsetTop === 'number') {
71
+ const topDiff = offsetTop - TOAST_DEFAULT_TOP_OFFSET
72
+ if (topDiff !== 0) {
73
+ pressableAdjustments.marginTop =
74
+ (pressableAdjustments.marginTop || 0) + topDiff
75
+ }
76
+ }
77
+
78
+ if (position === 'bottom' && typeof offsetBottom === 'number') {
79
+ const bottomDiff = offsetBottom - TOAST_DEFAULT_BOTTOM_OFFSET
80
+ if (bottomDiff !== 0) {
81
+ pressableAdjustments.marginBottom = bottomDiff
82
+ }
83
+ }
84
+
85
+ const mergedStyles =
86
+ Object.keys(pressableAdjustments).length > 0
87
+ ? {
88
+ ...(stylesOverride || {}),
89
+ pressable: {
90
+ ...(stylesOverride?.pressable || {}),
91
+ ...pressableAdjustments,
92
+ },
93
+ }
94
+ : stylesOverride
95
+
96
+ return {
97
+ duration,
98
+ position: position === 'bottom' ? ToastPosition.BOTTOM : ToastPosition.TOP,
99
+ providerKey: providerKey || getDefaultProviderKey(),
100
+ ...rest,
101
+ ...(mergedStyles ? {styles: mergedStyles} : {}),
102
+ }
103
+ }
104
+
105
+ export const MessageProvider = ({children}) => {
106
+ const [dialog, setDialog] = useState({
107
+ visible: false,
108
+ title: '',
109
+ message: '',
110
+ onConfirm: null,
111
+ onCancel: null,
112
+ });
113
+ const [prompt, setPrompt] = useState({
114
+ visible: false,
115
+ title: '',
116
+ message: '',
117
+ value: '',
118
+ placeholder: '',
119
+ confirmLabel: '',
120
+ cancelLabel: '',
121
+ keyboardType: 'default',
122
+ resolve: null,
123
+ });
124
+
125
+ const showToast = (message, options = {}) => {
126
+ return toast(normalizeMessage(message), normalizeOptions(options, 'center'));
127
+ };
128
+
129
+ // Backward-compatible aliases used by older module code
130
+ const showSuccess = (message, options = {}) => {
131
+ return toast.success(
132
+ normalizeMessage(message),
133
+ normalizeOptions(
134
+ {position: 'top', offsetTop: TOAST_DEFAULT_TOP_OFFSET, ...options},
135
+ 'top',
136
+ ),
137
+ );
138
+ };
139
+
140
+ const showError = (message, options = {}) => {
141
+ return publishSystemError(message, options)
142
+ };
143
+
144
+ const showWarning = (message, options = {}) => {
145
+ return showToast(
146
+ message,
147
+ withWarningToastStyles({
148
+ position: 'top',
149
+ offsetTop: TOAST_DEFAULT_TOP_OFFSET,
150
+ ...options,
151
+ }),
152
+ );
153
+ };
154
+
155
+ const showInfo = (message, options = {}) => {
156
+ return showToast(message, {
157
+ position: 'top',
158
+ offsetTop: TOAST_DEFAULT_TOP_OFFSET,
159
+ ...options,
160
+ });
161
+ };
162
+
163
+ useEffect(() => {
164
+ return subscribeSystemErrors(({error, message, options = {}}) => {
165
+ toast.error(
166
+ <SystemErrorToast error={error || message} />,
167
+ normalizeOptions(
168
+ withErrorToastStyles({
169
+ position: 'top',
170
+ offsetTop: TOAST_DEFAULT_TOP_OFFSET,
171
+ ...options,
172
+ }),
173
+ 'top',
174
+ ),
175
+ )
176
+ })
177
+ }, [])
178
+
179
+ const showDialog = ({title, message, onConfirm, onCancel}) => {
180
+ setDialog({
181
+ visible: true,
182
+ title,
183
+ message,
184
+ onConfirm,
185
+ onCancel,
186
+ });
187
+ };
188
+
189
+ const hideDialog = () => setDialog(d => ({...d, visible: false}));
190
+
191
+ const hidePrompt = () =>
192
+ setPrompt({
193
+ visible: false,
194
+ title: '',
195
+ message: '',
196
+ value: '',
197
+ placeholder: '',
198
+ confirmLabel: '',
199
+ cancelLabel: '',
200
+ keyboardType: 'default',
201
+ resolve: null,
202
+ });
203
+
204
+ const showPrompt = ({
205
+ title,
206
+ message,
207
+ defaultValue = '',
208
+ placeholder = '',
209
+ confirmLabel = '',
210
+ cancelLabel = '',
211
+ keyboardType = 'default',
212
+ }) =>
213
+ new Promise(resolve => {
214
+ setPrompt({
215
+ visible: true,
216
+ title: normalizeMessage(title),
217
+ message: normalizeMessage(message),
218
+ value: normalizeMessage(defaultValue),
219
+ placeholder: normalizeMessage(placeholder),
220
+ confirmLabel: normalizeMessage(confirmLabel),
221
+ cancelLabel: normalizeMessage(cancelLabel),
222
+ keyboardType,
223
+ resolve,
224
+ });
225
+ });
226
+
227
+ return (
228
+ <MessageContext.Provider
229
+ value={{
230
+ showToast,
231
+ showDialog,
232
+ showPrompt,
233
+ showSuccess,
234
+ showError,
235
+ showWarning,
236
+ showInfo,
237
+ }}>
238
+ {children}
239
+
240
+ <Portal>
241
+ <Dialog visible={dialog.visible} onDismiss={hideDialog}>
242
+ <Dialog.Title>{dialog.title}</Dialog.Title>
243
+ <Dialog.Content>
244
+ <Paragraph>{dialog.message}</Paragraph>
245
+ </Dialog.Content>
246
+ <Dialog.Actions>
247
+ <Button
248
+ onPress={() => {
249
+ hideDialog();
250
+ dialog.onCancel?.();
251
+ }}>
252
+ Cancelar
253
+ </Button>
254
+ <Button
255
+ onPress={() => {
256
+ hideDialog();
257
+ dialog.onConfirm?.();
258
+ }}>
259
+ Confirmar
260
+ </Button>
261
+ </Dialog.Actions>
262
+ </Dialog>
263
+
264
+ <Dialog
265
+ visible={prompt.visible}
266
+ onDismiss={() => {
267
+ prompt.resolve?.(null);
268
+ hidePrompt();
269
+ }}>
270
+ <Dialog.Title>{prompt.title}</Dialog.Title>
271
+ <Dialog.Content>
272
+ {!!prompt.message && <Paragraph>{prompt.message}</Paragraph>}
273
+ <PaperTextInput
274
+ autoFocus
275
+ mode="outlined"
276
+ value={prompt.value}
277
+ onChangeText={value => {
278
+ setPrompt(currentPrompt => ({
279
+ ...currentPrompt,
280
+ value,
281
+ }));
282
+ }}
283
+ keyboardType={prompt.keyboardType || 'default'}
284
+ placeholder={prompt.placeholder}
285
+ returnKeyType="done"
286
+ onSubmitEditing={() => {
287
+ const resolvedValue = String(prompt.value || '').trim();
288
+ prompt.resolve?.(resolvedValue || null);
289
+ hidePrompt();
290
+ }}
291
+ />
292
+ </Dialog.Content>
293
+ <Dialog.Actions>
294
+ <Button
295
+ onPress={() => {
296
+ prompt.resolve?.(null);
297
+ hidePrompt();
298
+ }}>
299
+ {prompt.cancelLabel || 'Cancelar'}
300
+ </Button>
301
+ <Button
302
+ onPress={() => {
303
+ const resolvedValue = String(prompt.value || '').trim();
304
+ prompt.resolve?.(resolvedValue || null);
305
+ hidePrompt();
306
+ }}>
307
+ {prompt.confirmLabel || 'Confirmar'}
308
+ </Button>
309
+ </Dialog.Actions>
310
+ </Dialog>
311
+ </Portal>
312
+ </MessageContext.Provider>
313
+ );
314
+ };