'use client'; import { useEffect, useMemo, useState, useCallback } from 'react'; // Safe defaults for SSR const defaultSelectors = { isMobile: false, isTablet: false, isDesktop: false, isBrowser: false, isMobileOnly: false, isSmartTV: false, isConsole: false, isWearable: false, isEmbedded: false, isAndroid: false, isIOS: false, isWindows: false, isMacOs: false, isWinPhone: false, isChrome: false, isFirefox: false, isSafari: false, isOpera: false, isIE: false, isEdge: false, isEdgeChromium: false, isLegacyEdge: false, isChromium: false, isMobileSafari: false, isYandex: false, isMIUI: false, isSamsungBrowser: false, isElectron: false, osVersion: 'unknown', osName: 'unknown', fullBrowserVersion: 'unknown', browserVersion: 'unknown', browserName: 'unknown', mobileVendor: 'unknown', mobileModel: 'unknown', engineName: 'unknown', engineVersion: 'unknown', getUA: '', deviceType: 'unknown', isIOS13: false, isIPad13: false, isIPhone13: false, isIPod13: false, }; const defaultDeviceData = { deviceType: 'unknown', osName: 'unknown', osVersion: 'unknown', browserName: 'unknown', browserVersion: 'unknown', fullBrowserVersion: 'unknown', mobileVendor: 'unknown', mobileModel: 'unknown', engineName: 'unknown', engineVersion: 'unknown', getUA: '', }; const defaultOrientation = { isPortrait: false, isLandscape: false, orientation: 'portrait' as 'portrait' | 'landscape', }; interface DeviceInfo { selectors: typeof defaultSelectors; deviceData: typeof defaultDeviceData; orientation: { isPortrait: boolean; isLandscape: boolean; orientation: 'portrait' | 'landscape'; }; } /** * Device detection hook wrapper for react-device-detect * * Provides a convenient interface to access device information including: * - Device type (mobile, tablet, desktop, etc.) * - Browser information (name, version, etc.) * - OS information (name, version, etc.) * - Orientation (portrait/landscape) * * @param userAgent - Optional user agent string (useful for SSR) * @returns Device detection object with all available information * * @example * ```tsx * const device = useDeviceDetect(); * * if (device.isMobile) { * return ; * } * * return ; * ``` */ export function useDeviceDetect(userAgent?: string) { const [deviceInfo, setDeviceInfo] = useState({ selectors: defaultSelectors, deviceData: defaultDeviceData, orientation: defaultOrientation, }); useEffect(() => { if (typeof window === 'undefined') return; // Dynamic import to avoid SSR issues import('react-device-detect').then((deviceDetect) => { const ua = userAgent || window.navigator.userAgent; if (!ua) { console.warn('No user agent available'); return; } const parsed = deviceDetect.parseUserAgent(ua); if (!parsed) { console.warn('Failed to parse user agent'); return; } const selectors = deviceDetect.getSelectorsByUserAgent(ua) || defaultSelectors; const deviceData = { deviceType: parsed.device?.type || 'unknown', osName: parsed.os?.name || 'unknown', osVersion: parsed.os?.version || 'unknown', browserName: parsed.browser?.name || 'unknown', browserVersion: parsed.browser?.version || 'unknown', fullBrowserVersion: parsed.browser?.version || 'unknown', mobileVendor: parsed.device?.vendor || 'unknown', mobileModel: parsed.device?.model || 'unknown', engineName: parsed.engine?.name || 'unknown', engineVersion: parsed.engine?.version || 'unknown', getUA: parsed.ua || ua, }; const isPortrait = window.innerHeight > window.innerWidth; const orientation = { isPortrait, isLandscape: !isPortrait, orientation: (isPortrait ? 'portrait' : 'landscape') as 'portrait' | 'landscape', }; setDeviceInfo({ selectors, deviceData, orientation }); }).catch((error) => { console.warn('Failed to load device detection:', error); }); }, [userAgent]); // Update orientation on window resize const handleResize = useCallback(() => { const isPortrait = window.innerHeight > window.innerWidth; setDeviceInfo((prev) => ({ ...prev, orientation: { isPortrait, isLandscape: !isPortrait, orientation: (isPortrait ? 'portrait' : 'landscape') as 'portrait' | 'landscape', }, })); }, []); useEffect(() => { if (typeof window === 'undefined') return; window.addEventListener('resize', handleResize); window.addEventListener('orientationchange', handleResize); return () => { window.removeEventListener('resize', handleResize); window.removeEventListener('orientationchange', handleResize); }; }, [handleResize]); const { selectors, deviceData, orientation } = deviceInfo; return useMemo(() => ({ // Device type selectors isMobile: selectors.isMobile ?? false, isTablet: selectors.isTablet ?? false, isDesktop: selectors.isDesktop ?? false, isBrowser: selectors.isBrowser ?? false, isMobileOnly: selectors.isMobileOnly ?? false, isSmartTV: selectors.isSmartTV ?? false, isConsole: selectors.isConsole ?? false, isWearable: selectors.isWearable ?? false, isEmbedded: selectors.isEmbedded ?? false, // OS selectors isAndroid: selectors.isAndroid ?? false, isIOS: selectors.isIOS ?? false, isWindows: selectors.isWindows ?? false, isMacOs: selectors.isMacOs ?? false, isWinPhone: selectors.isWinPhone ?? false, // Browser selectors isChrome: selectors.isChrome ?? false, isFirefox: selectors.isFirefox ?? false, isSafari: selectors.isSafari ?? false, isOpera: selectors.isOpera ?? false, isIE: selectors.isIE ?? false, isEdge: selectors.isEdge ?? false, isEdgeChromium: selectors.isEdgeChromium ?? false, isLegacyEdge: selectors.isLegacyEdge ?? false, isChromium: selectors.isChromium ?? false, isMobileSafari: selectors.isMobileSafari ?? false, isYandex: selectors.isYandex ?? false, isMIUI: selectors.isMIUI ?? false, isSamsungBrowser: selectors.isSamsungBrowser ?? false, isElectron: selectors.isElectron ?? false, // iOS version selectors isIOS13: selectors.isIOS13 ?? false, isIPad13: selectors.isIPad13 ?? false, isIPhone13: selectors.isIPhone13 ?? false, isIPod13: selectors.isIPod13 ?? false, // Device information deviceType: deviceData.deviceType ?? 'unknown', osName: deviceData.osName ?? 'unknown', osVersion: deviceData.osVersion ?? 'unknown', browserName: deviceData.browserName ?? 'unknown', browserVersion: deviceData.browserVersion ?? 'unknown', fullBrowserVersion: deviceData.fullBrowserVersion ?? 'unknown', mobileVendor: deviceData.mobileVendor ?? 'unknown', mobileModel: deviceData.mobileModel ?? 'unknown', engineName: deviceData.engineName ?? 'unknown', engineVersion: deviceData.engineVersion ?? 'unknown', getUA: deviceData.getUA ?? '', // Orientation isPortrait: orientation.isPortrait, isLandscape: orientation.isLandscape, orientation: orientation.orientation, // Raw data (for advanced usage) selectors, deviceData, }), [selectors, deviceData, orientation]); } export type DeviceDetectResult = ReturnType;