'use strict'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import { usePatronUser } from 'soapbox/api/hooks'; import Badge from 'soapbox/components/badge'; import Markup from 'soapbox/components/markup'; import { dateFormatOptions } from 'soapbox/components/relative-timestamp'; import { Icon, HStack, Stack, Text } from 'soapbox/components/ui'; import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; import { isLocal } from 'soapbox/utils/accounts'; import { badgeToTag, getBadges as getAccountBadges } from 'soapbox/utils/badges'; import { capitalize } from 'soapbox/utils/strings'; import ProfileFamiliarFollowers from './profile-familiar-followers'; import ProfileField from './profile-field'; import ProfileStats from './profile-stats'; import type { Account } from 'soapbox/types/entities'; /** Basically ensure the URL isn't `javascript:alert('hi')` or something like that */ const isSafeUrl = (text: string): boolean => { try { const url = new URL(text); return ['http:', 'https:'].includes(url.protocol); } catch (e) { return false; } }; const messages = defineMessages({ linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' }, deactivated: { id: 'account.deactivated', defaultMessage: 'Deactivated' }, bot: { id: 'account.badges.bot', defaultMessage: 'Bot' }, }); interface IProfileInfoPanel { account?: Account /** Username from URL params, in case the account isn't found. */ username: string } /** User profile metadata, such as location, birthday, etc. */ const ProfileInfoPanel: React.FC = ({ account, username }) => { const intl = useIntl(); const { displayFqn } = useSoapboxConfig(); const { patronUser } = usePatronUser(account?.url); const me = useAppSelector(state => state.me); const ownAccount = account?.id === me; const getStaffBadge = (): React.ReactNode => { if (account?.admin) { return } key='staff' />; } else if (account?.moderator) { return } key='staff' />; } else { return null; } }; const getCustomBadges = (): React.ReactNode[] => { const badges = account ? getAccountBadges(account) : []; return badges.map(badge => ( )); }; const getBadges = (): React.ReactNode[] => { const custom = getCustomBadges(); const staffBadge = getStaffBadge(); const isPatron = patronUser?.is_patron === true; const badges = []; if (staffBadge) { badges.push(staffBadge); } if (isPatron) { badges.push(} key='patron' />); } return [...badges, ...custom]; }; const renderBirthday = (): React.ReactNode => { const birthday = account?.pleroma?.birthday; if (!birthday) return null; const formattedBirthday = intl.formatDate(birthday, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' }); const date = new Date(birthday); const today = new Date(); const hasBirthday = date.getDate() === today.getDate() && date.getMonth() === today.getMonth(); return ( {hasBirthday ? ( ) : ( )} ); }; if (!account) { return (
@{username}
); } const deactivated = account.pleroma?.deactivated ?? false; const displayNameHtml = deactivated ? { __html: intl.formatMessage(messages.deactivated) } : { __html: account.display_name_html }; const memberSinceDate = intl.formatDate(account.created_at, { month: 'long', year: 'numeric' }); const badges = getBadges(); return (
{account.bot && } {badges.length > 0 && ( {badges} )} @{displayFqn ? account.fqn : account.acct} {account.locked && ( )} {account.note.length > 0 && ( )}
{isLocal(account) ? ( ) : null} {account.location ? ( {account.location} ) : null} {account.website ? (
{isSafeUrl(account.website) ? ( {account.website} ) : ( account.website )}
) : null} {renderBirthday()}
{ownAccount ? null : }
{account.fields.length > 0 && ( {account.fields.map((field, i) => ( ))} )}
); }; export default ProfileInfoPanel;