-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from shontzu-deriv/shontzu/FEQ-2199/adding-mob…
…ileOsDetect-utils-and-constants [CFDS] shontzu/FEQ-2199/adding-mobileOsDetect-utils-and-constants
- Loading branch information
Showing
7 changed files
with
215 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @description | ||
* This pattern matches any string that contains a sequence of three uppercase letters followed by a hyphen. | ||
* The sequence must be a word on its own (i.e., it must be surrounded by word boundaries). | ||
* The 'g' flag is used for global search (to find all matches rather than stopping after the first match), and the 'i' flag is used for case-insensitive search. | ||
* @example huaweiDevicesRegex.test("AMN-") // returns true | ||
* @example huaweiDevicesRegex.test("ANA-") // returns true | ||
* @example huaweiDevicesRegex.test("ANE-") // returns true | ||
* Source of list is from: https://gist.github.com/megaacheyounes/e1c7eec5c790e577db602381b8c50bfa | ||
*/ | ||
export const huaweiDevicesRegex = | ||
/(ALP-|AMN-|ANA-|ANE-|ANG-|AQM-|ARS-|ART-|ATU-|BAC-|BLA-|BRQ-|CAG-|CAM-|CAN-|CAZ-|CDL-|CDY-|CLT-|CRO-|CUN-|DIG-|DRA-|DUA-|DUB-|DVC-|ELE-|ELS-|EML-|EVA-|EVR-|FIG-|FLA-|FRL-|GLK-|HMA-|HW-|HWI-|INE-|JAT-|JEF-|JER-|JKM-|JNY-|JSC-|LDN-|LIO-|LON-|LUA-|LYA-|LYO-|MAR-|MED-|MHA-|MLA-|MRD-|MYA-|NCE-|NEO-|NOH-|NOP-|OCE-|PAR-|PIC-|POT-|PPA-|PRA-|RNE-|SEA-|SLA-|SNE-|SPN-|STK-|TAH-|TAS-|TET-|TRT-|VCE-|VIE-|VKY-|VNS-|VOG-|VTR-|WAS-|WKG-|WLZ-|JAD-|MLD-|RTE-|NAM-|NEN-|BAL-|JLN-|YAL-|MGA-|FGD-|XYAO-|BON-|ALN-|ALT-|BRA-|DBY2-|STG-|MAO-|LEM-|GOA-|FOA-|MNA-|LNA-)/; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { describe, expect, test } from "vitest"; | ||
import { mobileOSDetectAsync } from "../os-detect.utils"; | ||
|
||
describe("mobileOSDetectAsync", () => { | ||
test('should return "Windows Phone" for Windows Phone user agent', async () => { | ||
Object.defineProperty(window.navigator, "userAgent", { | ||
value: "windows phone", | ||
configurable: true, | ||
}); | ||
expect(await mobileOSDetectAsync()).toBe("Windows Phone"); | ||
}); | ||
|
||
test('should return "Android" for Android user agent', async () => { | ||
Object.defineProperty(window.navigator, "userAgent", { | ||
value: "android", | ||
configurable: true, | ||
}); | ||
expect(await mobileOSDetectAsync()).toBe("Android"); | ||
}); | ||
|
||
test('should return "iOS" for iOS user agent', async () => { | ||
Object.defineProperty(window.navigator, "userAgent", { | ||
value: "iPhone", | ||
configurable: true, | ||
}); | ||
expect(await mobileOSDetectAsync()).toBe("iOS"); | ||
}); | ||
|
||
test('should return "unknown" for unknown user agent', async () => { | ||
Object.defineProperty(window.navigator, "userAgent", { | ||
value: "unknown", | ||
configurable: true, | ||
}); | ||
expect(await mobileOSDetectAsync()).toBe("unknown"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { huaweiDevicesRegex } from "../constants/mobile-devices.constants"; | ||
|
||
/** | ||
* This file contains utility functions and types for detecting the mobile operating system. | ||
* It uses the User-Agent string and the User-Agent Client Hints API to determine the OS. | ||
*/ | ||
|
||
type ExtendedWindow = Window & { | ||
// MSStream is specific to IE and Edge browsers | ||
MSStream?: { | ||
msClose: () => void; | ||
msDetachStream: () => void; | ||
readonly type: string; | ||
}; | ||
// opera is specific to Opera browser | ||
opera?: string; | ||
}; | ||
|
||
type ExtendedNavigator = Navigator & { | ||
// userAgentData is part of the User-Agent Client Hints API | ||
userAgentData?: NavigatorUAData; | ||
}; | ||
|
||
/** | ||
* Type representing the User-Agent Client Hints API. | ||
*/ | ||
type NavigatorUAData = { | ||
brands: { brand: string; version: string }[]; | ||
getHighEntropyValues(hints: string[]): Promise<HighEntropyValues>; | ||
mobile: boolean; | ||
}; | ||
|
||
/** | ||
* Type representing the high entropy values that can be obtained from the User-Agent Client Hints API. | ||
*/ | ||
type HighEntropyValues = { | ||
model?: string; | ||
platform?: string; | ||
platformVersion?: string; | ||
uaFullVersion?: string; | ||
}; | ||
|
||
/** | ||
* It checks if the input string contains any of the valid Huawei device codes. | ||
* | ||
* @param {string} inputString - The string to check for Huawei device codes. | ||
* @returns {boolean} Returns true if the input string contains a valid Huawei device code, false otherwise. | ||
*/ | ||
const validateHuaweiCodes = (inputString: string) => { | ||
return huaweiDevicesRegex.test(inputString); | ||
}; | ||
|
||
/** | ||
* It uses the User-Agent string and the User-Agent Client Hints API to detects the mobile operating system asynchronously. | ||
* | ||
* @returns {Promise<string>} Returns a promise that resolves to the name of the detected mobile OS. | ||
*/ | ||
export const mobileOSDetectAsync = async () => { | ||
const extendedWindow = window as ExtendedWindow; | ||
const extendedNavigator = navigator as ExtendedNavigator; | ||
|
||
const userAgent = extendedNavigator.userAgent ?? extendedWindow.opera ?? ""; | ||
|
||
// Windows Phone must come first because its UA also contains "Android" | ||
if (/windows phone/i.test(userAgent)) { | ||
return "Windows Phone"; | ||
} | ||
|
||
if (/android/i.test(userAgent)) { | ||
// Check if navigator.userAgentData is available for modern browsers | ||
// userAgent only returns a string, while userAgentData returns an object with more detailed information | ||
if (extendedNavigator.userAgentData) { | ||
const ua = await extendedNavigator.userAgentData.getHighEntropyValues(["model"]); | ||
if (validateHuaweiCodes(ua?.model || "")) { | ||
return "huawei"; | ||
} | ||
} else if (validateHuaweiCodes(userAgent) || /huawei/i.test(userAgent)) { | ||
return "huawei"; | ||
} | ||
return "Android"; | ||
} | ||
|
||
if (/iPad|iPhone|iPod/.test(userAgent) && !extendedWindow.MSStream) { | ||
return "iOS"; | ||
} | ||
|
||
return "unknown"; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
--- | ||
sidebar_position: 2 | ||
--- | ||
|
||
# mobile-devices | ||
|
||
This utility module provides a regular expression and a set of valid codes to detect and validate Huawei device codes in a string. | ||
|
||
### `huaweiDevicesRegex` | ||
|
||
This regex matches standalone sequences of three uppercase letters followed by a hyphen, using global and case-insensitive search. | ||
|
||
```typescript | ||
import { huaweiDevicesRegex } from "@deriv-com/utils"; | ||
|
||
const isValid = huaweiDevicesRegex.test("ALP-"); // returns true | ||
``` | ||
|
||
### `validCodes` | ||
|
||
This is a set of valid Huawei device codes. It can be used to check if a detected code is a valid Huawei device code. | ||
|
||
```typescript | ||
import { validCodes } from "@deriv-com/utils"; | ||
|
||
const isValidCode = validCodes.has("ALP-"); // returns true | ||
``` | ||
|
||
#### Note | ||
|
||
These utilities can be used in conjunction with the `mobileOSDetectAsync` function to detect if a user is on a Huawei device running Android. If `mobileOSDetectAsync` returns "huawei", you can use `huaweiDevicesRegex` and `validCodes` to further validate the device code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--- | ||
sidebar_position: 2 | ||
--- | ||
|
||
# os-detect | ||
|
||
This utility module provides functions to detect mobile operating systems and extract information from user agent strings. | ||
|
||
### `mobileOSDetectAsync` | ||
|
||
This function asynchronously detects the mobile operating system based on the user agent string. | ||
|
||
#### Returns | ||
|
||
- `"Windows Phone"` if the user agent string indicates a Windows Phone device. | ||
- `"huawei"` if the user agent string indicates a Huawei device running Android. | ||
- `"Android"` if the user agent string indicates an Android device. | ||
- `"iOS"` if the user agent string indicates an iOS device (iPad, iPhone, or iPod). | ||
- `"unknown"` if the mobile operating system cannot be determined. | ||
|
||
#### Usage | ||
|
||
```typescript | ||
import { mobileOSDetectAsync } from "@deriv-com/utils"; | ||
|
||
const os = await mobileOSDetectAsync(); | ||
|
||
if (os === "iOS") { | ||
console.log("client on iOS"); | ||
} else if (os === "huawei") { | ||
console.log("client on huawei"); | ||
} | ||
console.log("client on android"); | ||
``` |