@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,499 @@
1
+ /*
2
+ * Regra do alerta operacional
3
+ * - `order.created` toca o som configurado para o device ou usuario.
4
+ * - Sem URL customizada, usar o fallback empacotado `caixa.m4a`.
5
+ * - O aviso sonoro faz parte do mesmo evento que atualiza os displays.
6
+ */
7
+
8
+ import React, {useCallback, useEffect, useMemo, useRef} from 'react';
9
+ import {createAudioPlayer} from 'expo-audio';
10
+ import {NativeModules, Platform} from 'react-native';
11
+ import {useStore, useStores} from '@store';
12
+ import {env as APP_ENV} from '@env';
13
+ import {api} from '@controleonline/ui-common/src/api';
14
+ import Formatter from '@controleonline/ui-common/src/utils/formatter';
15
+ import {
16
+ DEVICE_ALERT_SOUND_ENABLED_KEY,
17
+ DEVICE_ALERT_SOUND_URL_KEY,
18
+ isTruthyValue,
19
+ parseConfigsObject,
20
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
21
+ import {resolveOrderIdentity} from '@controleonline/ui-orders/src/react/utils/orderIdentity';
22
+ import {
23
+ isManagerAppType,
24
+ resolveManagerFinancialNotificationPreferences,
25
+ resolveManagerOrderNotificationPreferences,
26
+ showManagerFinancialNotification,
27
+ showManagerOrderNotification,
28
+ } from '@controleonline/ui-common/src/react/utils/managerOrderNotifications';
29
+ import {resolveNotificationSoundSource} from '@controleonline/ui-common/src/react/utils/notificationSound';
30
+
31
+ const MAX_PROCESSED_EVENTS = 200;
32
+ const ORDER_CREATED_EVENT = 'order.created';
33
+ const FINANCIAL_ALERT_EVENTS = new Set(['cash.closed', 'store.opened', 'store.closed']);
34
+
35
+ const normalizeEntityId = value => {
36
+ if (value === null || value === undefined || value === '') {
37
+ return '';
38
+ }
39
+
40
+ if (typeof value === 'object') {
41
+ return normalizeEntityId(
42
+ value['@id'] ||
43
+ value.id ||
44
+ value.device ||
45
+ value.queue ||
46
+ value.order ||
47
+ value.orderProductQueue,
48
+ );
49
+ }
50
+
51
+ return String(value).replace(/\D/g, '');
52
+ };
53
+
54
+ const normalizeText = value => String(value || '').trim();
55
+ const DEFAULT_ALERT_SOUND_SOURCE = resolveNotificationSoundSource('');
56
+
57
+ const parseNumericValue = value => {
58
+ if (typeof value === 'number') {
59
+ return Number.isFinite(value) ? value : NaN;
60
+ }
61
+
62
+ const normalizedValue = normalizeText(value);
63
+ if (!normalizedValue) {
64
+ return NaN;
65
+ }
66
+
67
+ const sanitizedValue = normalizedValue
68
+ .replace(/[^\d,.-]/g, '')
69
+ .replace(/\.(?=\d{3}(?:\D|$))/g, '')
70
+ .replace(',', '.');
71
+ const numericValue = Number(sanitizedValue);
72
+
73
+ return Number.isFinite(numericValue) ? numericValue : NaN;
74
+ };
75
+
76
+ const resolveOrderCustomerName = order =>
77
+ normalizeText(
78
+ order?.client?.alias ||
79
+ order?.client?.name ||
80
+ order?.customer?.alias ||
81
+ order?.customer?.name ||
82
+ order?.person?.alias ||
83
+ order?.person?.name,
84
+ );
85
+
86
+ const resolveOrderPriceLabel = order => {
87
+ const numericPrice = parseNumericValue(order?.price);
88
+
89
+ if (Number.isFinite(numericPrice) && numericPrice > 0) {
90
+ return Formatter.formatMoney(numericPrice);
91
+ }
92
+
93
+ return '';
94
+ };
95
+
96
+ const isFilledObject = value =>
97
+ !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
98
+
99
+ const collectMessages = (messages, message) => {
100
+ const nextMessages = Array.isArray(messages)
101
+ ? messages.filter(isFilledObject)
102
+ : [];
103
+
104
+ if (isFilledObject(message)) {
105
+ nextMessages.push(message);
106
+ }
107
+
108
+ return nextMessages;
109
+ };
110
+
111
+ const buildMessageFingerprint = message => {
112
+ const payload = {...message};
113
+ delete payload.store;
114
+
115
+ return JSON.stringify({
116
+ type: normalizeText(message?.event || message?.action || message?.command),
117
+ companyId: normalizeEntityId(
118
+ message?.company?.id || message?.companyId || message?.company,
119
+ ),
120
+ orderId: normalizeEntityId(message?.order),
121
+ queueId: normalizeEntityId(message?.queue),
122
+ orderProductQueueId: normalizeEntityId(
123
+ message?.orderProductQueue ||
124
+ message?.order_product_queue ||
125
+ message?.orderProductQueueId ||
126
+ message?.id,
127
+ ),
128
+ deviceId: normalizeEntityId(message?.device),
129
+ sentAt: normalizeText(message?.sentAt),
130
+ printType: normalizeText(message?.printType),
131
+ payload,
132
+ });
133
+ };
134
+
135
+ const isRelevantOrderCreatedMessage = message => {
136
+ const store = normalizeText(message?.store);
137
+ const event = normalizeText(message?.event);
138
+ const realStatus = normalizeText(message?.realStatus).toLowerCase();
139
+
140
+ return (
141
+ store === 'orders' &&
142
+ event === ORDER_CREATED_EVENT &&
143
+ realStatus === 'open'
144
+ );
145
+ };
146
+
147
+ const isRelevantFinancialMessage = message => {
148
+ const store = normalizeText(message?.store);
149
+ const event = normalizeText(message?.event);
150
+
151
+ return store === 'orders' && FINANCIAL_ALERT_EVENTS.has(event);
152
+ };
153
+
154
+ const isMessageForCurrentCompany = (message, currentCompanyId) => {
155
+ if (!currentCompanyId) {
156
+ return true;
157
+ }
158
+
159
+ const messageCompanyId = normalizeEntityId(
160
+ message?.company?.id || message?.companyId || message?.company,
161
+ );
162
+
163
+ return !messageCompanyId || messageCompanyId === currentCompanyId;
164
+ };
165
+
166
+ const DeviceAlertSoundService = () => {
167
+ const authStore = useStore('auth');
168
+ const peopleStore = useStore('people');
169
+ const deviceConfigStore = useStore('device_config');
170
+ const ordersStore = useStores(state => state?.orders || {getters: {}, actions: {}});
171
+
172
+ const processedEventsRef = useRef(new Map());
173
+ const playerRef = useRef(null);
174
+ const playerSourceRef = useRef('');
175
+
176
+ const {user} = authStore.getters;
177
+ const {currentCompany} = peopleStore.getters;
178
+ const deviceConfig = useMemo(
179
+ () => parseConfigsObject(deviceConfigStore.getters?.item?.configs),
180
+ [deviceConfigStore.getters?.item?.configs],
181
+ );
182
+ const managerOrderNotificationPreferences = useMemo(
183
+ () => resolveManagerOrderNotificationPreferences(user),
184
+ [user],
185
+ );
186
+ const managerFinancialNotificationPreferences = useMemo(
187
+ () => resolveManagerFinancialNotificationPreferences(user),
188
+ [user],
189
+ );
190
+
191
+ const isManagerRuntime = isManagerAppType(APP_ENV?.APP_TYPE);
192
+ const currentCompanyId = normalizeEntityId(currentCompany?.id);
193
+ const managerPushEnabled =
194
+ isManagerRuntime && managerOrderNotificationPreferences.pushEnabled;
195
+ const managerFinancialCashCloseEnabled =
196
+ isManagerRuntime && managerFinancialNotificationPreferences.cashClosePushEnabled;
197
+ const managerFinancialStoreCloseEnabled =
198
+ isManagerRuntime && managerFinancialNotificationPreferences.storeClosePushEnabled;
199
+ const managerSoundEnabled =
200
+ managerPushEnabled && managerOrderNotificationPreferences.soundEnabled;
201
+ const managerSoundUrl = normalizeText(
202
+ managerOrderNotificationPreferences.soundUrl,
203
+ );
204
+ const deviceAlertSoundEnabled = isTruthyValue(
205
+ deviceConfig?.[DEVICE_ALERT_SOUND_ENABLED_KEY],
206
+ );
207
+ const deviceAlertSoundUrl = normalizeText(
208
+ deviceConfig?.[DEVICE_ALERT_SOUND_URL_KEY],
209
+ );
210
+ const shouldPlayManagerSound = managerSoundEnabled;
211
+ const shouldPlayDeviceAlertSound = deviceAlertSoundEnabled;
212
+ const shouldPlayAlertSound =
213
+ shouldPlayManagerSound || shouldPlayDeviceAlertSound;
214
+ const alertSoundSource = shouldPlayManagerSound
215
+ ? resolveNotificationSoundSource(
216
+ managerSoundUrl,
217
+ DEFAULT_ALERT_SOUND_SOURCE,
218
+ )
219
+ : shouldPlayDeviceAlertSound
220
+ ? resolveNotificationSoundSource(
221
+ deviceAlertSoundUrl,
222
+ DEFAULT_ALERT_SOUND_SOURCE,
223
+ )
224
+ : null;
225
+ const isAndroidBackgroundRuntime =
226
+ Platform.OS === 'android' && !!NativeModules?.BackgroundRuntime;
227
+
228
+ const ensurePlayer = useCallback(
229
+ source => {
230
+ if (!source) {
231
+ return null;
232
+ }
233
+
234
+ try {
235
+ if (!playerRef.current) {
236
+ playerRef.current = createAudioPlayer(source);
237
+ playerSourceRef.current = source;
238
+ return playerRef.current;
239
+ }
240
+
241
+ if (playerSourceRef.current !== source) {
242
+ playerRef.current.replace(source);
243
+ playerSourceRef.current = source;
244
+ }
245
+
246
+ return playerRef.current;
247
+ } catch (error) {
248
+ console.warn('Erro ao preparar aviso sonoro do websocket', error);
249
+ return null;
250
+ }
251
+ },
252
+ [],
253
+ );
254
+
255
+ const playAlertSound = useCallback(async () => {
256
+ const player = ensurePlayer(alertSoundSource);
257
+
258
+ if (!player) {
259
+ return;
260
+ }
261
+
262
+ try {
263
+ if (typeof player.seekTo === 'function') {
264
+ await player.seekTo(0);
265
+ }
266
+ } catch {
267
+ // Alguns players ainda não aceitam seek antes da primeira carga completa.
268
+ }
269
+
270
+ try {
271
+ player.play();
272
+ } catch (error) {
273
+ console.warn('Erro ao tocar aviso sonoro do websocket', error);
274
+ }
275
+ }, [alertSoundSource, ensurePlayer]);
276
+
277
+ const enrichNotificationMessages = useCallback(async messages => {
278
+ const messageList = Array.isArray(messages) ? messages : [];
279
+
280
+ if (!isManagerRuntime || messageList.length !== 1) {
281
+ return messageList;
282
+ }
283
+
284
+ const [message] = messageList;
285
+ const orderId = normalizeEntityId(message?.order);
286
+
287
+ if (!orderId) {
288
+ return messageList;
289
+ }
290
+
291
+ try {
292
+ const order = await api.fetch(`orders/${orderId}`, {});
293
+ const identity = resolveOrderIdentity(order);
294
+
295
+ return [
296
+ {
297
+ ...message,
298
+ notificationHeader:
299
+ normalizeText(identity?.primaryText) ||
300
+ normalizeText(message?.notificationHeader),
301
+ notificationSubheader:
302
+ normalizeText(identity?.secondaryText) ||
303
+ normalizeText(message?.notificationSubheader),
304
+ notificationCustomerName:
305
+ resolveOrderCustomerName(order) ||
306
+ normalizeText(message?.notificationCustomerName),
307
+ notificationPriceLabel:
308
+ resolveOrderPriceLabel(order) ||
309
+ normalizeText(message?.notificationPriceLabel),
310
+ notificationStatusLabel: 'Fila',
311
+ },
312
+ ];
313
+ } catch {
314
+ return [
315
+ {
316
+ ...message,
317
+ notificationStatusLabel:
318
+ normalizeText(message?.notificationStatusLabel) || 'Fila',
319
+ },
320
+ ];
321
+ }
322
+ }, [isManagerRuntime]);
323
+
324
+ const markProcessedKeys = useCallback(keys => {
325
+ keys.forEach(key => {
326
+ processedEventsRef.current.set(key, Date.now());
327
+ if (processedEventsRef.current.size > MAX_PROCESSED_EVENTS) {
328
+ const oldestKey = processedEventsRef.current.keys().next().value;
329
+ processedEventsRef.current.delete(oldestKey);
330
+ }
331
+ });
332
+ }, []);
333
+
334
+ useEffect(() => {
335
+ processedEventsRef.current.clear();
336
+ }, [currentCompanyId]);
337
+
338
+ useEffect(() => {
339
+ return () => {
340
+ if (playerRef.current && typeof playerRef.current.remove === 'function') {
341
+ playerRef.current.remove();
342
+ }
343
+ playerRef.current = null;
344
+ playerSourceRef.current = '';
345
+ };
346
+ }, []);
347
+
348
+ const orderMessages = ordersStore.getters?.messages;
349
+ const orderMessage = ordersStore.getters?.message;
350
+
351
+ useEffect(() => {
352
+ if (isManagerRuntime && Platform.OS === 'android') {
353
+ return;
354
+ }
355
+
356
+ if (isAndroidBackgroundRuntime) {
357
+ return;
358
+ }
359
+
360
+ const incomingMessages = [...collectMessages(orderMessages, orderMessage)].filter(
361
+ message =>
362
+ isRelevantOrderCreatedMessage(message) &&
363
+ isMessageForCurrentCompany(message, currentCompanyId),
364
+ );
365
+
366
+ if (incomingMessages.length === 0) {
367
+ return;
368
+ }
369
+
370
+ const unseenEntries = incomingMessages
371
+ .map(message => ({
372
+ message,
373
+ key: buildMessageFingerprint(message),
374
+ }))
375
+ .filter(entry => entry.key && !processedEventsRef.current.has(entry.key));
376
+
377
+ if (unseenEntries.length === 0) {
378
+ return;
379
+ }
380
+
381
+ const unseenKeys = unseenEntries.map(entry => entry.key);
382
+ const unseenMessages = unseenEntries.map(entry => entry.message);
383
+
384
+ markProcessedKeys(unseenKeys);
385
+
386
+ if (!managerPushEnabled && !shouldPlayAlertSound) {
387
+ return;
388
+ }
389
+
390
+ Promise.resolve(
391
+ managerPushEnabled
392
+ ? enrichNotificationMessages(unseenMessages)
393
+ : unseenMessages,
394
+ ).then(notificationMessages =>
395
+ Promise.allSettled([
396
+ managerPushEnabled
397
+ ? showManagerOrderNotification({
398
+ messages: notificationMessages,
399
+ currentCompany,
400
+ })
401
+ : Promise.resolve(false),
402
+ shouldPlayAlertSound && alertSoundSource
403
+ ? playAlertSound()
404
+ : Promise.resolve(),
405
+ ]),
406
+ );
407
+ }, [
408
+ alertSoundSource,
409
+ currentCompanyId,
410
+ currentCompany,
411
+ enrichNotificationMessages,
412
+ managerPushEnabled,
413
+ markProcessedKeys,
414
+ orderMessage,
415
+ orderMessages,
416
+ playAlertSound,
417
+ shouldPlayAlertSound,
418
+ isManagerRuntime,
419
+ ]);
420
+
421
+ useEffect(() => {
422
+ if (isManagerRuntime && Platform.OS === 'android') {
423
+ return;
424
+ }
425
+
426
+ if (isAndroidBackgroundRuntime) {
427
+ return;
428
+ }
429
+
430
+ const incomingMessages = [...collectMessages(orderMessages, orderMessage)].filter(
431
+ message =>
432
+ isRelevantFinancialMessage(message) &&
433
+ isMessageForCurrentCompany(message, currentCompanyId),
434
+ );
435
+
436
+ if (incomingMessages.length === 0) {
437
+ return;
438
+ }
439
+
440
+ const unseenEntries = incomingMessages
441
+ .map(message => ({
442
+ message,
443
+ key: buildMessageFingerprint(message),
444
+ }))
445
+ .filter(entry => entry.key && !processedEventsRef.current.has(entry.key));
446
+
447
+ if (unseenEntries.length === 0) {
448
+ return;
449
+ }
450
+
451
+ const unseenKeys = unseenEntries.map(entry => entry.key);
452
+ const unseenMessages = unseenEntries.map(entry => entry.message);
453
+
454
+ markProcessedKeys(unseenKeys);
455
+
456
+ const enabledMessages = unseenMessages.filter(message => {
457
+ const event = normalizeText(message?.event);
458
+
459
+ if (event === 'cash.closed') {
460
+ return managerFinancialCashCloseEnabled;
461
+ }
462
+
463
+ if (event === 'store.opened' || event === 'store.closed') {
464
+ return managerFinancialStoreCloseEnabled;
465
+ }
466
+
467
+ return false;
468
+ });
469
+
470
+ if (enabledMessages.length === 0) {
471
+ return;
472
+ }
473
+
474
+ Promise.allSettled(
475
+ enabledMessages.map(message =>
476
+ showManagerFinancialNotification({
477
+ messages: [message],
478
+ currentCompany,
479
+ store: 'orders',
480
+ event: normalizeText(message?.event) || 'cash.closed',
481
+ }),
482
+ ),
483
+ ).catch(() => {});
484
+ }, [
485
+ currentCompany,
486
+ currentCompanyId,
487
+ managerFinancialCashCloseEnabled,
488
+ managerFinancialStoreCloseEnabled,
489
+ markProcessedKeys,
490
+ orderMessage,
491
+ orderMessages,
492
+ isManagerRuntime,
493
+ ]);
494
+
495
+ return null;
496
+ };
497
+
498
+ export default DeviceAlertSoundService;
499
+ // TODO(store-first): quando este arquivo for mexido, mover a leitura para stores, remover api.fetch e evitar repassar dados em objetos quando o store ja resolver isso.