@controleonline/ui-common 1.2.90 → 1.2.92

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.
@@ -1,3 +1,6 @@
1
+ import {useNativeTranslationRefresh, useNativeTranslationReset, useNativeAppLifecycle, useNativeNativeVersion, useNativeNativeDeviceIdentity, useNativeMainCompany, useNativeDeviceConfigReset, useNativeDeviceRegistration, useNativePrinters, useNativePaymentTypes, useNativeDeviceConfigFetch} from './DefaultProvider.native.lifecycle';
2
+ import {useNativeDeviceConfigSync, useNativeMainConfigReset, useNativeMainConfigDiscovery, useNativeMainConfigSeed, useNativeTranslationBootstrap, useNativeBootstrapReady, useNativeCompanies, useNativeMenus, useNativeThemeFetch, useNativeThemePalette} from './DefaultProvider.native.configuration';
3
+ import {resolveDomainThemeColors} from './resolveDomainThemeColors';
1
4
  import React, {
2
5
  createContext,
3
6
  useCallback,
@@ -169,12 +172,26 @@ export const DefaultProvider = ({
169
172
  deviceInfo: device || {},
170
173
  });
171
174
  const {width: windowWidth, height: windowHeight} = useWindowDimensions();
175
+ // app-community#1051: size the scaled content from the area it really
176
+ // occupies (shell minus runtime footer), not from the full window.
177
+ const [contentSize, setContentSize] = useState(null);
178
+ const handleContentLayout = useCallback(({nativeEvent}) => {
179
+ const {width, height} = nativeEvent?.layout || {};
180
+ setContentSize(current =>
181
+ current?.width === width && current?.height === height
182
+ ? current
183
+ : {width, height},
184
+ );
185
+ }, []);
172
186
  const runtimeUiScaleStyle = useMemo(
173
187
  () =>
174
188
  buildRuntimeZoomStyle(APP_ENV?.ZOOM, {
175
- viewport: {width: windowWidth, height: windowHeight},
189
+ viewport: {
190
+ width: contentSize?.width || windowWidth,
191
+ height: contentSize?.height || windowHeight,
192
+ },
176
193
  }),
177
- [windowHeight, windowWidth],
194
+ [contentSize, windowHeight, windowWidth],
178
195
  );
179
196
  const deviceConfigPeopleIri = resolveDeviceConfigPeopleIri({
180
197
  appType,
@@ -268,43 +285,11 @@ export const DefaultProvider = ({
268
285
  </>
269
286
  );
270
287
 
271
- useEffect(() => {
272
- global.refreshTranslationsUI = () => {
273
- setTranslateVersion(version => version + 1);
274
- };
275
-
276
- return () => {
277
- if (global.refreshTranslationsUI) {
278
- delete global.refreshTranslationsUI;
279
- }
280
- };
281
- }, []);
282
-
283
- useEffect(() => {
284
- if (isLogged && !isPublicRouteActive) {
285
- return;
286
- }
287
-
288
- setTranslateReady(true);
289
- setActiveTranslateBootstrapKey('');
290
- translateBootstrapKeyRef.current = '';
291
- translateActions.setMessages({});
292
- translateActions.setPendingMessages?.({});
293
-
294
- if (global.t) {
295
- delete global.t;
296
- }
297
- }, [isLogged, isPublicRouteActive, translateActions]);
288
+ useNativeTranslationRefresh({setTranslateVersion});
298
289
 
299
- useEffect(() => {
300
- const subscription = AppState.addEventListener('change', nextState => {
301
- setAppState(nextState || 'active');
302
- });
290
+ useNativeTranslationReset({isLogged, isPublicRouteActive, setTranslateReady, setActiveTranslateBootstrapKey, translateBootstrapKeyRef, translateActions});
303
291
 
304
- return () => {
305
- subscription?.remove?.();
306
- };
307
- }, []);
292
+ useNativeAppLifecycle({AppState, setAppState});
308
293
 
309
294
  const fetchDeviceId = async () => {
310
295
  const uniqueId = await DeviceInfo.getUniqueId();
@@ -345,524 +330,43 @@ export const DefaultProvider = ({
345
330
  }, 300);
346
331
  }
347
332
  };
348
- useEffect(() => {
349
- const checkVersion = async () => {
350
- const appVersion = await DeviceInfo.getVersion();
351
- if (
352
- device &&
353
- ((device.appVersion && device.appVersion != appVersion) || !device.appName)
354
- ) {
355
- fetchDeviceId();
356
- }
357
- };
358
- checkVersion();
359
- }, [device]);
360
-
361
- useEffect(() => {
362
- if (!device || !device.id) {
363
- fetchDeviceId();
364
- } else {
365
- deviceActions.setItem(device);
366
- }
367
- }, [device]);
368
-
369
- useEffect(() => {
370
- if (device && device.id) {
371
- peopleActions.mainCompany();
372
- }
373
- }, [device?.id]);
374
-
375
- useEffect(() => {
376
- if (
377
- !deviceConfigPeopleIri ||
378
- lastDeviceConfigPeopleIriRef.current === deviceConfigPeopleIri
379
- ) {
380
- return;
381
- }
382
-
383
- lastDeviceConfigPeopleIriRef.current = deviceConfigPeopleIri;
384
- setDeviceConfigFetched(false);
385
- setDeviceRuntimeConfigSynced(false);
386
- deviceConfigsActions.setItem({});
387
- }, [deviceConfigPeopleIri, deviceConfigsActions]);
388
-
389
- useEffect(() => {
390
- if (!sessionChecked || !isLogged || !deviceConfigPeopleIri || !device?.id || deviceConfigFetched) {
391
- return;
392
- }
393
-
394
- // Device registration changes tenant context and is not valid without an
395
- // active company or administrative authority for that company.
396
- if (
397
- !currentCompany?.id ||
398
- !canAdministerCompany({company: currentCompany, mainCompany, user})
399
- ) {
400
- return;
401
- }
402
-
403
- let cancelled = false;
404
-
405
- const syncDeviceRegistration = async () => {
406
- const items = await deviceActions.getItems({
407
- device: device.id,
408
- people: deviceConfigPeopleIri,
409
- type: runtimeDeviceType,
410
- });
411
-
412
- if (cancelled) {
413
- return;
414
- }
415
-
416
- const existingDevice =
417
- Array.isArray(items) && items.length > 0 ? items[0] : null;
418
- const nextDevice = buildDeviceRegistrationPayload({
419
- deviceInfo: device,
420
- appType: app_type,
421
- existingDevice,
422
- });
423
-
424
- if (!hasDeviceRecordChanges({existingDevice, nextDevice})) {
425
- const nextLocalDevice = {
426
- ...device,
427
- entityId: existingDevice?.id || device?.entityId || null,
428
- entityIri:
429
- existingDevice?.['@id'] ||
430
- device?.entityIri ||
431
- (existingDevice?.id ? `/devices/${existingDevice.id}` : null),
432
- alias: existingDevice?.alias || nextDevice.alias,
433
- type: device?.type || runtimeDeviceType,
434
- metadata: {
435
- ...(existingDevice?.metadata || {}),
436
- ...(nextDevice.metadata || {}),
437
- },
438
- };
439
-
440
- if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
441
- setDevice(nextLocalDevice);
442
- localStorage.setItem('device', JSON.stringify(nextLocalDevice));
443
- deviceActions.setItem(nextLocalDevice);
444
- }
445
- return;
446
- }
447
-
448
- const savedDevice = await deviceActions.save(nextDevice);
449
- if (cancelled || !savedDevice) {
450
- return;
451
- }
452
-
453
- const nextLocalDevice = {
454
- ...device,
455
- entityId: savedDevice?.id || device?.entityId || null,
456
- entityIri:
457
- savedDevice?.['@id'] ||
458
- device?.entityIri ||
459
- (savedDevice?.id ? `/devices/${savedDevice.id}` : null),
460
- alias: savedDevice.alias || nextDevice.alias,
461
- type: device?.type || runtimeDeviceType,
462
- metadata: {
463
- ...(savedDevice?.metadata || {}),
464
- ...(nextDevice.metadata || {}),
465
- },
466
- };
467
-
468
- if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
469
- setDevice(nextLocalDevice);
470
- localStorage.setItem('device', JSON.stringify(nextLocalDevice));
471
- deviceActions.setItem(nextLocalDevice);
472
- }
473
- };
474
-
475
- syncDeviceRegistration().catch(() => {});
476
-
477
- return () => {
478
- cancelled = true;
479
- };
480
- }, [
481
- device?.appVersion,
482
- device?.batteryLevel,
483
- device?.buildNumber,
484
- device?.deviceType,
485
- device?.id,
486
- device?.metadata,
487
- device?.manufacturer,
488
- device?.model,
489
- device?.systemVersion,
490
- deviceConfigFetched,
491
- deviceConfigPeopleIri,
492
- isLogged,
493
- currentCompany,
494
- mainCompany,
495
- sessionChecked,
496
- runtimeDeviceType,
497
- user,
498
- ]);
333
+ useNativeNativeVersion({DeviceInfo, device, fetchDeviceId});
499
334
 
500
- useEffect(() => {
501
- if (isShopClientApp || !sessionChecked || !isLogged || !currentCompany?.id) {
502
- return;
503
- }
335
+ useNativeNativeDeviceIdentity({device, fetchDeviceId, deviceActions});
504
336
 
505
- printerActions
506
- .ensureCompanyPrintersLoaded({people: currentCompany.id})
507
- .catch(() => {});
508
- }, [currentCompany?.id, isLogged, isShopClientApp, printerActions, sessionChecked]);
509
-
510
- useEffect(() => {
511
- const paymentConfigSource = isShopClientApp
512
- ? Object.keys(companyConfigs || {}).length > 0
513
- ? companyConfigs
514
- : currentCompany?.configs
515
- : device_config?.configs;
516
-
517
- if (!currentCompany?.id || !paymentConfigSource) {
518
- paymentTypeActions.setItems([]);
519
- return;
520
- }
337
+ useNativeMainCompany({device, peopleActions});
521
338
 
522
- if (!isShopClientApp && !mainConfigsDiscovered) {
523
- return;
524
- }
339
+ useNativeDeviceConfigReset({deviceConfigPeopleIri, lastDeviceConfigPeopleIriRef, setDeviceConfigFetched, setDeviceRuntimeConfigSynced, deviceConfigsActions});
525
340
 
526
- let isMounted = true;
341
+ useNativeDeviceRegistration({sessionChecked, isLogged, deviceConfigPeopleIri, device, deviceConfigFetched, currentCompany, canAdministerCompany, mainCompany, user, deviceActions, runtimeDeviceType, buildDeviceRegistrationPayload, app_type, hasDeviceRecordChanges, setDevice});
527
342
 
528
- api
529
- .fetch('wallet_payment_types', {
530
- params: {
531
- people: `/people/${currentCompany.id}`,
532
- },
533
- })
534
- .then(response => {
535
- if (!isMounted) {
536
- return;
537
- }
538
-
539
- const walletPaymentTypes = Array.isArray(response?.member)
540
- ? response.member
541
- : Array.isArray(response?.['hydra:member'])
542
- ? response['hydra:member']
543
- : Array.isArray(response)
544
- ? response
545
- : [];
546
- paymentTypeActions.setItems(
547
- selectPosWalletPaymentTypes({
548
- walletPaymentTypes,
549
- deviceConfigs: paymentConfigSource,
550
- companyConfigs,
551
- gateway: getPaymentGateway(device_config || paymentConfigSource),
552
- }),
553
- );
554
- })
555
- .catch(() => {
556
- if (isMounted) {
557
- paymentTypeActions.setItems([]);
558
- }
559
- });
343
+ useNativePrinters({isShopClientApp, sessionChecked, isLogged, currentCompany, printerActions});
560
344
 
561
- return () => {
562
- isMounted = false;
563
- };
564
- }, [
565
- companyConfigs,
566
- currentCompany?.configs,
567
- currentCompany?.id,
568
- device_config?.configs,
569
- isShopClientApp,
570
- paymentTypeActions,
571
- ]);
572
-
573
- useEffect(() => {
574
- if (
575
- device &&
576
- device.id &&
577
- isLogged &&
578
- currentCompany &&
579
- Object.entries(currentCompany).length > 0
580
- ) {
581
- setDeviceConfigFetched(false);
582
- setDeviceRuntimeConfigSynced(false);
583
- deviceConfigsActions
584
- .getItems({
585
- 'device.device': device.id,
586
- people: '/people/' + currentCompany.id,
587
- type: runtimeDeviceType,
588
- })
589
- .then(data => {
590
- if (Array.isArray(data) && data.length > 0) {
591
- const nextItem = {
592
- ...data[0],
593
- configs: parseConfigsObject(data[0]?.configs),
594
- };
595
- deviceConfigsActions.setItem(nextItem);
596
- return;
597
- }
598
-
599
- deviceConfigsActions.setItem({});
600
- })
601
- .catch(() => {})
602
- .finally(() => {
603
- setDeviceConfigFetched(true);
604
- });
605
- }
606
- }, [currentCompany?.id, isLogged, device?.id, runtimeDeviceType]);
607
-
608
- useEffect(() => {
609
- if (
610
- !deviceConfigFetched ||
611
- !isLogged ||
612
- !deviceConfigPeopleIri ||
613
- !device?.id ||
614
- deviceRuntimeConfigSynced
615
- ) {
616
- return;
617
- }
345
+ useNativePaymentTypes({isShopClientApp, companyConfigs, currentCompany, device_config, paymentTypeActions, mainConfigsDiscovered, api, selectPosWalletPaymentTypes, getPaymentGateway});
618
346
 
619
- const isNewPdvConfig =
620
- runtimeDeviceType === 'PDV' &&
621
- !device_config?.id &&
622
- !device_config?.['@id'];
623
- const buildDeviceConfigs = isNewPdvConfig
624
- ? buildDefaultDeviceConfigs
625
- : buildProviderManagedDeviceConfigs;
626
- const {nextConfigs, needsUpdate} = buildDeviceConfigs({
627
- configs: device_config?.configs,
628
- appVersion,
629
- deviceInfo: device,
630
- });
347
+ useNativeDeviceConfigFetch({device, isLogged, currentCompany, setDeviceConfigFetched, setDeviceRuntimeConfigSynced, deviceConfigsActions, runtimeDeviceType, parseConfigsObject});
631
348
 
632
- if (!needsUpdate) {
633
- setDeviceRuntimeConfigSynced(true);
634
- return;
635
- }
349
+ useNativeDeviceConfigSync({deviceConfigFetched, isLogged, deviceConfigPeopleIri, device, deviceRuntimeConfigSynced, runtimeDeviceType, device_config, buildDefaultDeviceConfigs, buildProviderManagedDeviceConfigs, appVersion, setDeviceRuntimeConfigSynced, deviceConfigsActions, user});
636
350
 
637
- // app-community#821: never auto-persist device_config from global bootstrap.
638
- // Provider-managed fields stay in memory only; API mutations only from
639
- // explicit device settings UI (avoids 403 on order-history / PDV|MANAGER).
640
- const currentItem = device_config || {};
641
- deviceConfigsActions.setItem({
642
- ...currentItem,
643
- configs: nextConfigs,
644
- device: currentItem.device || device.id,
645
- people: currentItem.people || deviceConfigPeopleIri,
646
- type: currentItem.type || runtimeDeviceType,
647
- });
648
- setDeviceRuntimeConfigSynced(true);
649
- }, [
650
- appVersion,
651
- device?.id,
652
- device?.manufacturer,
653
- device?.isEmulator,
654
- deviceConfigFetched,
655
- deviceConfigPeopleIri,
656
- deviceRuntimeConfigSynced,
657
- deviceConfigsActions,
658
- device_config,
659
- isLogged,
660
- runtimeDeviceType,
661
- user,
662
- ]);
351
+ useNativeMainConfigReset({isLogged, currentCompany, device, setMainConfigsDiscovered});
663
352
 
664
- useEffect(() => {
665
- if (!isLogged || !currentCompany?.id || !device?.id) {
666
- return;
667
- }
353
+ useNativeMainConfigDiscovery({isLogged, currentCompany, device, mainConfigsDiscovered, configActions, setMainConfigsDiscovered});
668
354
 
669
- setMainConfigsDiscovered(false);
670
- }, [isLogged, currentCompany?.id, device?.id]);
671
-
672
- useEffect(() => {
673
- if (
674
- !isLogged ||
675
- !currentCompany?.id ||
676
- !device?.id ||
677
- mainConfigsDiscovered
678
- ) {
679
- return;
680
- }
355
+ useNativeMainConfigSeed({isLogged, currentCompany, mainConfigsDiscovered, configActions});
681
356
 
682
- configActions
683
- .discoveryMainConfigs({
684
- people: '/people/' + currentCompany.id,
685
- })
686
- .catch(() => {})
687
- .finally(() => {
688
- setMainConfigsDiscovered(true);
689
- });
690
- }, [configActions, currentCompany?.id, device?.id, isLogged, mainConfigsDiscovered]);
691
-
692
- useEffect(() => {
693
- if (
694
- isLogged &&
695
- currentCompany &&
696
- Object.entries(currentCompany).length > 0 &&
697
- !mainConfigsDiscovered
698
- ) {
699
- configActions.setItems(currentCompany.configs);
700
- }
701
- }, [currentCompany, isLogged, mainConfigsDiscovered]);
357
+ useNativeTranslationBootstrap({currentRouteName, isPublicRouteActive, isLogged, hasCurrentCompany, deviceConfigFetched, expectedTranslateBootstrapKey, translateBootstrapKeyRef, companies, currentCompany, mainCompany, setActiveTranslateBootstrapKey, setTranslateReady, configuredTranslationLanguage, Translate, stores, translateStore});
702
358
 
703
- useEffect(() => {
704
- if (!currentRouteName || isPublicRouteActive) {
705
- return;
706
- }
707
359
 
708
- if (
709
- !isLogged ||
710
- !hasCurrentCompany ||
711
- !deviceConfigFetched ||
712
- !expectedTranslateBootstrapKey
713
- ) {
714
- return;
715
- }
360
+ useNativeBootstrapReady({isLogged, hasCurrentCompany, currentRouteName, isTranslateBootstrapReady, onBootstrapReady});
716
361
 
717
- const currentConfig = JSON.parse(localStorage.getItem('config') || '{}');
718
362
 
719
- if (
720
- translateBootstrapKeyRef.current === expectedTranslateBootstrapKey &&
721
- global.t
722
- ) {
723
- global.t.companies = companies;
724
- global.t.currentCompany = currentCompany;
725
- global.t.mainCompany = mainCompany;
726
- setActiveTranslateBootstrapKey(expectedTranslateBootstrapKey);
727
- setTranslateReady(true);
363
+ useNativeCompanies({device, isLogged, peopleActions});
728
364
 
729
- return;
730
- }
365
+ useNativeMenus({isLogged, currentCompany, actions, api, appType, normalizeRuntimeMenuResponse});
731
366
 
732
- setTranslateReady(false);
367
+ useNativeThemeFetch({api, parseThemeCss, setBaseThemeColors, actions, device, currentCompany, mainCompany});
733
368
 
734
- if (currentConfig.language !== configuredTranslationLanguage) {
735
- const nextConfig = {
736
- ...currentConfig,
737
- language: configuredTranslationLanguage,
738
- };
739
- localStorage.setItem(
740
- 'config',
741
- JSON.stringify(nextConfig),
742
- );
743
- }
744
-
745
- translateBootstrapKeyRef.current = expectedTranslateBootstrapKey;
746
- global.t = new Translate(
747
- companies,
748
- mainCompany,
749
- currentCompany,
750
- Object.keys(stores),
751
- translateStore,
752
- );
753
- setActiveTranslateBootstrapKey(expectedTranslateBootstrapKey);
754
- setTranslateReady(true);
755
- global.refreshTranslationsUI?.();
756
- }, [
757
- companies,
758
- configuredTranslationLanguage,
759
- currentCompany,
760
- currentRouteName,
761
- mainCompany,
762
- deviceConfigFetched,
763
- expectedTranslateBootstrapKey,
764
- hasCurrentCompany,
765
- isLogged,
766
- isPublicRouteActive,
767
- translateStore,
768
- ]);
769
-
770
-
771
- useEffect(() => {
772
- if (
773
- !isLogged ||
774
- !hasCurrentCompany ||
775
- (currentRouteName && isTranslateBootstrapReady)
776
- ) {
777
- onBootstrapReady?.();
778
- }
779
- }, [
780
- currentRouteName,
781
- hasCurrentCompany,
782
- isLogged,
783
- isTranslateBootstrapReady,
784
- onBootstrapReady,
785
- ]);
786
-
787
-
788
- useEffect(() => {
789
- if (
790
- device &&
791
- device.id &&
792
- isLogged
793
- ) {
794
- peopleActions.myCompanies();
795
- }
796
- }, [isLogged, device?.id]);
797
-
798
- useEffect(() => {
799
- if (!isLogged || !currentCompany?.id) {
800
- actions.setMenus([]);
801
- return undefined;
802
- }
803
-
804
- let cancelled = false;
805
-
806
- api
807
- .fetch('menus-people', {
808
- params: {
809
- myCompany: currentCompany.id,
810
- appType,
811
- menuType: 'home',
812
- },
813
- })
814
- .then(result => {
815
- if (!cancelled) {
816
- actions.setMenus(normalizeRuntimeMenuResponse(result, {appType}));
817
- }
818
- })
819
- .catch(() => {
820
- if (!cancelled) {
821
- actions.setMenus(normalizeRuntimeMenuResponse(null, {appType}));
822
- }
823
- });
824
-
825
- return () => {
826
- cancelled = true;
827
- };
828
- }, [actions, appType, currentCompany?.id, isLogged]);
829
-
830
- useEffect(() => {
831
- const fetchColors = async () => {
832
- try {
833
- const cssText = await api.fetch('themes-colors.css', {
834
- responseType: 'text',
835
- });
836
- const parsedColors = parseThemeCss(cssText);
837
- setBaseThemeColors(parsedColors);
838
- actions.setColors(parsedColors);
839
- } catch {
840
- setBaseThemeColors({});
841
- }
842
- };
843
-
844
- if (device?.id) {
845
- fetchColors();
846
- }
847
- }, [actions, currentCompany?.id, mainCompany?.id, device?.id]);
848
-
849
- useEffect(() => {
850
- const companyThemeColors =
851
- currentCompany?.theme?.colors || mainCompany?.theme?.colors || {};
852
- const mergedThemeColors = {
853
- ...(baseThemeColors || {}),
854
- ...(companyThemeColors || {}),
855
- };
856
-
857
- const palette = resolveThemePalette(mergedThemeColors, runtimeColors);
858
- applyPaletteToRuntimeColors(palette, runtimeColors);
859
- applyThemeCssVariables({
860
- themeColors: mergedThemeColors,
861
- palette,
862
- });
863
-
864
- actions.setColors(mergedThemeColors);
865
- }, [actions, baseThemeColors, currentCompany?.id, currentCompany?.theme?.colors]);
369
+ useNativeThemePalette({resolveDomainThemeColors, baseThemeColors, mainCompany, resolveThemePalette, runtimeColors, applyPaletteToRuntimeColors, applyThemeCssVariables, actions, currentCompany});
866
370
 
867
371
  if (requiresTranslateBootstrap && !isTranslateBootstrapReady) {
868
372
  return <View style={providerStyles.loadingContainer} />;
@@ -881,7 +385,15 @@ export const DefaultProvider = ({
881
385
  backgroundColor: colors?.background || runtimeColors.background,
882
386
  },
883
387
  ]}>
884
- <View style={[providerStyles.content, runtimeUiScaleStyle]}>{children}</View>
388
+ <View
389
+ style={providerStyles.content}
390
+ onLayout={runtimeUiScaleStyle ? handleContentLayout : undefined}>
391
+ {runtimeUiScaleStyle ? (
392
+ <View style={runtimeUiScaleStyle}>{children}</View>
393
+ ) : (
394
+ children
395
+ )}
396
+ </View>
885
397
  {!isShopClientApp && bottomNavigationCount === 0 && (
886
398
  <RuntimeInfoFooter
887
399
  appVersion={appVersion}