deviceName.tsx 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import * as React from 'react';
  2. import * as Sentry from '@sentry/react';
  3. // Self reference to the module, so that we can mock a failed import in a test.
  4. import * as selfModule from 'sentry/components/deviceName';
  5. import {IOSDeviceList} from 'sentry/types/iOSDeviceList';
  6. export function deviceNameMapper(
  7. model: string | undefined,
  8. module: IOSDeviceList | null
  9. ): string | null {
  10. // If we have no model, render nothing
  11. if (typeof model !== 'string') {
  12. return null;
  13. }
  14. // If module has not loaded yet, render the unparsed model
  15. if (module === null) {
  16. return model;
  17. }
  18. const [identifier, ...rest] = model.split(' ');
  19. const modelName = module.generationByIdentifier(identifier);
  20. return modelName === undefined ? model : `${modelName} ${rest.join(' ')}`;
  21. }
  22. export async function loadDeviceListModule(platform: 'iOS') {
  23. if (platform !== 'iOS') {
  24. Sentry.captureException('DeviceName component only supports iOS module');
  25. }
  26. return import('ios-device-list');
  27. }
  28. interface DeviceNameProps {
  29. value: string;
  30. children?: (name: string) => React.ReactNode;
  31. }
  32. /**
  33. * This is used to map iOS Device Names to model name.
  34. * This asynchronously loads the ios-device-list library because of its size
  35. */
  36. function DeviceName({value, children}: DeviceNameProps): React.ReactElement | null {
  37. const [deviceList, setDeviceList] = React.useState<IOSDeviceList | null>(null);
  38. React.useEffect(() => {
  39. let didUnmount = false;
  40. selfModule
  41. .loadDeviceListModule('iOS')
  42. .then(module => {
  43. // We need to track component unmount so we dont try and setState on an unmounted component
  44. if (didUnmount) {
  45. return;
  46. }
  47. setDeviceList(module);
  48. })
  49. .catch(() => {
  50. Sentry.captureException('Failed to load ios-device-list module');
  51. });
  52. return () => {
  53. didUnmount = true;
  54. };
  55. }, []);
  56. const deviceName = React.useMemo(
  57. () => deviceNameMapper(value, deviceList),
  58. [value, deviceList]
  59. );
  60. return deviceName ? (
  61. <span data-test-id="loaded-device-name">
  62. {children ? children(deviceName) : deviceName}
  63. </span>
  64. ) : null;
  65. }
  66. export {DeviceName};