107 lines
2.7 KiB
TypeScript
107 lines
2.7 KiB
TypeScript
import { useCallback } from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { useLinks } from 'mastodon/hooks/useLinks';
|
|
|
|
import { useAppSelector } from '../store';
|
|
import { isModernEmojiEnabled } from '../utils/environment';
|
|
import type { OnElementHandler } from '../utils/html';
|
|
|
|
import { EmojiHTML } from './emoji/html';
|
|
import { HandledLink } from './status/handled_link';
|
|
|
|
interface AccountBioProps {
|
|
className: string;
|
|
accountId: string;
|
|
showDropdown?: boolean;
|
|
}
|
|
|
|
export const AccountBio: React.FC<AccountBioProps> = ({
|
|
className,
|
|
accountId,
|
|
showDropdown = false,
|
|
}) => {
|
|
const handleClick = useLinks(showDropdown);
|
|
const handleNodeChange = useCallback(
|
|
(node: HTMLDivElement | null) => {
|
|
if (
|
|
!showDropdown ||
|
|
!node ||
|
|
node.childNodes.length === 0 ||
|
|
isModernEmojiEnabled()
|
|
) {
|
|
return;
|
|
}
|
|
addDropdownToHashtags(node, accountId);
|
|
},
|
|
[showDropdown, accountId],
|
|
);
|
|
|
|
const handleLink = useCallback<OnElementHandler>(
|
|
(element, { key, ...props }) => {
|
|
if (element instanceof HTMLAnchorElement) {
|
|
return (
|
|
<HandledLink
|
|
{...props}
|
|
key={key as string} // React requires keys to not be part of spread props.
|
|
href={element.href}
|
|
text={element.innerText}
|
|
hashtagAccountId={accountId}
|
|
/>
|
|
);
|
|
}
|
|
return undefined;
|
|
},
|
|
[accountId],
|
|
);
|
|
|
|
const note = useAppSelector((state) => {
|
|
const account = state.accounts.get(accountId);
|
|
if (!account) {
|
|
return '';
|
|
}
|
|
return isModernEmojiEnabled() ? account.note : account.note_emojified;
|
|
});
|
|
const extraEmojis = useAppSelector((state) => {
|
|
const account = state.accounts.get(accountId);
|
|
return account?.emojis;
|
|
});
|
|
|
|
if (note.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<EmojiHTML
|
|
htmlString={note}
|
|
extraEmojis={extraEmojis}
|
|
className={classNames(className, 'translate')}
|
|
onClickCapture={isModernEmojiEnabled() ? undefined : handleClick}
|
|
ref={handleNodeChange}
|
|
onElement={handleLink}
|
|
/>
|
|
);
|
|
};
|
|
|
|
function addDropdownToHashtags(node: HTMLElement | null, accountId: string) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
for (const childNode of node.childNodes) {
|
|
if (!(childNode instanceof HTMLElement)) {
|
|
continue;
|
|
}
|
|
if (
|
|
childNode instanceof HTMLAnchorElement &&
|
|
(childNode.classList.contains('hashtag') ||
|
|
childNode.innerText.startsWith('#')) &&
|
|
!childNode.dataset.menuHashtag
|
|
) {
|
|
childNode.dataset.menuHashtag = accountId;
|
|
} else if (childNode.childNodes.length > 0) {
|
|
addDropdownToHashtags(childNode, accountId);
|
|
}
|
|
}
|
|
}
|