From 8bff7b7c820331d3b7e22a3d904073bc31e4232e Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Fri, 11 Oct 2024 15:14:12 -0300 Subject: [PATCH 1/2] feat: Add advanced UA probing --- src/hooks/useAdvancedUserAgentData.ts | 86 +++++++++++++++++++++++++++ src/hooks/useUserAgentData.ts | 11 ---- 2 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/hooks/useAdvancedUserAgentData.ts diff --git a/src/hooks/useAdvancedUserAgentData.ts b/src/hooks/useAdvancedUserAgentData.ts new file mode 100644 index 00000000..d96aabf9 --- /dev/null +++ b/src/hooks/useAdvancedUserAgentData.ts @@ -0,0 +1,86 @@ +// TODO(2fd): add Docs, set as good practice +import { useState } from 'react' + +import { UAParser } from 'ua-parser-js' +// @ts-expect-error: missing types +// eslint-disable-next-line import/no-unresolved +import { isAppleSilicon } from 'ua-parser-js/helpers' + +import useAsyncEffect from './useAsyncEffect' +const DEFAULT_VALUE = 'Unknown' + +export type AdvancedNavigatorUAData = { + browser: { + name: string + version: string + } + engine: { + name: string + version: string + } + os: { + name: string + version: string + } + cpu: { + architecture: string + } + mobile: boolean +} + +/** + * extract or infer the [UserAgentData](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData) + * that is an object which can be used to access the User-Agent Client Hints API. + */ +export default function useAdvancedUserAgentData(): [ + boolean, + AdvancedNavigatorUAData | undefined +] { + const [isLoading, setLoading] = useState(true) + const [data, setData] = useState() + + useAsyncEffect(async () => { + setLoading(true) + const ua = new UAParser(navigator.userAgent) + const uaData = ua.getResult() + const browser = { + name: uaData.browser.name ?? DEFAULT_VALUE, + version: uaData.browser.version ?? DEFAULT_VALUE, + } + const engine = { + name: uaData.engine.name ?? DEFAULT_VALUE, + version: uaData.engine.version ?? DEFAULT_VALUE, + } + const [osData, cpuData] = await Promise.all([ + ua.getOS().withClientHints(), + ua.getCPU().withClientHints(), + ]) + + const os = { + name: osData.name ?? DEFAULT_VALUE, + version: osData.version ?? DEFAULT_VALUE, + } + + let architecture: string + if (!cpuData.architecture) { + architecture = + os.name === 'macOS' && isAppleSilicon(ua) ? 'arm64' : 'Unknown' + } else { + architecture = cpuData.architecture + } + + setData({ + browser, + engine, + os, + cpu: { + architecture, + }, + mobile: ua.getDevice().is('mobile'), + }) + + setLoading(false) + }, []) + + return [isLoading, data] +} diff --git a/src/hooks/useUserAgentData.ts b/src/hooks/useUserAgentData.ts index 4836e00f..f1779756 100644 --- a/src/hooks/useUserAgentData.ts +++ b/src/hooks/useUserAgentData.ts @@ -13,7 +13,6 @@ export type NavigatorUAData = { brands: Brand[] mobile: boolean platform: string - cpu: string } export type Brand = { @@ -25,7 +24,6 @@ const defaultGlobalValue: NavigatorUAData = { brands: [], mobile: false, platform: 'Unknown', - cpu: 'Unknown', } const getUserAgentData = once((): NavigatorUAData => { @@ -50,17 +48,8 @@ const getUserAgentData = once((): NavigatorUAData => { }) } - let architecture - if (!ua.cpu.architecture) { - architecture = - ua.os.name === 'Mac OS' && isAppleSilicon(ua) ? 'arm64' : 'Unknown' - } else { - architecture = ua.cpu.architecture - } - return { platform: ua.os.name ?? defaultGlobalValue.platform, - cpu: architecture, mobile: isMobile(), brands, } From 82be9e6cd6b03f4a5df3907b525e3176a3effa11 Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Fri, 11 Oct 2024 15:29:32 -0300 Subject: [PATCH 2/2] fix: Remove comment --- src/hooks/useUserAgentData.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/useUserAgentData.ts b/src/hooks/useUserAgentData.ts index f1779756..0e275c27 100644 --- a/src/hooks/useUserAgentData.ts +++ b/src/hooks/useUserAgentData.ts @@ -1,4 +1,3 @@ -// TODO(2fd): add Docs, set as good practice import { useEffect, useMemo, useState } from 'react' import { UAParser } from 'ua-parser-js'