import React, { useEffect, useState } from 'react' import * as storage from '../../util/storage' import { getFiltersByKeyPrefix, hasConversionGoalFilter, isFilteringOnFixedValue } from '../../util/filters' import ListReport from '../reports/list' import * as metrics from '../reports/metrics' import * as api from '../../api' import * as url from '../../util/url' import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning' import { useQueryContext } from '../../query-context' import { useSiteContext } from '../../site-context' import { browsersRoute, browserVersionsRoute, operatingSystemsRoute, operatingSystemVersionsRoute, screenSizesRoute } from '../../router' import { TabButton, TabWrapper } from '../../components/tabs' // Icons copied from https://github.com/alrra/browser-logos const BROWSER_ICONS = { Chrome: 'chrome.svg', curl: 'curl.svg', Safari: 'safari.png', Firefox: 'firefox.svg', 'Microsoft Edge': 'edge.svg', Vivaldi: 'vivaldi.svg', Opera: 'opera.svg', 'Samsung Browser': 'samsung-internet.svg', Chromium: 'chromium.svg', 'UC Browser': 'uc.svg', 'Yandex Browser': 'yandex.png', // Only PNG available in browser-logos // Logos underneath this line are not available in browser-logos. Grabbed from random places on the internets. 'DuckDuckGo Privacy Browser': 'duckduckgo.svg', 'MIUI Browser': 'miui.webp', 'Huawei Browser Mobile': 'huawei.png', 'QQ Browser': 'qq.png', Ecosia: 'ecosia.png', 'vivo Browser': 'vivo.png' } export function browserIconFor(browser) { const filename = BROWSER_ICONS[browser] || 'fallback.svg' return ( ) } function Browsers({ afterFetchData }) { const site = useSiteContext() const { query } = useQueryContext() function fetchData() { return api.get(url.apiPath(site, '/browsers'), query) } function getFilterInfo(listItem) { return { prefix: 'browser', filter: ['is', 'browser', [listItem['name']]] } } function renderIcon(listItem) { return browserIconFor(listItem.name) } function chooseMetrics() { return [ metrics.createVisitors({ meta: { plot: true } }), !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { showOnHover: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate() ].filter((metric) => !!metric) } return ( search }} /> ) } function BrowserVersions({ afterFetchData }) { const { query } = useQueryContext() const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/browser-versions'), query) } function renderIcon(listItem) { return browserIconFor(listItem.browser) } function getFilterInfo(listItem) { if (getSingleFilter(query, 'browser') == '(not set)') { return null } return { prefix: 'browser_version', filter: ['is', 'browser_version', [listItem.version]] } } function chooseMetrics() { return [ metrics.createVisitors({ meta: { plot: true } }), !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { showOnHover: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate() ].filter((metric) => !!metric) } return ( search }} /> ) } // Icons copied from https://github.com/ngeenx/operating-system-logos const OS_ICONS = { iOS: 'ios.png', Mac: 'mac.png', Windows: 'windows.png', 'Windows Phone': 'windows.png', Android: 'android.png', 'GNU/Linux': 'gnu_linux.png', Ubuntu: 'ubuntu.png', 'Chrome OS': 'chrome_os.png', iPadOS: 'ipad_os.png', 'Fire OS': 'fire_os.png', HarmonyOS: 'harmony_os.png', Tizen: 'tizen.png', PlayStation: 'playstation.png', KaiOS: 'kai_os.png', Fedora: 'fedora.png', FreeBSD: 'freebsd.png' } export function osIconFor(os) { const filename = OS_ICONS[os] || 'fallback.svg' return ( ) } function OperatingSystems({ afterFetchData }) { const { query } = useQueryContext() const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/operating-systems'), query) } function getFilterInfo(listItem) { return { prefix: 'os', filter: ['is', 'os', [listItem['name']]] } } function chooseMetrics() { return [ metrics.createVisitors({ meta: { plot: true } }), !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { showOnHover: true, hiddenOnMobile: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate() ].filter((metric) => !!metric) } function renderIcon(listItem) { return osIconFor(listItem.name) } return ( search }} /> ) } function OperatingSystemVersions({ afterFetchData }) { const { query } = useQueryContext() const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/operating-system-versions'), query) } function renderIcon(listItem) { return osIconFor(listItem.os) } function getFilterInfo(listItem) { if (getSingleFilter(query, 'os') == '(not set)') { return null } return { prefix: 'os_version', filter: ['is', 'os_version', [listItem.version]] } } function chooseMetrics() { return [ metrics.createVisitors({ meta: { plot: true } }), !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { showOnHover: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate() ].filter((metric) => !!metric) } return ( search }} /> ) } function ScreenSizes({ afterFetchData }) { const { query } = useQueryContext() const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/screen-sizes'), query) } function renderIcon(listItem) { return screenSizeIconFor(listItem.name) } function getFilterInfo(listItem) { return { prefix: 'screen', filter: ['is', 'screen', [listItem['name']]] } } function chooseMetrics() { return [ metrics.createVisitors({ meta: { plot: true } }), !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { showOnHover: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate() ].filter((metric) => !!metric) } return ( search }} /> ) } export function screenSizeIconFor(screenSize) { let svg = null if (screenSize === 'Mobile') { svg = ( ) } else if (screenSize === 'Tablet') { svg = ( ) } else if (screenSize === 'Laptop') { svg = ( ) } else if (screenSize === 'Desktop') { svg = ( ) } return {svg} } export default function Devices() { const { query } = useQueryContext() const site = useSiteContext() const tabKey = `deviceTab__${site.domain}` const storedTab = storage.getItem(tabKey) const [mode, setMode] = useState(storedTab || 'browser') const [loading, setLoading] = useState(true) const [skipImportedReason, setSkipImportedReason] = useState(null) function switchTab(mode) { storage.setItem(tabKey, mode) setMode(mode) } function afterFetchData(apiResponse) { setLoading(false) setSkipImportedReason(apiResponse.skip_imported_reason) } useEffect(() => setLoading(true), [query, mode]) function renderContent() { switch (mode) { case 'browser': if (isFilteringOnFixedValue(query, 'browser')) { return } return case 'os': if (isFilteringOnFixedValue(query, 'os')) { return } return case 'size': default: return } } return (

Devices

{[ { label: 'Browser', value: 'browser' }, { label: 'OS', value: 'os' }, { label: 'Size', value: 'size' } ].map(({ label, value }) => ( switchTab(value)} > {label} ))}
{renderContent()}
) } function getSingleFilter(query, filterKey) { const matches = getFiltersByKeyPrefix(query, filterKey) if (matches.length != 1) { return null } const clauses = matches[0][2] return clauses.length == 1 ? clauses[0] : null }