131 lines
4.1 KiB
TypeScript
131 lines
4.1 KiB
TypeScript
import { UAParser } from "ua-parser-js";
|
|
|
|
const ONEPLUS_KEYBOARD_FALLBACK_MODELS = new Set(["PKC110", "PLB110"]);
|
|
|
|
export type PlatformName = "ios" | "android" | "desktop" | "unknown";
|
|
|
|
export interface PlatformInfo {
|
|
platform: PlatformName;
|
|
osName: string;
|
|
osVersion: string;
|
|
deviceType: string;
|
|
deviceVendor: string;
|
|
deviceModel: string;
|
|
cpuArchitecture: string;
|
|
isMobile: boolean;
|
|
isTablet: boolean;
|
|
isDesktop: boolean;
|
|
isIOS: boolean;
|
|
isAndroid: boolean;
|
|
userAgent: string;
|
|
}
|
|
|
|
/**
|
|
* 平台 / OS / 设备检测
|
|
*
|
|
* 与浏览器内核、内置浏览器判断解耦;浏览器相关能力请使用 `BrowserDetector`。
|
|
*/
|
|
export class PlatformDetector {
|
|
private constructor() {}
|
|
|
|
static getPlatformInfo(ua: string | null = null): PlatformInfo {
|
|
const userAgent = PlatformDetector.getUserAgent(ua);
|
|
const result = new UAParser(userAgent).getResult();
|
|
const osName = result.os.name ?? "";
|
|
const deviceType = result.device.type ?? "";
|
|
const isIOS = PlatformDetector.isIOS(userAgent);
|
|
const isAndroid = PlatformDetector.isAndroid(userAgent);
|
|
const isTablet = deviceType === "tablet" || PlatformDetector.isIPadOS();
|
|
const isMobile = deviceType === "mobile" || isTablet;
|
|
|
|
return {
|
|
platform: PlatformDetector.getPlatform(userAgent),
|
|
osName,
|
|
osVersion: result.os.version ?? "",
|
|
deviceType,
|
|
deviceVendor: result.device.vendor ?? "",
|
|
deviceModel: result.device.model ?? "",
|
|
cpuArchitecture: result.cpu.architecture ?? "",
|
|
isMobile,
|
|
isTablet,
|
|
isDesktop: !isMobile,
|
|
isIOS,
|
|
isAndroid,
|
|
userAgent,
|
|
};
|
|
}
|
|
|
|
static getPlatform(ua: string | null = null): PlatformName {
|
|
const userAgent = PlatformDetector.getUserAgent(ua);
|
|
if (PlatformDetector.isIOS(userAgent)) return "ios";
|
|
if (PlatformDetector.isAndroid(userAgent)) return "android";
|
|
if (userAgent.length > 0) return "desktop";
|
|
return "unknown";
|
|
}
|
|
|
|
static isIOS(ua: string | null = null): boolean {
|
|
const userAgent = PlatformDetector.getUserAgent(ua);
|
|
const osName = new UAParser(userAgent).getOS().name ?? "";
|
|
return /iOS/i.test(osName) || PlatformDetector.isIPadOS();
|
|
}
|
|
|
|
static isAndroid(ua: string | null = null): boolean {
|
|
const userAgent = PlatformDetector.getUserAgent(ua);
|
|
const osName = new UAParser(userAgent).getOS().name ?? "";
|
|
return /Android/i.test(osName) || /Android/i.test(userAgent);
|
|
}
|
|
|
|
static isMobile(ua: string | null = null): boolean {
|
|
return PlatformDetector.getPlatformInfo(ua).isMobile;
|
|
}
|
|
|
|
static isTablet(ua: string | null = null): boolean {
|
|
return PlatformDetector.getPlatformInfo(ua).isTablet;
|
|
}
|
|
|
|
static isDesktop(ua: string | null = null): boolean {
|
|
return PlatformDetector.getPlatformInfo(ua).isDesktop;
|
|
}
|
|
|
|
static isXiaomiFamilyDevice(ua: string | null = null): boolean {
|
|
const info = PlatformDetector.getPlatformInfo(ua);
|
|
const target = `${info.deviceVendor} ${info.deviceModel} ${info.userAgent}`;
|
|
return /\b(Xiaomi|Redmi|POCO|Mi\s|MIX|MIUI)\b/i.test(target);
|
|
}
|
|
|
|
static isOppoFamilyDevice(ua: string | null = null): boolean {
|
|
const info = PlatformDetector.getPlatformInfo(ua);
|
|
const target = `${info.deviceVendor} ${info.deviceModel} ${info.userAgent}`;
|
|
const isOnePlusFamily = /\bOnePlus\b/i.test(
|
|
`${info.deviceVendor} ${info.userAgent}`,
|
|
);
|
|
if (isOnePlusFamily) {
|
|
return isOnePlusKeyboardFallbackDevice({
|
|
model: info.deviceModel,
|
|
vendor: info.deviceVendor,
|
|
});
|
|
}
|
|
return /\b(OPPO|OPlus|ColorOS|CPH\d{3,})\b/i.test(target);
|
|
}
|
|
|
|
private static getUserAgent(ua: string | null = null): string {
|
|
if (ua) return ua;
|
|
if (typeof navigator === "undefined") return "";
|
|
return navigator.userAgent;
|
|
}
|
|
|
|
private static isIPadOS(): boolean {
|
|
if (typeof navigator === "undefined") return false;
|
|
return navigator.platform === "MacIntel" && (navigator.maxTouchPoints ?? 0) > 1;
|
|
}
|
|
}
|
|
|
|
export function isOnePlusKeyboardFallbackDevice(
|
|
device: { model: string; vendor: string },
|
|
): boolean {
|
|
if (!/\bOnePlus\b/i.test(device.vendor)) return false;
|
|
return ONEPLUS_KEYBOARD_FALLBACK_MODELS.has(
|
|
device.model.trim().toUpperCase(),
|
|
);
|
|
}
|