227 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 
 | |
| `<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/features/emoji/emoji';
 | |
| import IconButton from '../../../mastodon/components/icon_button';
 | |
| import Avatar from '../../../mastodon/components/avatar';
 | |
| 
 | |
| //  Our imports  //
 | |
| import { processBio } from '../../util/bio_metadata';
 | |
| 
 | |
| //  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 | |
| 
 | |
| /*
 | |
| 
 | |
| 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' },
 | |
| });
 | |
| 
 | |
| //  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 | |
| 
 | |
| /*
 | |
| 
 | |
| Implementation:
 | |
| ---------------
 | |
| 
 | |
| */
 | |
| 
 | |
| @injectIntl
 | |
| export default class AccountHeader extends ImmutablePureComponent {
 | |
| 
 | |
|   static propTypes = {
 | |
|     account  : ImmutablePropTypes.map,
 | |
|     me       : PropTypes.string.isRequired,
 | |
|     onFollow : PropTypes.func.isRequired,
 | |
|     intl     : PropTypes.object.isRequired,
 | |
|   };
 | |
| 
 | |
| /*
 | |
| 
 | |
| ###  `render()`
 | |
| 
 | |
| The `render()` function is used to render our component.
 | |
| 
 | |
| */
 | |
| 
 | |
|   render () {
 | |
|     const { account, me, intl } = this.props;
 | |
| 
 | |
| /*
 | |
| 
 | |
| 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');
 | |
|     let info        = '';
 | |
|     let actionBtn   = '';
 | |
|     let following   = false;
 | |
| 
 | |
| /*
 | |
| 
 | |
| 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.
 | |
| 
 | |
| */
 | |
| 
 | |
|     if (me !== account.get('id')) {
 | |
|       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'])) {
 | |
|         following = account.getIn(['relationship', 'following']);
 | |
|         actionBtn = (
 | |
|           <div className='account--action-button'>
 | |
|             <IconButton
 | |
|               size={26}
 | |
|               icon={following ? 'user-times' : 'user-plus'}
 | |
|               active={following}
 | |
|               title={intl.formatMessage(following ? messages.unfollow : messages.follow)}
 | |
|               onClick={this.props.onFollow}
 | |
|             />
 | |
|           </div>
 | |
|         );
 | |
|       }
 | |
|     }
 | |
| 
 | |
| /*
 | |
|  we extract the `text` and
 | |
| `metadata` from our account's `note` using `processBio()`.
 | |
| 
 | |
| */
 | |
| 
 | |
|     const { text, metadata } = processBio(account.get('note'));
 | |
| 
 | |
| /*
 | |
| 
 | |
| Here, we render our component using all the things we've defined above.
 | |
| 
 | |
| */
 | |
| 
 | |
|     return (
 | |
|       <div className='account__header__wrapper'>
 | |
|         <div
 | |
|           className='account__header'
 | |
|           style={{ backgroundImage: `url(${account.get('header')})` }}
 | |
|         >
 | |
|           <div>
 | |
|             <a href={account.get('url')} target='_blank' rel='noopener'>
 | |
|               <span className='account__header__avatar'>
 | |
|                 <Avatar account={account} size={90} />
 | |
|               </span>
 | |
|               <span
 | |
|                 className='account__header__display-name'
 | |
|                 dangerouslySetInnerHTML={{ __html: displayName }}
 | |
|               />
 | |
|             </a>
 | |
|             <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) }} />
 | |
| 
 | |
|             {info}
 | |
|             {actionBtn}
 | |
|           </div>
 | |
|         </div>
 | |
| 
 | |
|         {metadata.length && (
 | |
|           <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>
 | |
|           </table>
 | |
|         ) || null}
 | |
|       </div>
 | |
|     );
 | |
|   }
 | |
| 
 | |
| }
 |