Punycode support. Closes #2170

This commit is contained in:
Francis Cao committed 2026-07-17 10:22:37 -07:00
1 parent b47847958a
commit 8004afe63d
8 files changed
+40 -5

No files matched your search

+1
View File
@@ -100,6 +100,7 @@
"pg": "^8.22.0",
"prisma": "^7.8.0",
"prop-types": "^15.8.1",
"punycode": "^2.3.1",
"react": "^19.2.7",
"react-aria-components": "^1.19.0",
"react-dom": "^19.2.7",
+3
View File
@@ -149,6 +149,9 @@ importers:
prop-types:
specifier: ^15.8.1
version: 15.8.1
punycode:
specifier: ^2.3.1
version: 2.3.1
react:
specifier: ^19.2.7
version: 19.2.7
@@ -7,6 +7,7 @@ import { SortableLabel } from '@/components/common/SortableLabel';
import { useMessages } from '@/components/hooks';
import { Edit, Trash, Users } from '@/components/icons';
import { MenuButton } from '@/components/input/MenuButton';
import { decodePunycodeDomain } from '@/lib/format';
export function AdminWebsitesTable({ data = [], ...props }: { data: any[] }) {
const { t, labels } = useMessages();
@@ -23,7 +24,7 @@ export function AdminWebsitesTable({ data = [], ...props }: { data: any[] }) {
)}
</DataColumn>
<DataColumn id="domain" label={<SortableLabel label={t(labels.domain)} sortKey="domain" />}>
{(row: any) => <Text truncate>{row.domain}</Text>}
{(row: any) => <Text truncate>{decodePunycodeDomain(row.domain)}</Text>}
</DataColumn>
<DataColumn id="owner" label={t(labels.owner)}>
{(row: any) => {
+7 -2
View File
@@ -5,6 +5,7 @@ import { LinkButton } from '@/components/common/LinkButton';
import { SortableLabel } from '@/components/common/SortableLabel';
import { useMessages, useNavigation, useWebsiteListChartsQuery } from '@/components/hooks';
import { SquarePen } from '@/components/icons';
import { decodePunycodeDomain } from '@/lib/format';
import { WebsiteSparkline } from './WebsiteSparkline';
export interface WebsitesTableProps extends DataTableProps {
@@ -42,8 +43,12 @@ export function WebsitesTable({
style={{ minWidth: 0 }}
>
{(row: any) => (
<Text truncate title={row.domain} style={{ maxWidth: '100%' }}>
{row.domain}
<Text
truncate
title={decodePunycodeDomain(row.domain) ?? undefined}
style={{ maxWidth: '100%' }}
>
{decodePunycodeDomain(row.domain)}
</Text>
)}
</DataColumn>
@@ -4,6 +4,7 @@ import Link from '@/components/common/Link';
import { PageHeader } from '@/components/common/PageHeader';
import { useMessages, useNavigation, useWebsite } from '@/components/hooks';
import { ArrowLeft, Globe } from '@/components/icons';
import { decodePunycodeDomain } from '@/lib/format';
export function WebsiteSettingsHeader() {
const website = useWebsite();
@@ -17,7 +18,11 @@ export function WebsiteSettingsHeader() {
<IconLabel icon={<ArrowLeft />} label={t(labels.website)} />
</Link>
</Row>
<PageHeader title={website?.name} description={website?.domain} icon={<Globe />} />
<PageHeader
title={website?.name}
description={decodePunycodeDomain(website?.domain)}
icon={<Globe />}
/>
</>
);
}
+2 -1
View File
@@ -10,6 +10,7 @@ import {
useRegionNames,
} from '@/components/hooks';
import { GROUPED_DOMAINS } from '@/lib/constants';
import { decodePunycodeDomain } from '@/lib/format';
export interface MetricLabelProps {
type: string;
@@ -113,7 +114,7 @@ export function MetricLabel({ type, data }: MetricLabelProps) {
type="referrer"
value={label}
externalUrl={`https://${label}`}
label={!label && t(labels.none)}
label={!label ? t(labels.none) : decodePunycodeDomain(label)}
icon={<Favicon domain={label} />}
/>
);
+6
View File
@@ -42,3 +42,9 @@ test('truncateString', () => {
expect(format.truncateString('hello', 3)).toBe('hel');
expect(format.truncateString(undefined, 3)).toBeUndefined();
});
test('decodePunycodeDomain', () => {
expect(format.decodePunycodeDomain('xn--bcher-kva.example')).toBe('bücher.example');
expect(format.decodePunycodeDomain('example.com')).toBe('example.com');
expect(format.decodePunycodeDomain(undefined)).toBeUndefined();
});
+13
View File
@@ -1,3 +1,4 @@
import { toUnicode } from 'punycode';
import { DEFAULT_CURRENCY } from './constants';
export function parseTime(val: number) {
@@ -119,6 +120,18 @@ export function formatLongCurrency(value: number, currency: string, locale = 'en
return formatCurrency(n, currency, locale);
}
export function decodePunycodeDomain(domain?: string | null) {
if (!domain) {
return domain;
}
try {
return toUnicode(domain);
} catch {
return domain;
}
}
export function truncateString<T extends string | null | undefined>(
value: T,
maxLength: number,