pl-fe: Use ParsedContent for account bios

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2024-10-06 00:04:15 +02:00
parent cacc89483e
commit 8bc30f18f8
7 changed files with 54 additions and 83 deletions

View File

@ -7,6 +7,7 @@ import { useAccount } from 'pl-fe/api/hooks';
import Account from 'pl-fe/components/account';
import Badge from 'pl-fe/components/badge';
import HoverRefWrapper from 'pl-fe/components/hover-ref-wrapper';
import { ParsedContent } from 'pl-fe/components/parsed-content';
import RelativeTimestamp from 'pl-fe/components/relative-timestamp';
import { Avatar, Stack, Text } from 'pl-fe/components/ui';
import ActionButton from 'pl-fe/features/ui/components/action-button';
@ -67,12 +68,15 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
withRelationship={false}
/>
<Text
truncate
align='left'
className='line-clamp-2 inline text-ellipsis [&_br]:hidden [&_p:first-child]:inline [&_p:first-child]:truncate [&_p]:hidden'
dangerouslySetInnerHTML={{ __html: account.note_emojified || '&nbsp;' }}
/>
{!!account.note_emojified && (
<Text
truncate
align='left'
className='line-clamp-2 inline text-ellipsis [&_br]:hidden [&_p:first-child]:inline [&_p:first-child]:truncate [&_p]:hidden'
>
<ParsedContent html={account.note_emojified} />
</Text>
)}
</Stack>
<div className='grid grid-cols-3 gap-1 py-4'>

View File

@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import GroupAvatar from 'pl-fe/components/groups/group-avatar';
import { ParsedContent } from 'pl-fe/components/parsed-content';
import StillImage from 'pl-fe/components/still-image';
import { HStack, Icon, Stack, Text } from 'pl-fe/components/ui';
import { useModalsStore } from 'pl-fe/stores';
@ -150,9 +151,10 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
<Text
theme='muted'
align='center'
dangerouslySetInnerHTML={{ __html: group.note_emojified }}
className='[&_a]:text-primary-600 [&_a]:hover:underline [&_a]:dark:text-accent-blue'
/>
>
<ParsedContent html={group.note_emojified} />
</Text>
</Stack>
<HStack alignItems='center' space={2} data-testid='group-actions'>

View File

@ -1,6 +1,7 @@
import React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { ParsedContent } from 'pl-fe/components/parsed-content';
import { Avatar, Divider, HStack, Stack, Text, Button } from 'pl-fe/components/ui';
import toast from 'pl-fe/toast';
import copy from 'pl-fe/utils/copy';
@ -57,8 +58,9 @@ const ConfirmationStep: React.FC<IConfirmationStep> = ({ group }) => {
<Text
size='md'
className='mx-auto max-w-sm [&_a]:text-primary-600 [&_a]:hover:underline [&_a]:dark:text-accent-blue'
dangerouslySetInnerHTML={{ __html: group.note_emojified }}
/>
>
<ParsedContent html={group.note_emojified} />
</Text>
</Stack>
</Stack>

View File

@ -1,13 +1,11 @@
import parse, { Element, type HTMLReactParserOptions, domToReact, type DOMNode } from 'html-react-parser';
import React, { useMemo } from 'react';
import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import Badge from 'pl-fe/components/badge';
import HashtagLink from 'pl-fe/components/hashtag-link';
import Markup from 'pl-fe/components/markup';
import { ParsedContent } from 'pl-fe/components/parsed-content';
import { dateFormatOptions } from 'pl-fe/components/relative-timestamp';
import Scrobble from 'pl-fe/components/scrobble';
import StatusMention from 'pl-fe/components/status-mention';
import { Icon, HStack, Stack, Text } from 'pl-fe/components/ui';
import { useAppSelector, usePlFeConfig } from 'pl-fe/hooks';
import { capitalize } from 'pl-fe/utils/strings';
@ -104,54 +102,6 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
);
};
const note = useMemo(() => {
if (!account) return false;
const options: HTMLReactParserOptions = {
replace(domNode) {
if (domNode instanceof Element && ['script', 'iframe'].includes(domNode.name)) {
return null;
}
if (domNode instanceof Element && domNode.name === 'a') {
const classes = domNode.attribs.class?.split(' ');
const id = domNode.attribs['data-user'];
const fallback = (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<a
{...domNode.attribs}
onClick={(e) => e.stopPropagation()}
rel='nofollow noopener'
target='_blank'
title={domNode.attribs.href}
>
{domToReact(domNode.children as DOMNode[], options)}
</a>
);
if (classes?.includes('mention') && id) {
return (
<StatusMention accountId={id} fallback={fallback} />
);
}
if (classes?.includes('hashtag')) {
const child = domToReact(domNode.children as DOMNode[]);
const hashtag = typeof child === 'string' ? child.replace(/^#/, '') : undefined;
if (hashtag) {
return <HashtagLink hashtag={hashtag} />;
}
}
return fallback;
}
},
};
return !!account.note.length && parse(account.note_emojified, options);
}, [account?.note_emojified]);
if (!account) {
return (
<div className='mt-6 min-w-0 flex-1 sm:px-2'>
@ -206,8 +156,10 @@ const ProfileInfoPanel: React.FC<IProfileInfoPanel> = ({ account, username }) =>
<ProfileStats account={account} />
{note && (
<Markup size='sm'>{note}</Markup>
{!!account.note_emojified && (
<Markup size='sm'>
<ParsedContent html={account.note_emojified} />
</Markup>
)}
<div className='flex flex-col items-start gap-2 md:flex-row md:flex-wrap md:items-center'>