Files
ThreadNet-Web/src/utils/device/parseUserAgent.ts
T

112 lines
3.5 KiB
TypeScript
Raw Normal View History

/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-09 14:57:16 +01:00
Please see LICENSE files in the repository root for full details.
*/
2022-12-12 12:24:14 +01:00
import UAParser from "ua-parser-js";
export enum DeviceType {
2022-12-12 12:24:14 +01:00
Desktop = "Desktop",
Mobile = "Mobile",
Web = "Web",
Unknown = "Unknown",
}
export type ExtendedDeviceInformation = {
deviceType: DeviceType;
// eg Google Pixel 6
deviceModel?: string;
// eg Android 11
deviceOperatingSystem?: string;
// eg Firefox 1.1.0
client?: string;
};
// Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00)
const IOS_KEYWORD = "; iOS ";
const BROWSER_KEYWORD = "Mozilla/";
const getDeviceType = (
userAgent: string,
device: UAParser.IDevice,
browser: UAParser.IBrowser,
operatingSystem: UAParser.IOS,
): DeviceType => {
if (device.type === "mobile" || operatingSystem.name?.includes("Android") || userAgent.indexOf(IOS_KEYWORD) > -1) {
return DeviceType.Mobile;
}
2022-12-12 12:24:14 +01:00
if (browser.name === "Electron") {
return DeviceType.Desktop;
}
if (!!browser.name) {
return DeviceType.Web;
}
return DeviceType.Unknown;
};
2022-10-11 09:29:19 +02:00
interface CustomValues {
customDeviceModel?: string;
customDeviceOS?: string;
}
/**
* Some mobile model and OS strings are not recognised
* by the UA parsing library
* check they exist by hand
*/
2022-10-11 09:29:19 +02:00
const checkForCustomValues = (userAgent: string): CustomValues => {
if (userAgent.includes(BROWSER_KEYWORD)) {
return {};
}
2022-12-12 12:24:14 +01:00
const mightHaveDevice = userAgent.includes("(");
if (!mightHaveDevice) {
return {};
}
2022-12-12 12:24:14 +01:00
const deviceInfoSegments = userAgent.substring(userAgent.indexOf("(") + 1).split("; ");
const customDeviceModel = deviceInfoSegments[0] || undefined;
const customDeviceOS = deviceInfoSegments[1] || undefined;
return { customDeviceModel, customDeviceOS };
};
const concatenateNameAndVersion = (name?: string, version?: string): string | undefined =>
2022-12-12 12:24:14 +01:00
name && [name, version].filter(Boolean).join(" ");
export const parseUserAgent = (userAgent?: string): ExtendedDeviceInformation => {
if (!userAgent) {
return {
deviceType: DeviceType.Unknown,
};
}
const parser = new UAParser(userAgent);
const browser = parser.getBrowser();
const device = parser.getDevice();
const operatingSystem = parser.getOS();
const deviceType = getDeviceType(userAgent, device, browser, operatingSystem);
2022-10-11 09:29:19 +02:00
// OSX versions are frozen at 10.15.17 in UA strings https://chromestatus.com/feature/5452592194781184
// ignore OS version in browser based sessions
const shouldIgnoreOSVersion = deviceType === DeviceType.Web || deviceType === DeviceType.Desktop;
const deviceOperatingSystem = concatenateNameAndVersion(
operatingSystem.name,
shouldIgnoreOSVersion ? undefined : operatingSystem.version,
);
const deviceModel = concatenateNameAndVersion(device.vendor, device.model);
const client = concatenateNameAndVersion(browser.name, browser.version);
// only try to parse custom model and OS when device type is known
2022-12-12 12:24:14 +01:00
const { customDeviceModel, customDeviceOS } =
deviceType !== DeviceType.Unknown ? checkForCustomValues(userAgent) : ({} as CustomValues);
2022-10-11 09:29:19 +02:00
return {
deviceType,
deviceModel: deviceModel || customDeviceModel,
deviceOperatingSystem: deviceOperatingSystem || customDeviceOS,
client,
};
};