about summary refs log tree commit diff
path: root/app/javascript/glitch/components/account/header.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/glitch/components/account/header.js')
-rw-r--r--app/javascript/glitch/components/account/header.js227
1 files changed, 227 insertions, 0 deletions
diff --git a/app/javascript/glitch/components/account/header.js b/app/javascript/glitch/components/account/header.js
new file mode 100644
index 000000000..6359c1775
--- /dev/null
+++ b/app/javascript/glitch/components/account/header.js
@@ -0,0 +1,227 @@
+/*
+
+`<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>
+    );
+  }
+
+}