Grouped referrers.

This commit is contained in:
Mike Cao 2025-02-07 10:14:01 -08:00
parent fd4a405779
commit 84193a4ec5
6 changed files with 175 additions and 71 deletions

View file

@ -1,3 +1,5 @@
import { GROUPED_DOMAINS } from '@/lib/constants';
function getHostName(url: string) {
const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?([^:/\n?=]+)/im);
return match && match.length > 1 ? match[1] : null;
@ -9,16 +11,11 @@ export function Favicon({ domain, ...props }) {
}
const hostName = domain ? getHostName(domain) : null;
const src = hostName
? `https://icons.duckduckgo.com/ip3/${GROUPED_DOMAINS[hostName]?.domain || hostName}.ico`
: null;
return hostName ? (
<img
src={`https://icons.duckduckgo.com/ip3/${hostName}.ico`}
width={16}
height={16}
alt=""
{...props}
/>
) : null;
return hostName ? <img src={src} width={16} height={16} alt="" {...props} /> : null;
}
export default Favicon;

View file

@ -295,6 +295,8 @@ export const labels = defineMessages({
paidSocial: { id: 'label.paid-social', defaultMessage: 'Paid social' },
paidShopping: { id: 'label.paid-shopping', defaultMessage: 'Paid shopping' },
paidVideo: { id: 'label.paid-video', defaultMessage: 'Paid video' },
grouped: { id: 'label.grouped', defaultMessage: 'Grouped' },
other: { id: 'label.other', defaultMessage: 'Other' },
});
export const messages = defineMessages({

View file

@ -1,12 +1,53 @@
import FilterLink from '@/components/common/FilterLink';
import Favicon from '@/components/common/Favicon';
import { useMessages } from '@/components/hooks';
import { useMessages, useNavigation } from '@/components/hooks';
import MetricsTable, { MetricsTableProps } from './MetricsTable';
import FilterButtons from '@/components/common/FilterButtons';
import thenby from 'thenby';
import { GROUPED_DOMAINS } from '@/lib/constants';
import { Flexbox } from 'react-basics';
export function ReferrersTable(props: MetricsTableProps) {
export interface ReferrersTableProps extends MetricsTableProps {
allowFilter?: boolean;
}
export function ReferrersTable({ allowFilter, ...props }: ReferrersTableProps) {
const {
router,
renderUrl,
query: { view = 'referrer' },
} = useNavigation();
const { formatMessage, labels } = useMessages();
const handleSelect = (key: any) => {
router.push(renderUrl({ view: key }), { scroll: false });
};
const buttons = [
{
label: formatMessage(labels.domain),
key: 'referrer',
},
{
label: formatMessage(labels.grouped),
key: 'grouped',
},
];
const renderLink = ({ x: referrer }) => {
if (view === 'grouped') {
if (referrer === '_other') {
return `(${formatMessage(labels.other)})`;
} else {
return (
<Flexbox alignItems="center" gap={10}>
<Favicon domain={referrer} />
{GROUPED_DOMAINS.find(({ domain }) => domain === referrer)?.name}
</Flexbox>
);
}
}
return (
<FilterLink
id="referrer"
@ -19,6 +60,27 @@ export function ReferrersTable(props: MetricsTableProps) {
);
};
const groupedFilter = (data: any[]) => {
const groups = { _other: 0 };
for (const { x, y } of data) {
for (const { domain, match } of GROUPED_DOMAINS) {
if (Array.isArray(match) ? match.some(str => x.includes(str)) : x.includes(match)) {
if (!groups[domain]) {
groups[domain] = 0;
}
groups[domain] += y;
} else {
groups._other += y;
}
}
}
return Object.keys(groups)
.map((key: any) => ({ x: key, y: groups[key] }))
.sort(thenby.firstBy('y', -1));
};
return (
<>
<MetricsTable
@ -26,8 +88,13 @@ export function ReferrersTable(props: MetricsTableProps) {
title={formatMessage(labels.referrers)}
type="referrer"
metric={formatMessage(labels.views)}
dataFilter={view === 'grouped' ? groupedFilter : undefined}
renderLabel={renderLink}
/>
>
{allowFilter && (
<FilterButtons items={buttons} selectedKey={view} onSelect={handleSelect} />
)}
</MetricsTable>
</>
);
}