Files
cozsweet-frontend-nextjs/src/utils/platform-detect.ts
T

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(),
);
}