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
}