Punycode support. Closes #2170
This commit is contained in:
1 parent
b47847958a
commit
8004afe63d
8 files changed
+40
-5
No files matched your search
@@ -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",
|
||||
|
||||
Generated
+3
@@ -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) => {
|
||||
|
||||
@@ -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 />}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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} />}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user