import PropTypes from "prop-types"; import { defineMessages, injectIntl, FormattedMessage } from "react-intl"; import classNames from "classnames"; import { Helmet } from "react-helmet"; import { NavLink } from "react-router-dom"; import ImmutablePropTypes from "react-immutable-proptypes"; import ImmutablePureComponent from "react-immutable-pure-component"; import { Avatar } from "mastodon/components/avatar"; import { Badge, AutomatedBadge, GroupBadge } from "mastodon/components/badge"; import Button from "mastodon/components/button"; import { FollowersCounter, FollowingCounter, StatusesCounter } from "mastodon/components/counters"; import { Icon } from "mastodon/components/icon"; import { IconButton } from "mastodon/components/icon_button"; import { ShortNumber } from "mastodon/components/short_number"; import DropdownMenuContainer from "mastodon/containers/dropdown_menu_container"; import { autoPlayGif, me, domain } from "mastodon/initial_state"; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from "mastodon/permissions"; import AccountNoteContainer from "../containers/account_note_container"; import FollowRequestNoteContainer from "../containers/follow_request_note_container"; const messages = defineMessages({ unfollow: { id: "account.unfollow", defaultMessage: "Unfollow" }, follow: { id: "account.follow", defaultMessage: "Follow" }, cancel_follow_request: { id: "account.cancel_follow_request", defaultMessage: "Withdraw follow request" }, requested: { id: "account.requested", defaultMessage: "Awaiting approval. Click to cancel follow request" }, unblock: { id: "account.unblock", defaultMessage: "Unblock @{name}" }, edit_profile: { id: "account.edit_profile", defaultMessage: "Edit profile" }, 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." }, mention: { id: "account.mention", defaultMessage: "Mention @{name}" }, direct: { id: "account.direct", defaultMessage: "Privately mention @{name}" }, unmute: { id: "account.unmute", defaultMessage: "Unmute @{name}" }, block: { id: "account.block", defaultMessage: "Block @{name}" }, mute: { id: "account.mute", defaultMessage: "Mute @{name}" }, report: { id: "account.report", defaultMessage: "Report @{name}" }, share: { id: "account.share", defaultMessage: "Share @{name}'s profile" }, media: { id: "account.media", defaultMessage: "Media" }, blockDomain: { id: "account.block_domain", defaultMessage: "Block domain {domain}" }, unblockDomain: { id: "account.unblock_domain", defaultMessage: "Unblock domain {domain}" }, hideReblogs: { id: "account.hide_reblogs", defaultMessage: "Hide boosts from @{name}" }, showReblogs: { id: "account.show_reblogs", defaultMessage: "Show boosts from @{name}" }, enableNotifications: { id: "account.enable_notifications", defaultMessage: "Notify me when @{name} posts" }, disableNotifications: { id: "account.disable_notifications", defaultMessage: "Stop notifying me when @{name} posts" }, pins: { id: "navigation_bar.pins", defaultMessage: "Pinned posts" }, preferences: { id: "navigation_bar.preferences", defaultMessage: "Preferences" }, follow_requests: { id: "navigation_bar.follow_requests", defaultMessage: "Follow requests" }, favourites: { id: "navigation_bar.favourites", defaultMessage: "Favorites" }, lists: { id: "navigation_bar.lists", defaultMessage: "Lists" }, followed_tags: { id: "navigation_bar.followed_tags", defaultMessage: "Followed hashtags" }, blocks: { id: "navigation_bar.blocks", defaultMessage: "Blocked users" }, domain_blocks: { id: "navigation_bar.domain_blocks", defaultMessage: "Blocked domains" }, mutes: { id: "navigation_bar.mutes", defaultMessage: "Muted users" }, endorse: { id: "account.endorse", defaultMessage: "Feature on profile" }, unendorse: { id: "account.unendorse", defaultMessage: "Don't feature on profile" }, add_or_remove_from_list: { id: "account.add_or_remove_from_list", defaultMessage: "Add or Remove from lists" }, admin_account: { id: "status.admin_account", defaultMessage: "Open moderation interface for @{name}" }, admin_domain: { id: "status.admin_domain", defaultMessage: "Open moderation interface for {domain}" }, languages: { id: "account.languages", defaultMessage: "Change subscribed languages" }, openOriginalPage: { id: "account.open_original_page", defaultMessage: "Open original page" }, }); const titleFromAccount = account => { const displayName = account.get("display_name"); const acct = account.get("acct") === account.get("username") ? `${account.get("username")}@${domain}` : account.get("acct"); const prefix = displayName.trim().length === 0 ? account.get("username") : displayName; return `${prefix} (@${acct})`; }; const dateFormatOptions = { month: "short", day: "numeric", year: "numeric", hour12: false, hour: "2-digit", minute: "2-digit", }; class Header extends ImmutablePureComponent { static contextTypes = { identity: PropTypes.object, router: PropTypes.object, }; static propTypes = { account: ImmutablePropTypes.map, identity_props: ImmutablePropTypes.list, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onDirect: PropTypes.func.isRequired, onReblogToggle: PropTypes.func.isRequired, onNotifyToggle: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, onBlockDomain: PropTypes.func.isRequired, onUnblockDomain: PropTypes.func.isRequired, onEndorseToggle: PropTypes.func.isRequired, onAddToList: PropTypes.func.isRequired, onEditAccountNote: PropTypes.func.isRequired, onChangeLanguages: PropTypes.func.isRequired, onInteractionModal: PropTypes.func.isRequired, onOpenAvatar: PropTypes.func.isRequired, onOpenURL: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, domain: PropTypes.string.isRequired, hidden: PropTypes.bool, }; setRef = c => { this.node = c; }; openEditProfile = () => { window.open("/settings/profile", "_blank"); }; isStatusesPageActive = (match, location) => { if (!match) { return false; } return !location.pathname.match(/\/(followers|following)\/?$/); }; handleMouseEnter = ({ currentTarget }) => { if (autoPlayGif) { return; } const emojis = currentTarget.querySelectorAll(".custom-emoji"); for (var i = 0; i < emojis.length; i++) { let emoji = emojis[i]; emoji.src = emoji.getAttribute("data-original"); } }; handleMouseLeave = ({ currentTarget }) => { if (autoPlayGif) { return; } const emojis = currentTarget.querySelectorAll(".custom-emoji"); for (var i = 0; i < emojis.length; i++) { let emoji = emojis[i]; emoji.src = emoji.getAttribute("data-static"); } }; handleAvatarClick = e => { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); this.props.onOpenAvatar(); } }; handleShare = () => { const { account } = this.props; navigator.share({ url: account.get("url"), }).catch((e) => { if (e.name !== "AbortError") { console.error(e); } }); }; handleHashtagClick = e => { const { router } = this.context; const value = e.currentTarget.textContent.replace(/^#/, ""); if (router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); router.history.push(`/tags/${value}`); } }; handleMentionClick = e => { const { router } = this.context; const { onOpenURL } = this.props; if (router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { e.preventDefault(); const link = e.currentTarget; onOpenURL(link.href, router.history, () => { window.location = link.href; }); } }; _attachLinkEvents () { const node = this.node; if (!node) { return; } const links = node.querySelectorAll("a"); let link; for (var i = 0; i < links.length; ++i) { link = links[i]; if (link.textContent[0] === "#" || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === "#")) { link.addEventListener("click", this.handleHashtagClick, false); } else if (link.classList.contains("mention")) { link.addEventListener("click", this.handleMentionClick, false); } } } componentDidMount () { this._attachLinkEvents(); } componentDidUpdate () { this._attachLinkEvents(); } render () { const { account, hidden, intl, domain } = this.props; const { signedIn, permissions } = this.context.identity; if (!account) { return null; } const suspended = account.get("suspended"); const isRemote = account.get("acct") !== account.get("username"); const remoteDomain = isRemote ? account.get("acct").split("@")[1] : null; let info = []; let actionBtn = ""; let bellBtn = ""; let lockedIcon = ""; let menu = []; if (me !== account.get("id") && account.getIn(["relationship", "followed_by"])) { info.push(); } else if (me !== account.get("id") && account.getIn(["relationship", "blocking"])) { info.push(); } if (me !== account.get("id") && account.getIn(["relationship", "muting"])) { info.push(); } else if (me !== account.get("id") && account.getIn(["relationship", "domain_blocking"])) { info.push(); } if (account.getIn(["relationship", "requested"]) || account.getIn(["relationship", "following"])) { bellBtn = ; } if (me !== account.get("id")) { if (signedIn && !account.get("relationship")) { // Wait until the relationship is loaded actionBtn = ""; } else if (account.getIn(["relationship", "requested"])) { actionBtn =