2023-06-12 11:47:13 +02:00

101 lines
2.1 KiB
TypeScript

import React, { memo } from '../../lib/teact/teact';
import { getActions } from '../../global';
import convertPunycode from '../../lib/punycode';
import type { FC } from '../../lib/teact/teact';
import { ApiMessageEntityTypes } from '../../api/types';
import {
DEBUG,
} from '../../config';
import buildClassName from '../../util/buildClassName';
import { ensureProtocol } from '../../util/ensureProtocol';
import useLastCallback from '../../hooks/useLastCallback';
type OwnProps = {
url?: string;
text: string;
className?: string;
children?: React.ReactNode;
isRtl?: boolean;
};
const SafeLink: FC<OwnProps> = ({
url,
text,
className,
children,
isRtl,
}) => {
const { openUrl } = getActions();
const content = children || text;
const isSafe = url === text;
const handleClick = useLastCallback((e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
if (!url) return true;
e.preventDefault();
openUrl({ url, shouldSkipModal: isSafe });
return false;
});
if (!url) {
return undefined;
}
const classNames = buildClassName(
className || 'text-entity-link',
text.length > 50 && 'long-word-break-all',
);
return (
<a
href={ensureProtocol(url)}
title={getDomain(url)}
target="_blank"
rel="noopener noreferrer"
className={classNames}
onClick={handleClick}
dir={isRtl ? 'rtl' : 'auto'}
data-entity-type={ApiMessageEntityTypes.Url}
>
{content}
</a>
);
};
function getDomain(url?: string) {
if (!url) {
return undefined;
}
const href = ensureProtocol(url);
if (!href) {
return undefined;
}
try {
let decodedHref = decodeURI(href.replace(/%%/g, '%25'));
const match = decodedHref.match(/^https?:\/\/([^/:?#]+)(?:[/:?#]|$)/i);
if (!match) {
return undefined;
}
const domain = match[1];
decodedHref = decodedHref.replace(domain, convertPunycode(domain));
return decodedHref;
} catch (error) {
if (DEBUG) {
// eslint-disable-next-line no-console
console.error('SafeLink.getDecodedUrl error ', url, error);
}
}
return undefined;
}
export default memo(SafeLink);