fix(referrers): replace low-res Google favicon globe with proper icons

Detect Google's 16x16 default globe fallback via naturalWidth on load
and fall back to Phosphor icons. Add Chrome icon for googlechrome.github.io,
CursorClick for Direct, and abbreviation support (ig, fb, yt).
This commit is contained in:
Usman Baig
2026-03-23 12:23:10 +01:00
parent 31471792f8
commit 508bb006a8
2 changed files with 26 additions and 6 deletions

View File

@@ -2,7 +2,6 @@
import { useState, useEffect } from 'react'
import { logger } from '@/lib/utils/logger'
import Image from 'next/image'
import { formatNumber } from '@ciphera-net/ui'
import { getReferrerDisplayName, getReferrerFavicon, getReferrerIcon, mergeReferrersByDisplayName } from '@/lib/utils/icons'
import Link from 'next/link'
@@ -48,14 +47,21 @@ export default function TopReferrers({ referrers, collectReferrers = true, siteI
const useFavicon = faviconUrl && !faviconFailed.has(referrer)
if (useFavicon) {
return (
<Image
// eslint-disable-next-line @next/next/no-img-element
<img
src={faviconUrl}
alt=""
width={20}
height={20}
className="w-5 h-5 flex-shrink-0 rounded object-contain"
onError={() => setFaviconFailed((prev) => new Set(prev).add(referrer))}
unoptimized
onLoad={(e) => {
// Google's favicon service returns a 16x16 default globe when no real favicon exists
const img = e.currentTarget
if (img.naturalWidth <= 16) {
setFaviconFailed((prev) => new Set(prev).add(referrer))
}
}}
/>
)
}

View File

@@ -6,6 +6,7 @@ import {
DeviceTablet,
Desktop,
Link,
CursorClick,
} from '@phosphor-icons/react'
import {
SiGoogle,
@@ -122,12 +123,16 @@ const SI = { size: 16 } as const
export function getReferrerIcon(referrerName: string) {
if (!referrerName) return <Globe className="text-neutral-400" />
const lower = referrerName.toLowerCase()
// Direct traffic
if (lower === 'direct') return <CursorClick className="text-neutral-500" />
// Browsers as referrers (e.g. googlechrome.com, firefox.com)
if (lower.includes('googlechrome') || lower.includes('chrome')) return <img src="/icons/browsers/chrome.svg" alt="Chrome" width={16} height={16} className="inline-block" />
// Social / platforms
if (lower.includes('google') && !lower.includes('gemini')) return <SiGoogle size={SI.size} color="#4285F4" />
if (lower.includes('facebook')) return <SiFacebook size={SI.size} color="#0866FF" />
if (lower.includes('facebook') || lower === 'fb') return <SiFacebook size={SI.size} color="#0866FF" />
if (lower.includes('twitter') || lower.includes('t.co') || lower.includes('x.com')) return <XIcon />
if (lower.includes('linkedin')) return <LinkedInIcon />
if (lower.includes('instagram')) return <SiInstagram size={SI.size} color="#E4405F" />
if (lower.includes('instagram') || lower === 'ig') return <SiInstagram size={SI.size} color="#E4405F" />
if (lower.includes('github')) return <SiGithub size={SI.size} color="#fff" />
if (lower.includes('youtube')) return <SiYoutube size={SI.size} color="#FF0000" />
if (lower.includes('reddit')) return <SiReddit size={SI.size} color="#FF4500" />
@@ -183,6 +188,10 @@ const REFERRER_DISPLAY_OVERRIDES: Record<string, string> = {
threads: 'Threads',
tumblr: 'Tumblr',
quora: 'Quora',
ig: 'Instagram',
fb: 'Facebook',
yt: 'YouTube',
googlechrome: 'Google Chrome',
't.co': 'X',
'x.com': 'X',
// AI assistants and search tools
@@ -239,7 +248,12 @@ export function getReferrerDisplayName(referrer: string): string {
const trimmed = referrer.trim()
if (trimmed === '') return ''
const hostname = getReferrerHostname(trimmed)
if (!hostname) return trimmed
if (!hostname) {
// Plain names without a dot (e.g. "Ig", "Direct") — check override map before returning raw
const overrideByPlain = REFERRER_DISPLAY_OVERRIDES[trimmed.toLowerCase()]
if (overrideByPlain) return overrideByPlain
return trimmed
}
const overrideByHostname = REFERRER_DISPLAY_OVERRIDES[hostname]
if (overrideByHostname) return overrideByHostname
const label = getReferrerLabel(hostname)