pl-fe: Use ParsedContent for account bios
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@ -13,6 +13,7 @@ import { useAppSelector, useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useAccountHoverCardStore } from 'pl-fe/stores';
|
||||
|
||||
import { showAccountHoverCard } from './hover-ref-wrapper';
|
||||
import { ParsedContent } from './parsed-content';
|
||||
import { dateFormatOptions } from './relative-timestamp';
|
||||
import Scrobble from './scrobble';
|
||||
import { Card, CardBody, HStack, Icon, Stack, Text } from './ui';
|
||||
@ -98,7 +99,6 @@ const AccountHoverCard: React.FC<IAccountHoverCard> = ({ visible = true }) => {
|
||||
};
|
||||
|
||||
if (!account) return null;
|
||||
const accountBio = { __html: account.note_emojified };
|
||||
const memberSinceDate = intl.formatDate(account.created_at, { month: 'long', year: 'numeric' });
|
||||
const followedBy = me !== account.id && account.relationship?.followed_by === true;
|
||||
|
||||
@ -153,9 +153,10 @@ const AccountHoverCard: React.FC<IAccountHoverCard> = ({ visible = true }) => {
|
||||
<Text
|
||||
truncate
|
||||
size='sm'
|
||||
dangerouslySetInnerHTML={accountBio}
|
||||
className='mr-2 rtl:ml-2 rtl:mr-0 [&_br]:hidden [&_p:first-child]:inline [&_p:first-child]:truncate [&_p]:hidden'
|
||||
/>
|
||||
>
|
||||
<ParsedContent html={account.note_emojified} />
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import { getAcct } from 'pl-fe/utils/accounts';
|
||||
import { displayFqn } from 'pl-fe/utils/state';
|
||||
|
||||
import Badge from './badge';
|
||||
import { ParsedContent } from './parsed-content';
|
||||
import RelativeTimestamp from './relative-timestamp';
|
||||
import { Avatar, Emoji, HStack, Icon, IconButton, Stack, Text } from './ui';
|
||||
|
||||
@ -348,9 +349,9 @@ const Account = ({
|
||||
<Text
|
||||
truncate
|
||||
size='sm'
|
||||
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
|
||||
className='mr-2 rtl:ml-2 rtl:mr-0 [&_br]:hidden [&_p:first-child]:inline [&_p:first-child]:truncate [&_p]:hidden'
|
||||
/>
|
||||
>
|
||||
<ParsedContent html={account.note_emojified} />
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
@ -5,6 +5,7 @@ import { Link } from 'react-router-dom';
|
||||
|
||||
import HashtagLink from './hashtag-link';
|
||||
import HoverRefWrapper from './hover-ref-wrapper';
|
||||
import StatusMention from './status-mention';
|
||||
|
||||
import type { Mention } from 'pl-api';
|
||||
|
||||
@ -12,7 +13,9 @@ const nodesToText = (nodes: Array<DOMNode>): string =>
|
||||
nodes.map(node => node.type === 'text' ? node.data : node.type === 'tag' ? nodesToText(node.children as Array<DOMNode>) : '').join('');
|
||||
|
||||
interface IParsedContent {
|
||||
/** HTML content to display. */
|
||||
html: string;
|
||||
/** Array of mentioned accounts. */
|
||||
mentions?: Array<Mention>;
|
||||
/** Whether it's a status which has a quote. */
|
||||
hasQuote?: boolean;
|
||||
@ -62,20 +65,26 @@ const ParsedContent: React.FC<IParsedContent> = (({ html, mentions, hasQuote })
|
||||
</a>
|
||||
);
|
||||
|
||||
if (classes?.includes('mention') && mentions) {
|
||||
const mention = mentions.find(({ url }) => domNode.attribs.href === url);
|
||||
if (mention) {
|
||||
if (classes?.includes('mention')) {
|
||||
if (mentions) {
|
||||
const mention = mentions.find(({ url }) => domNode.attribs.href === url);
|
||||
if (mention) {
|
||||
return (
|
||||
<HoverRefWrapper accountId={mention.id} inline>
|
||||
<Link
|
||||
to={`/@${mention.acct}`}
|
||||
className='text-primary-600 hover:underline dark:text-accent-blue'
|
||||
dir='ltr'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
@{mention.username}
|
||||
</Link>
|
||||
</HoverRefWrapper>
|
||||
);
|
||||
}
|
||||
} else if (domNode.attribs['data-user']) {
|
||||
return (
|
||||
<HoverRefWrapper accountId={mention.id} inline>
|
||||
<Link
|
||||
to={`/@${mention.acct}`}
|
||||
className='text-primary-600 hover:underline dark:text-accent-blue'
|
||||
dir='ltr'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
@{mention.username}
|
||||
</Link>
|
||||
</HoverRefWrapper>
|
||||
<StatusMention accountId={domNode.attribs['data-user']} fallback={fallback} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 || ' ' }}
|
||||
/>
|
||||
{!!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'>
|
||||
|
||||
@ -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'>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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'>
|
||||
|
||||
Reference in New Issue
Block a user