import { IMedia, ArrowSize, DeviceCategory } from '../types'; import HistoryService from './history'; export const Helpers = { createArray: (length: number): number[] => Array.from({ length }), // eslint-disable-next-line max-len chunkArray: (array: T[], chunkSize: number): T[][] => Array.from({ length: Math.ceil(array.length / chunkSize) }, (_, i) => array.slice(i * chunkSize, (i + 1) * chunkSize)), getAbsoluteBackgroundColor: (element?: Element | null): string => { if (!element) { return 'white'; } const bg = window.getComputedStyle(element).getPropertyValue('background-color'); if (bg === 'transparent' || bg === 'rgba(0, 0, 0, 0)') { return element.parentElement ? Helpers.getAbsoluteBackgroundColor(element.parentElement) : 'white'; } return bg; }, isValidJson: (jsonString: string): boolean => { try { const parsed: unknown = JSON.parse(jsonString); return Boolean(parsed) && typeof parsed === 'object'; } catch { return false; } }, isMobileDevice: (): boolean => { if (typeof navigator === 'undefined') { return false; } const userAgent = navigator.userAgent.toLowerCase(); // Explicit mobile device patterns - more specific detection const mobilePatterns = [ /android.*mobile/i, // Android phones (not tablets) /iphone/i, /ipod/i, /blackberry/i, /windows phone/i, /windows ce/i, /palm/i, /symbian/i, /webos/i, /mobile/i, ]; // Explicit tablet patterns const tabletPatterns = [ /ipad/i, /android(?!.*mobile)/i, // Android tablets /tablet/i, /kindle/i, /playbook/i, /nexus.*7/i, ]; // Check for explicit mobile patterns first const isMobileUserAgent = mobilePatterns.some((pattern) => pattern.test(userAgent)); const isTabletUserAgent = tabletPatterns.some((pattern) => pattern.test(userAgent)); // If it's explicitly a desktop OS, return false regardless of touch if ( /windows nt|mac os x|linux.*x86/i.test(userAgent) && !isMobileUserAgent && !isTabletUserAgent ) { return false; } // Return true only for explicit mobile devices return isMobileUserAgent || isTabletUserAgent; }, getDeviceCategory: (): DeviceCategory => { if (typeof navigator === 'undefined' || typeof window === 'undefined') { return 'other'; } const ua = navigator.userAgent; // Gaming consoles and Smart TVs → 'other' if (/PlayStation|Xbox|Nintendo|SmartTV|SMART-TV|Tizen|HbbTV|AppleTV|Roku/i.test(ua)) { return 'other'; } // webOS Smart TV (LG) without a Mobile token — modern webOS is a TV OS if (/webOS/i.test(ua) && !/Mobile/i.test(ua)) { return 'other'; } // Chrome OS (Chromebooks) → desktop if (/CrOS/i.test(ua)) { return 'desktop'; } // iPad iOS 13+ — Safari reports 'Macintosh' but exposes many touch points if (/Macintosh/i.test(ua) && navigator.maxTouchPoints > 1) { return 'mobile'; } // Explicit mobile phone patterns if (/Android.*Mobile|iPhone|iPod|BlackBerry|Windows Phone|IEMobile|Opera Mini/i.test(ua)) { return 'mobile'; } // Tablets — touch-first devices, group with mobile if (/iPad|Android(?!.*Mobile)|Tablet|Kindle|Silk|PlayBook/i.test(ua)) { return 'mobile'; } // Explicit desktop OS patterns if (/Windows NT|Macintosh|Mac OS X|Linux/i.test(ua)) { return 'desktop'; } // Ambiguous UA: use touch capability and screen width as a final hint const hasTouchScreen = navigator.maxTouchPoints > 0; const screenWidth = window.screen?.width ?? 1920; if (hasTouchScreen && screenWidth <= 768) { return 'mobile'; } return 'other'; }, pushPostToHistory: (post: IMedia): void => { HistoryService.push(post); }, replacePostInHistory: (post: IMedia): void => { HistoryService.replace(post); }, uniqId: (): string => `${Date.now()}${Math.floor(Math.random() * 1000)}`, randomId: (): string => Math.random().toString(36).slice(2, 10), calculateArrowSize: (containerWidth: number): ArrowSize => { if (containerWidth < 200) return 'nano'; if (containerWidth < 300) return 'micro'; if (containerWidth < 650) return 'small'; if (containerWidth < 800) return 'compact'; if (containerWidth < 1000) return 'medium'; return 'large'; }, }; export default Helpers;