pl-fe: allow to always display links target domain

Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
Nicole Mikołajczyk
2025-04-13 19:08:47 +02:00
parent 4f45b14059
commit 47945f4d12
5 changed files with 39 additions and 4 deletions

View File

@ -23,6 +23,21 @@ const GREENTEXT_CLASS = 'dark:text-accent-green text-lime-600';
const nodesToText = (nodes: Array<DOMNode>): string =>
nodes.map(node => node.type === 'text' ? node.data : node.type === 'tag' ? nodesToText(node.children as Array<DOMNode>) : '').join('');
const isHostNotVisible = (href: string, nodes: Array<DOMNode>): false | string => {
try {
const { host } = new URL(href);
const text = nodesToText(nodes).trim();
if (new RegExp(`^(https?://)?(www\.)?${host}(/|$)`).test(text)) {
return false;
} else {
return host;
}
} catch (e) {
return false;
}
};
interface IParsedContent {
/** HTML content to display. */
html: string;
@ -32,7 +47,10 @@ interface IParsedContent {
hasQuote?: boolean;
/** Related custom emojis. */
emojis?: Array<CustomEmoji>;
/** Whether to call a function to remove tracking parameters from URLs. */
cleanUrls?: boolean;
/** Whether to display link target domain when it's not part of the text. */
displayTargetHost?: boolean;
greentext?: boolean;
speakAsCat?: boolean;
}
@ -81,6 +99,7 @@ function parseContent({
hasQuote,
emojis,
cleanUrls = false,
displayTargetHost = true,
greentext = false,
speakAsCat = false,
}: IParsedContent, extractHashtags = false) {
@ -158,6 +177,8 @@ function parseContent({
}
}
const host = displayTargetHost && isHostNotVisible(href, domNode.children as Array<DOMNode>);
const fallback = (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<a
@ -168,7 +189,10 @@ function parseContent({
target='_blank'
title={domNode.attribs.href}
>
{domToReact(domNode.children as DOMNode[], options)}
{domToReact(domNode.children as Array<DOMNode>, options)}
{host && (
<span className='text-xs lowercase'>{' '}[{host}]</span>
)}
</a>
);
@ -196,7 +220,7 @@ function parseContent({
}
}
if (classes?.includes('hashtag')) {
if (classes?.includes('hashtag') || domNode.attribs.rel === 'tag') {
const hashtag = nodesToText(domNode.children as Array<DOMNode>);
if (hashtag) {
return <HashtagLink hashtag={hashtag.replace(/^#/, '')} />;
@ -248,10 +272,10 @@ function parseContent({
}
const ParsedContent: React.FC<IParsedContent> = React.memo((props) => {
const settings = useSettings();
const { urlPrivacy } = useSettings();
if (props.cleanUrls === undefined) {
props = { ...props, cleanUrls: settings.urlPrivacy.clearLinksInContent };
props = { ...props, cleanUrls: urlPrivacy.clearLinksInContent, displayTargetHost: urlPrivacy.displayTargetHost };
}
return parseContent(props, false);

View File

@ -147,6 +147,7 @@ const StatusContent: React.FC<IStatusContent> = React.memo(({
hasQuote: !!status.quote_id,
emojis: status.emojis,
cleanUrls: urlPrivacy.clearLinksInContent,
displayTargetHost: urlPrivacy.displayTargetHost,
greentext,
speakAsCat: status.account.speak_as_cat,
}, true), [content]);

View File

@ -26,6 +26,7 @@ const UrlPrivacy = () => {
const { urlPrivacy } = useSettings();
const [displayTargetHost, setDisplayTargetHost] = useState(urlPrivacy.displayTargetHost);
const [clearLinksInCompose, setClearLinksInCompose] = useState(urlPrivacy.clearLinksInCompose);
const [clearLinksInContent, setClearLinksInContent] = useState(urlPrivacy.clearLinksInContent);
// const [allowReferralMarketing, setAllowReferralMarketing] = useState(urlPrivacy.allowReferralMarketing);
@ -35,6 +36,7 @@ const UrlPrivacy = () => {
const onSubmit = () => {
dispatch(changeSetting(['urlPrivacy'], {
...urlPrivacy,
displayTargetHost,
clearLinksInCompose,
clearLinksInContent,
// allowReferralMarketing,
@ -58,6 +60,12 @@ const UrlPrivacy = () => {
<CardBody>
<Form onSubmit={onSubmit}>
<List>
<ListItem label={<FormattedMessage id='url_privacy.display_target_host' defaultMessage='Always display the domain external links lead to' />}>
<Toggle checked={displayTargetHost} onChange={({ target }) => setDisplayTargetHost(target.checked)} />
</ListItem>
</List>
<List>
<ListItem label={<FormattedMessage id='url_privacy.clear_links_in_compose' defaultMessage='Suggest removing tracking parameters when composing a post' />}>
<Toggle checked={clearLinksInCompose} onChange={({ target }) => setClearLinksInCompose(target.checked)} />

View File

@ -1670,6 +1670,7 @@
"upload_progress.label": "Uploading…",
"url_privacy.clear_links_in_compose": "Suggest removing tracking parameters when composing a post",
"url_privacy.clear_links_in_content": "Remove tracking parameters from displayed posts",
"url_privacy.display_target_host": "Always display the domain external links lead to",
"url_privacy.hash_url.hint": "SHA256 hash of rules database, used to avoid unnecessary fetches, eg. {url}",
"url_privacy.hash_url.label": "URL cleaning rules hash address (optional)",
"url_privacy.hash_url.placeholder": "Hash URL",

View File

@ -43,6 +43,7 @@ const settingsSchema = v.object({
allowReferralMarketing: v.fallback(v.boolean(), false),
rulesUrl: v.fallback(v.string(), ''),
hashUrl: v.fallback(v.string(), ''),
displayTargetHost: v.fallback(v.boolean(), true),
}),
theme: v.fallback(v.optional(v.object({