mastodon/app/javascript/glitch/components/account/header.js

232 lines
6.3 KiB
JavaScript
Raw Normal View History

2017-07-14 20:13:02 +02:00
/*
`<AccountHeader>`
=================
> For more information on the contents of this file, please contact:
>
> - kibigo! [@kibi@glitch.social]
Original file by @gargron@mastodon.social et al as part of
tootsuite/mastodon. We've expanded it in order to handle user bio
frontmatter.
The `<AccountHeader>` component provides the header for account
timelines. It is a fairly simple component which mostly just consists
of a `render()` method.
__Props:__
- __`account` (`ImmutablePropTypes.map`) :__
The account to render a header for.
- __`me` (`PropTypes.number.isRequired`) :__
The id of the currently-signed-in account.
- __`onFollow` (`PropTypes.func.isRequired`) :__
The function to call when the user clicks the "follow" button.
- __`intl` (`PropTypes.object.isRequired`) :__
Our internationalization object, inserted by `@injectIntl`.
*/
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/*
Imports:
--------
*/
// Package imports //
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
// Mastodon imports //
import emojify from '../../../mastodon/emoji';
import IconButton from '../../../mastodon/components/icon_button';
import Avatar from '../../../mastodon/components/avatar';
// Our imports //
import { processBio } from '../../util/bio_metadata';
2017-07-14 20:13:02 +02:00
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/*
Inital setup:
-------------
The `messages` constant is used to define any messages that we need
from inside props. In our case, these are the `unfollow`, `follow`, and
`requested` messages used in the `title` of our buttons.
*/
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
});
2017-07-14 20:13:02 +02:00
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/*
Implementation:
---------------
*/
@injectIntl
2017-07-14 20:13:02 +02:00
export default class AccountHeader extends ImmutablePureComponent {
static propTypes = {
2017-07-14 20:13:02 +02:00
account : ImmutablePropTypes.map,
me : PropTypes.string.isRequired,
2017-07-14 20:13:02 +02:00
onFollow : PropTypes.func.isRequired,
intl : PropTypes.object.isRequired,
};
2017-07-14 20:13:02 +02:00
/*
### `render()`
The `render()` function is used to render our component.
*/
render () {
const { account, me, intl } = this.props;
2017-07-14 20:13:02 +02:00
/*
If no `account` is provided, then we can't render a header. Otherwise,
we get the `displayName` for the account, if available. If it's blank,
then we set the `displayName` to just be the `username` of the account.
*/
if (!account) {
return null;
}
let displayName = account.get('display_name_html');
2016-11-15 18:38:57 +01:00
let info = '';
let actionBtn = '';
2017-07-14 20:13:02 +02:00
let following = false;
if (displayName.length === 0) {
displayName = account.get('username');
}
2017-07-14 20:13:02 +02:00
/*
Next, we handle the account relationships. If the account follows the
user, then we add an `info` message. If the user has requested a
follow, then we disable the `actionBtn` and display an hourglass.
Otherwise, if the account isn't blocked, we set the `actionBtn` to the
appropriate icon.
*/
2016-10-09 22:19:15 +02:00
if (me !== account.get('id')) {
2017-07-14 20:13:02 +02:00
if (account.getIn(['relationship', 'followed_by'])) {
info = (
<span className='account--follows-info'>
<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />
</span>
);
}
if (account.getIn(['relationship', 'requested'])) {
actionBtn = (
<div className='account--action-button'>
<IconButton size={26} disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />
</div>
);
} else if (!account.getIn(['relationship', 'blocking'])) {
2017-07-14 20:13:02 +02:00
following = account.getIn(['relationship', 'following']);
actionBtn = (
<div className='account--action-button'>
2017-07-14 20:13:02 +02:00
<IconButton
size={26}
icon={following ? 'user-times' : 'user-plus'}
active={following}
title={intl.formatMessage(following ? messages.unfollow : messages.follow)}
onClick={this.props.onFollow}
/>
</div>
);
}
}
2017-07-14 20:13:02 +02:00
/*
we extract the `text` and
2017-07-14 20:13:02 +02:00
`metadata` from our account's `note` using `processBio()`.
*/
2017-06-21 04:44:43 +02:00
const { text, metadata } = processBio(account.get('note'));
2017-07-14 20:13:02 +02:00
/*
Here, we render our component using all the things we've defined above.
*/
return (
2017-06-21 04:44:43 +02:00
<div className='account__header__wrapper'>
2017-07-14 20:13:02 +02:00
<div
className='account__header'
style={{ backgroundImage: `url(${account.get('header')})` }}
>
2017-06-21 04:44:43 +02:00
<div>
<a href={account.get('url')} target='_blank' rel='noopener'>
2017-07-14 20:13:02 +02:00
<span className='account__header__avatar'>
<Avatar account={account} size={90} />
2017-07-14 20:13:02 +02:00
</span>
<span
className='account__header__display-name'
dangerouslySetInnerHTML={{ __html: displayName }}
2017-07-14 20:13:02 +02:00
/>
</a>
2017-07-14 20:13:02 +02:00
<span className='account__header__username'>
@{account.get('acct')}
{account.get('locked') ? <i className='fa fa-lock' /> : null}
</span>
<div className='account__header__content' dangerouslySetInnerHTML={{ __html: emojify(text) }} />
2016-10-09 22:19:15 +02:00
2017-06-21 04:44:43 +02:00
{info}
{actionBtn}
</div>
</div>
2017-06-21 04:44:43 +02:00
{metadata.length && (
2017-06-27 14:48:26 +02:00
<table className='account__metadata'>
<tbody>
{(() => {
let data = [];
for (let i = 0; i < metadata.length; i++) {
data.push(
<tr key={i}>
<th scope='row'><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][0]) }} /></th>
<td><div dangerouslySetInnerHTML={{ __html: emojify(metadata[i][1]) }} /></td>
</tr>
);
}
return data;
})()}
</tbody>
2017-06-27 14:48:26 +02:00
</table>
2017-06-21 04:44:43 +02:00
) || null}
</div>
);
}
}