about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch')
-rw-r--r--app/javascript/flavours/glitch/actions/identity_proofs.js30
-rw-r--r--app/javascript/flavours/glitch/actions/search.js1
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js2
-rw-r--r--app/javascript/flavours/glitch/components/status_content.js6
-rw-r--r--app/javascript/flavours/glitch/containers/status_container.js9
-rw-r--r--app/javascript/flavours/glitch/features/account/components/action_bar.js109
-rw-r--r--app/javascript/flavours/glitch/features/account/components/header.js257
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/header.js15
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js9
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js3
-rw-r--r--app/javascript/flavours/glitch/features/drawer/results/index.js8
-rw-r--r--app/javascript/flavours/glitch/features/emoji_picker/index.js51
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js2
-rw-r--r--app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js9
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js9
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js12
-rw-r--r--app/javascript/flavours/glitch/reducers/identity_proofs.js25
-rw-r--r--app/javascript/flavours/glitch/reducers/index.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/timelines.js9
-rw-r--r--app/javascript/flavours/glitch/styles/_mixins.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/admin.scss5
-rw-r--r--app/javascript/flavours/glitch/styles/components/accounts.scss351
-rw-r--r--app/javascript/flavours/glitch/styles/components/drawer.scss46
-rw-r--r--app/javascript/flavours/glitch/styles/components/emoji.scss4
-rw-r--r--app/javascript/flavours/glitch/styles/components/emoji_picker.scss25
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss14
-rw-r--r--app/javascript/flavours/glitch/styles/components/metadata.scss45
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/containers.scss9
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss45
-rw-r--r--app/javascript/flavours/glitch/styles/metadata.scss56
-rw-r--r--app/javascript/flavours/glitch/styles/stream_entries.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/widgets.scss1
-rw-r--r--app/javascript/flavours/glitch/util/api.js8
-rw-r--r--app/javascript/flavours/glitch/util/emoji/emoji_picker.js4
36 files changed, 630 insertions, 555 deletions
diff --git a/app/javascript/flavours/glitch/actions/identity_proofs.js b/app/javascript/flavours/glitch/actions/identity_proofs.js
new file mode 100644
index 000000000..a7241da20
--- /dev/null
+++ b/app/javascript/flavours/glitch/actions/identity_proofs.js
@@ -0,0 +1,30 @@
+import api from 'flavours/glitch/util/api';
+
+export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST';
+export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS';
+export const IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL    = 'IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL';
+
+export const fetchAccountIdentityProofs = accountId => (dispatch, getState) => {
+  dispatch(fetchAccountIdentityProofsRequest(accountId));
+
+  api(getState).get(`/api/v1/accounts/${accountId}/identity_proofs`)
+    .then(({ data }) => dispatch(fetchAccountIdentityProofsSuccess(accountId, data)))
+    .catch(err => dispatch(fetchAccountIdentityProofsFail(accountId, err)));
+};
+
+export const fetchAccountIdentityProofsRequest = id => ({
+  type: IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
+  id,
+});
+
+export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) => ({
+  type: IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
+  accountId,
+  identity_proofs,
+});
+
+export const fetchAccountIdentityProofsFail = (accountId, err) => ({
+  type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
+  accountId,
+  err,
+});
diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js
index bc094eed5..b2d24e10b 100644
--- a/app/javascript/flavours/glitch/actions/search.js
+++ b/app/javascript/flavours/glitch/actions/search.js
@@ -37,6 +37,7 @@ export function submitSearch() {
       params: {
         q: value,
         resolve: true,
+        limit: 10,
       },
     }).then(response => {
       if (response.data.accounts) {
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index 1d3130604..e0cc652d2 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -146,7 +146,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
   }
 
   handleBlockClick = () => {
-    this.props.onBlock(this.props.status.get('account'));
+    this.props.onBlock(this.props.status);
   }
 
   handleOpen = () => {
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js
index 98a34ebaf..a62844185 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.js
@@ -198,7 +198,7 @@ export default class StatusContent extends React.PureComponent {
           <p
             style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
           >
-            <span dangerouslySetInnerHTML={spoilerContent} />
+            <span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
             {' '}
             <button tabIndex='0' className='status__content__spoiler-link' onClick={this.handleSpoilerClick}>
               {toggleText}
@@ -213,6 +213,7 @@ export default class StatusContent extends React.PureComponent {
               style={directionStyle}
               tabIndex={!hidden ? 0 : null}
               dangerouslySetInnerHTML={content}
+              lang={status.get('language')}
             />
             {media}
           </div>
@@ -231,6 +232,7 @@ export default class StatusContent extends React.PureComponent {
           <div
             ref={this.setRef}
             dangerouslySetInnerHTML={content}
+            lang={status.get('language')}
             tabIndex='0'
           />
           {media}
@@ -243,7 +245,7 @@ export default class StatusContent extends React.PureComponent {
           style={directionStyle}
           tabIndex='0'
         >
-          <div ref={this.setRef} dangerouslySetInnerHTML={content} tabIndex='0' />
+          <div ref={this.setRef} dangerouslySetInnerHTML={content} lang={status.get('language')} tabIndex='0' />
           {media}
         </div>
       );
diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js
index f783878b0..60636feb4 100644
--- a/app/javascript/flavours/glitch/containers/status_container.js
+++ b/app/javascript/flavours/glitch/containers/status_container.js
@@ -35,6 +35,7 @@ const messages = defineMessages({
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -169,11 +170,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(openModal('VIDEO', { media, time }));
   },
 
-  onBlock (account) {
+  onBlock (status) {
+    const account = status.get('account');
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   },
 
diff --git a/app/javascript/flavours/glitch/features/account/components/action_bar.js b/app/javascript/flavours/glitch/features/account/components/action_bar.js
index fdacb7298..a2c00c1c2 100644
--- a/app/javascript/flavours/glitch/features/account/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/account/components/action_bar.js
@@ -3,58 +3,19 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 import { NavLink } from 'react-router-dom';
-import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
+import { injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
 import { me, isStaff } from 'flavours/glitch/util/initial_state';
 import { profileLink, accountAdminLink } from 'flavours/glitch/util/backend_links';
-
-const messages = defineMessages({
-  mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
-  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
-  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
-  unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
-  unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
-  block: { id: 'account.block', defaultMessage: 'Block @{name}' },
-  mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
-  follow: { id: 'account.follow', defaultMessage: 'Follow' },
-  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: 'Hide everything from {domain}' },
-  unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
-  hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
-  showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
-  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}' },
-});
+import Icon from 'flavours/glitch/components/icon';
 
 @injectIntl
 export default class ActionBar extends React.PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
-    onFollow: PropTypes.func,
-    onBlock: PropTypes.func.isRequired,
-    onMention: PropTypes.func.isRequired,
-    onDirect: PropTypes.func.isRequired,
-    onReblogToggle: 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,
     intl: PropTypes.object.isRequired,
   };
 
-  handleShare = () => {
-    navigator.share({
-      url: this.props.account.get('url'),
-    });
-  }
-
   isStatusesPageActive = (match, location) => {
     if (!match) {
       return false;
@@ -65,56 +26,12 @@ export default class ActionBar extends React.PureComponent {
   render () {
     const { account, intl } = this.props;
 
-    let menu = [];
     let extraInfo = '';
 
-    menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
-    menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
-
-    if ('share' in navigator) {
-      menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
-    }
-
-    menu.push(null);
-
-    if (account.get('id') === me) {
-      if (profileLink !== undefined) {
-        menu.push({ text: intl.formatMessage(messages.edit_profile), href: profileLink });
-      }
-    } else {
-      if (account.getIn(['relationship', 'following'])) {
-        if (account.getIn(['relationship', 'showing_reblogs'])) {
-          menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        } else {
-          menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
-        }
-
-        menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
-        menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
-        menu.push(null);
-      }
-
-      if (account.getIn(['relationship', 'muting'])) {
-        menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
-      }
-
-      if (account.getIn(['relationship', 'blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
-      }
-
-      menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
-    }
-
     if (account.get('acct') !== account.get('username')) {
-      const domain = account.get('acct').split('@')[1];
-
       extraInfo = (
         <div className='account__disclaimer'>
-          <FormattedMessage
+          <Icon icon='info-circle' fixedWidth /> <FormattedMessage
             id='account.disclaimer_full'
             defaultMessage="Information below may reflect the user's profile incompletely."
           />
@@ -124,22 +41,6 @@ export default class ActionBar extends React.PureComponent {
           </a>
         </div>
       );
-
-      menu.push(null);
-
-      if (account.getIn(['relationship', 'domain_blocking'])) {
-        menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
-      } else {
-        menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
-      }
-    }
-
-    if (account.get('id') !== me && isStaff && (accountAdminLink !== undefined)) {
-      menu.push(null);
-      menu.push({
-        text: intl.formatMessage(messages.admin_account, { name: account.get('username') }),
-        href: accountAdminLink(account.get('id')),
-      });
     }
 
     return (
@@ -147,10 +48,6 @@ export default class ActionBar extends React.PureComponent {
         {extraInfo}
 
         <div className='account__action-bar'>
-          <div className='account__action-bar-dropdown'>
-            <DropdownMenuContainer items={menu} icon='bars' size={24} direction='right' />
-          </div>
-
           <div className='account__action-bar-links'>
             <NavLink isActive={this.isStatusesPageActive} activeClassName='active' className='account__action-bar__tab' to={`/accounts/${account.get('id')}`}>
               <FormattedMessage id='account.posts' defaultMessage='Posts' />
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js
index 96696c2a5..13f7741c8 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.js
@@ -3,12 +3,14 @@ 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';
-
-import Avatar from 'flavours/glitch/components/avatar';
-import IconButton from 'flavours/glitch/components/icon_button';
-
-import { autoPlayGif, me } from 'flavours/glitch/util/initial_state';
+import { autoPlayGif, me, isStaff } from 'flavours/glitch/util/initial_state';
 import classNames from 'classnames';
+import Icon from 'flavours/glitch/components/icon';
+import Avatar from 'flavours/glitch/components/avatar';
+import Button from 'flavours/glitch/components/button';
+import { shortNumberFormat } from 'flavours/glitch/util/numbers';
+import { NavLink } from 'react-router-dom';
+import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 
 const messages = defineMessages({
   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@@ -16,7 +18,34 @@ const messages = defineMessages({
   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' },
-  link_verified_on: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
+  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: 'Direct message @{name}' },
+  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+  unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{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: 'Hide everything from {domain}' },
+  unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
+  hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
+  showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
+  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
+  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
+  follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
+  favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
+  lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
+  domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden 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}' },
 });
 
 const dateFormatOptions = {
@@ -28,14 +57,16 @@ const dateFormatOptions = {
   minute: '2-digit',
 };
 
-@injectIntl
-export default class Header extends ImmutablePureComponent {
+export default @injectIntl
+class Header extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map,
+    identity_props: ImmutablePropTypes.list,
     onFollow: PropTypes.func.isRequired,
     onBlock: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
+    domain: PropTypes.string.isRequired,
   };
 
   openEditProfile = () => {
@@ -43,109 +74,191 @@ export default class Header extends ImmutablePureComponent {
   }
 
   render () {
-    const { account, intl } = this.props;
+    const { account, intl, domain, identity_proofs } = this.props;
 
     if (!account) {
       return null;
     }
 
-    let displayName = account.get('display_name_html');
-    let fields      = account.get('fields');
-    let badge       = account.get('bot') ? (<div className='roles'><div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div></div>) : null;
-
-    let info        = '';
-    let mutingInfo  = '';
+    let info        = [];
     let actionBtn   = '';
+    let lockedIcon  = '';
+    let menu        = [];
 
     if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
-      info = <span className='account--follows-info'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>;
+      info.push(<span className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
     }
     else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
-      info = <span className='account--follows-info'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>;
+      info.push(<span className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
     }
 
     if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
-      mutingInfo = <span className='account--muting-info'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>;
+      info.push(<span className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
     } else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
-      mutingInfo = <span className='account--muting-info'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>;
+      info.push(<span className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain hidden' /></span>);
     }
 
     if (me !== account.get('id')) {
       if (!account.get('relationship')) { // Wait until the relationship is loaded
         actionBtn = '';
       } else if (account.getIn(['relationship', 'requested'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} active icon='hourglass' title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />
-          </div>
-        );
+        actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
       } else if (!account.getIn(['relationship', 'blocking'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
-          </div>
-        );
+        actionBtn = <Button className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
       } else if (account.getIn(['relationship', 'blocking'])) {
-        actionBtn = (
-          <div className='account--action-button'>
-            <IconButton size={26} icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />
-          </div>
-        );
+        actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
       }
     } else {
-      actionBtn = (
-        <div className='account--action-button'>
-          <IconButton size={26} icon='pencil' title={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />
-        </div>
-      );
+      actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />;
     }
 
     if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
       actionBtn = '';
     }
 
-    const content = { __html: account.get('note_emojified') };
+    if (account.get('locked')) {
+      lockedIcon = <Icon icon='lock' title={intl.formatMessage(messages.account_locked)} />;
+    }
 
-    return (
-      <div className='account__header__wrapper'>
-        <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${autoPlayGif ? account.get('header') : account.get('header_static')})` }}>
-          <div>
-            <a
-              href={account.get('url')}
-              className='account__header__avatar'
-              role='presentation'
-              target='_blank'
-              rel='noopener'
-            >
-              <Avatar account={account} size={90} />
-            </a>
+    if (account.get('id') !== me) {
+      menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
+      menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
+      menu.push(null);
+    }
+
+    if ('share' in navigator) {
+      menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
+      menu.push(null);
+    }
+
+    if (account.get('id') === me) {
+      menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
+      menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
+      menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
+      menu.push(null);
+      menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
+      menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
+      menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+      menu.push(null);
+      menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
+      menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
+      menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
+    } else {
+      if (account.getIn(['relationship', 'following'])) {
+        if (account.getIn(['relationship', 'showing_reblogs'])) {
+          menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
+        } else {
+          menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
+        }
+
+        menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
+        menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
+        menu.push(null);
+      }
+
+      if (account.getIn(['relationship', 'muting'])) {
+        menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
+      } else {
+        menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
+      }
+
+      if (account.getIn(['relationship', 'blocking'])) {
+        menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
+      } else {
+        menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
+      }
+
+      menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
+    }
+
+    if (account.get('acct') !== account.get('username')) {
+      const domain = account.get('acct').split('@')[1];
 
-            <span className='account__header__display-name' dangerouslySetInnerHTML={{ __html: displayName }} />
-            <span className='account__header__username'>@{account.get('acct')} {account.get('locked') ? <i className='fa fa-lock' /> : null}</span>
+      menu.push(null);
 
-            {badge}
+      if (account.getIn(['relationship', 'domain_blocking'])) {
+        menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
+      } else {
+        menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
+      }
+    }
 
-            <div className='account__header__content' dangerouslySetInnerHTML={content} />
+    if (account.get('id') !== me && isStaff) {
+      menu.push(null);
+      menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
+    }
 
-            {fields.size > 0 && (
-              <div className='account__header__fields'>
-                {fields.map((pair, i) => (
-                  <dl key={i}>
-                    <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
-                    <dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
-                      {pair.get('verified_at') && <span title={intl.formatMessage(messages.link_verified_on, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><i className='fa fa-check verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
-                    </dd>
-                 </dl>
-                ))}
-              </div>
-            )}
+    const content          = { __html: account.get('note_emojified') };
+    const displayNameHtml = { __html: account.get('display_name_html') };
+    const fields          = account.get('fields');
+    const badge           = account.get('bot') ? (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div>) : null;
+    const acct            = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
 
+    return (
+      <div className={classNames('account__header', { inactive: !!account.get('moved') })}>
+        <div className='account__header__image'>
+          <div className='account__header__info'>
             {info}
-            {mutingInfo}
-            {actionBtn}
           </div>
+
+          <img src={autoPlayGif ? account.get('header') : account.get('header_static')} alt='' className='parallax' />
         </div>
-      </div>
+
+        <div className='account__header__bar'>
+          <div className='account__header__tabs'>
+            <a className='avatar' href={account.get('url')} rel='noopener' target='_blank'>
+              <Avatar account={account} size={90} />
+            </a>
+
+            <div className='spacer' />
+
+            <div className='account__header__tabs__buttons'>
+              {actionBtn}
+
+              <DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
+            </div>
+          </div>
+
+          <div className='account__header__tabs__name'>
+            <h1>
+              <span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
+              <small>@{acct} {lockedIcon}</small>
+            </h1>
+          </div>
+
+          <div className='account__header__extra'>
+            <div className='account__header__bio'>
+              { (fields.size > 0 || identity_proofs.size > 0) && (
+                <div className='account__header__fields'>
+                  {identity_proofs.map((proof, i) => (
+                    <dl key={i}>
+                      <dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
+
+                      <dd className='verified'>
+                        <a href={proof.get('proof_url')} target='_blank' rel='noopener'><span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
+                          <Icon id='check' className='verified__mark' />
+                        </span></a>
+                        <a href={proof.get('profile_url')} target='_blank' rel='noopener'><span dangerouslySetInnerHTML={{ __html: ' '+proof.get('provider_username') }} /></a>
+                      </dd>
+                    </dl>
+                  ))}
+                  {fields.map((pair, i) => (
+                    <dl key={i}>
+                      <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
+ 
+                      <dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
+                        {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
+                      </dd>
+                    </dl>
+                  ))}
+                </div>
+              )}
+
+              {account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content' dangerouslySetInnerHTML={content} />}
+            </div>
+         </div>
+       </div>
+     </div>
     );
   }
 
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/header.js b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
index 8dc0be93e..96cabe847 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
@@ -13,6 +13,7 @@ export default class Header extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map,
+    identity_proofs: ImmutablePropTypes.list,
     onFollow: PropTypes.func.isRequired,
     onBlock: PropTypes.func.isRequired,
     onMention: PropTypes.func.isRequired,
@@ -25,6 +26,7 @@ export default class Header extends ImmutablePureComponent {
     onEndorseToggle: PropTypes.func.isRequired,
     onAddToList: PropTypes.func.isRequired,
     hideTabs: PropTypes.bool,
+    domain: PropTypes.string.isRequired,
   };
 
   static contextTypes = {
@@ -84,7 +86,7 @@ export default class Header extends ImmutablePureComponent {
   }
 
   render () {
-    const { account, hideTabs } = this.props;
+    const { account, hideTabs, identity_proofs } = this.props;
 
     if (account === null) {
       return <MissingIndicator />;
@@ -96,13 +98,9 @@ export default class Header extends ImmutablePureComponent {
 
         <InnerHeader
           account={account}
+          identity_proofs={identity_proofs}
           onFollow={this.handleFollow}
           onBlock={this.handleBlock}
-        />
-
-        <ActionBar
-          account={account}
-          onBlock={this.handleBlock}
           onMention={this.handleMention}
           onDirect={this.handleDirect}
           onReblogToggle={this.handleReblogToggle}
@@ -112,6 +110,11 @@ export default class Header extends ImmutablePureComponent {
           onUnblockDomain={this.handleUnblockDomain}
           onEndorseToggle={this.handleEndorseToggle}
           onAddToList={this.handleAddToList}
+          domain={this.props.domain}
+        />
+
+        <ActionBar
+          account={account}
         />
 
         {!hideTabs && (
diff --git a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
index e333c31a1..787a36658 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
@@ -21,11 +21,13 @@ import { openModal } from 'flavours/glitch/actions/modal';
 import { blockDomain, unblockDomain } from 'flavours/glitch/actions/domain_blocks';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { unfollowModal } from 'flavours/glitch/util/initial_state';
+import { List as ImmutableList } from 'immutable';
 
 const messages = defineMessages({
   unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -33,6 +35,8 @@ const makeMapStateToProps = () => {
 
   const mapStateToProps = (state, { accountId }) => ({
     account: getAccount(state, accountId),
+    domain: state.getIn(['meta', 'domain']),
+    identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()),
   });
 
   return mapStateToProps;
@@ -64,6 +68,11 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
         message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
         confirm: intl.formatMessage(messages.blockConfirm),
         onConfirm: () => dispatch(blockAccount(account.get('id'))),
+        secondary: intl.formatMessage(messages.blockAndReport),
+        onSecondary: () => {
+          dispatch(blockAccount(account.get('id')));
+          dispatch(initReport(account));
+        },
       }));
     }
   },
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index d683b8789..9971c0f4a 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -12,6 +12,7 @@ import HeaderContainer from './containers/header_container';
 import { List as ImmutableList } from 'immutable';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { FormattedMessage } from 'react-intl';
+import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
 
 const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
   const path = withReplies ? `${accountId}:with_replies` : accountId;
@@ -41,6 +42,7 @@ export default class AccountTimeline extends ImmutablePureComponent {
     const { params: { accountId }, withReplies } = this.props;
 
     this.props.dispatch(fetchAccount(accountId));
+    this.props.dispatch(fetchAccountIdentityProofs(accountId));
     if (!withReplies) {
       this.props.dispatch(expandAccountFeaturedTimeline(accountId));
     }
@@ -50,6 +52,7 @@ export default class AccountTimeline extends ImmutablePureComponent {
   componentWillReceiveProps (nextProps) {
     if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
       this.props.dispatch(fetchAccount(nextProps.params.accountId));
+      this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
       if (!nextProps.withReplies) {
         this.props.dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
       }
diff --git a/app/javascript/flavours/glitch/features/drawer/results/index.js b/app/javascript/flavours/glitch/features/drawer/results/index.js
index ac7a14ef4..4574c0e1e 100644
--- a/app/javascript/flavours/glitch/features/drawer/results/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/results/index.js
@@ -10,6 +10,7 @@ import spring from 'react-motion/lib/spring';
 import { Link } from 'react-router-dom';
 
 //  Components.
+import Icon from 'flavours/glitch/components/icon';
 import AccountContainer from 'flavours/glitch/containers/account_container';
 import StatusContainer from 'flavours/glitch/containers/status_container';
 import Hashtag from 'flavours/glitch/components/hashtag';
@@ -62,6 +63,7 @@ export default function DrawerResults ({
           }}
         >
           <header>
+            <Icon icon='search' fixedWidth />
             <FormattedMessage
               {...messages.total}
               values={{ count }}
@@ -69,7 +71,7 @@ export default function DrawerResults ({
           </header>
           {accounts && accounts.size ? (
             <section>
-              <h5><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
+              <h5><Icon icon='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
 
               {accounts.map(
                 accountId => (
@@ -83,7 +85,7 @@ export default function DrawerResults ({
           ) : null}
           {statuses && statuses.size ? (
             <section>
-              <h5><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
+              <h5><Icon icon='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Toots' /></h5>
 
               {statuses.map(
                 statusId => (
@@ -97,7 +99,7 @@ export default function DrawerResults ({
           ) : null}
           {hashtags && hashtags.size ? (
             <section>
-              <h5><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
+              <h5><Icon icon='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></h5>
 
               {hashtags.map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
             </section>
diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js
index d963039dc..a78117971 100644
--- a/app/javascript/flavours/glitch/features/emoji_picker/index.js
+++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js
@@ -129,7 +129,6 @@ class ModifierPickerMenu extends React.PureComponent {
     active: PropTypes.bool,
     onSelect: PropTypes.func.isRequired,
     onClose: PropTypes.func.isRequired,
-    modifier: PropTypes.number,
   };
 
   handleClick = e => {
@@ -166,36 +165,20 @@ class ModifierPickerMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-    if (this.node) {
-      this.node.querySelector('li:first-child button').focus(); // focus the first element when opened
-    }
   }
 
   render () {
-    const { active, modifier } = this.props;
+    const { active } = this.props;
 
     return (
-      <ul
-        className='emoji-picker-dropdown__modifiers__menu'
-        style={{ display: active ? 'block' : 'none' }}
-        role='menuitem'
-        ref={this.setRef}
-      >
-        {[1, 2, 3, 4, 5, 6].map(i => (
-          <li
-            onClick={this.handleClick}
-            role='menuitemradio'
-            aria-checked={i === (modifier || 1)}
-            data-index={i}
-            key={i}
-          >
-            <Emoji
-              emoji='fist' set='twitter' size={22} sheetSize={32} skin={i}
-              backgroundImageFn={backgroundImageFn}
-            />
-          </li>
-        ))}
-      </ul>
+      <div className='emoji-picker-dropdown__modifiers__menu' style={{ display: active ? 'block' : 'none' }} ref={this.setRef}>
+        <button onClick={this.handleClick} data-index={1}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={1} backgroundImageFn={backgroundImageFn} /></button>
+        <button onClick={this.handleClick} data-index={2}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={2} backgroundImageFn={backgroundImageFn} /></button>
+        <button onClick={this.handleClick} data-index={3}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={3} backgroundImageFn={backgroundImageFn} /></button>
+        <button onClick={this.handleClick} data-index={4}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={4} backgroundImageFn={backgroundImageFn} /></button>
+        <button onClick={this.handleClick} data-index={5}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={5} backgroundImageFn={backgroundImageFn} /></button>
+        <button onClick={this.handleClick} data-index={6}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={6} backgroundImageFn={backgroundImageFn} /></button>
+      </div>
     );
   }
 
@@ -227,22 +210,10 @@ class ModifierPicker extends React.PureComponent {
   render () {
     const { active, modifier } = this.props;
 
-    function setRef(ref) {
-      if (!ref) {
-        return;
-      }
-      // TODO: It would be nice if we could pass props directly to emoji-mart's buttons.
-      const button = ref.querySelector('button');
-      button.setAttribute('aria-haspopup', 'true');
-      button.setAttribute('aria-expanded', active);
-    }
-
     return (
       <div className='emoji-picker-dropdown__modifiers'>
-        <div ref={setRef}>
-          <Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={modifier} onClick={this.handleClick} backgroundImageFn={backgroundImageFn} />
-        </div>
-        <ModifierPickerMenu active={active} modifier={modifier} onSelect={this.handleSelect} onClose={this.props.onClose} />
+        <Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={modifier} onClick={this.handleClick} backgroundImageFn={backgroundImageFn} />
+        <ModifierPickerMenu active={active} onSelect={this.handleSelect} onClose={this.props.onClose} />
       </div>
     );
   }
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index 66cc10d78..8291319c3 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -98,7 +98,7 @@ export default class ActionBar extends React.PureComponent {
   }
 
   handleBlockClick = () => {
-    this.props.onBlock(this.props.status.get('account'));
+    this.props.onBlock(this.props.status);
   }
 
   handleReport = () => {
diff --git a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
index 6d3909ea7..e6c390537 100644
--- a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
+++ b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
@@ -38,6 +38,7 @@ const messages = defineMessages({
   blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
 });
 
 const makeMapStateToProps = () => {
@@ -136,11 +137,17 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(openModal('VIDEO', { media, time }));
   },
 
-  onBlock (account) {
+  onBlock (status) {
+    const account = status.get('account');
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   },
 
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index 73d3c7e9e..7f8f02188 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -54,6 +54,7 @@ const messages = defineMessages({
   detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
   tootHeading: { id: 'column.toot', defaultMessage: 'Toots and replies' },
 });
 
@@ -279,13 +280,19 @@ export default class Status extends ImmutablePureComponent {
     this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded });
   }
 
-  handleBlockClick = (account) => {
+  handleBlockClick = (status) => {
     const { dispatch, intl } = this.props;
+    const account = status.get('account');
 
     dispatch(openModal('CONFIRM', {
       message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
       confirm: intl.formatMessage(messages.blockConfirm),
       onConfirm: () => dispatch(blockAccount(account.get('id'))),
+      secondary: intl.formatMessage(messages.blockAndReport),
+      onSecondary: () => {
+        dispatch(blockAccount(account.get('id')));
+        dispatch(initReport(account, status));
+      },
     }));
   }
 
diff --git a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js
index 07281f818..970df30b6 100644
--- a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js
@@ -11,6 +11,8 @@ export default class ConfirmationModal extends React.PureComponent {
     confirm: PropTypes.string.isRequired,
     onClose: PropTypes.func.isRequired,
     onConfirm: PropTypes.func.isRequired,
+    secondary: PropTypes.string,
+    onSecondary: PropTypes.func,
     onDoNotAsk: PropTypes.func,
     intl: PropTypes.object.isRequired,
   };
@@ -27,6 +29,11 @@ export default class ConfirmationModal extends React.PureComponent {
     }
   }
 
+  handleSecondary = () => {
+    this.props.onClose();
+    this.props.onSecondary();
+  }
+
   handleCancel = () => {
     this.props.onClose();
   }
@@ -40,7 +47,7 @@ export default class ConfirmationModal extends React.PureComponent {
   }
 
   render () {
-    const { message, confirm, onDoNotAsk } = this.props;
+    const { message, confirm, secondary, onDoNotAsk } = this.props;
 
     return (
       <div className='modal-root__modal confirmation-modal'>
@@ -61,6 +68,9 @@ export default class ConfirmationModal extends React.PureComponent {
             <Button onClick={this.handleCancel} className='confirmation-modal__cancel-button'>
               <FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
             </Button>
+            {secondary !== undefined && (
+              <Button text={secondary} onClick={this.handleSecondary} className='confirmation-modal__secondary-button' />
+            )}
             <Button text={confirm} onClick={this.handleClick} ref={this.setRef} />
           </div>
         </div>
diff --git a/app/javascript/flavours/glitch/reducers/identity_proofs.js b/app/javascript/flavours/glitch/reducers/identity_proofs.js
new file mode 100644
index 000000000..58af0a5fa
--- /dev/null
+++ b/app/javascript/flavours/glitch/reducers/identity_proofs.js
@@ -0,0 +1,25 @@
+import { Map as ImmutableMap, fromJS } from 'immutable';
+import {
+  IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
+  IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
+  IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
+} from '../actions/identity_proofs';
+
+const initialState = ImmutableMap();
+
+export default function identityProofsReducer(state = initialState, action) {
+  switch(action.type) {
+  case IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST:
+    return state.set('isLoading', true);
+  case IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL:
+    return state.set('isLoading', false);
+  case IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS:
+    return state.update(identity_proofs => identity_proofs.withMutations(map => {
+      map.set('isLoading', false);
+      map.set('loaded', true);
+      map.set(action.accountId, fromJS(action.identity_proofs));
+    }));
+  default:
+    return state;
+  }
+};
diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js
index 7b3e0f651..76b38adb4 100644
--- a/app/javascript/flavours/glitch/reducers/index.js
+++ b/app/javascript/flavours/glitch/reducers/index.js
@@ -30,6 +30,7 @@ import listAdder from './list_adder';
 import filters from './filters';
 import pinnedAccountsEditor from './pinned_accounts_editor';
 import polls from './polls';
+import identity_proofs from './identity_proofs';
 
 const reducers = {
   dropdown_menu,
@@ -57,6 +58,7 @@ const reducers = {
   notifications,
   height_cache,
   custom_emojis,
+  identity_proofs,
   lists,
   listEditor,
   listAdder,
diff --git a/app/javascript/flavours/glitch/reducers/timelines.js b/app/javascript/flavours/glitch/reducers/timelines.js
index ca71c3833..cb233de1c 100644
--- a/app/javascript/flavours/glitch/reducers/timelines.js
+++ b/app/javascript/flavours/glitch/reducers/timelines.js
@@ -73,14 +73,15 @@ const updateTimeline = (state, timeline, status) => {
   }));
 };
 
-const deleteStatus = (state, id, accountId, references) => {
+const deleteStatus = (state, id, accountId, references, exclude_account = null) => {
   state.keySeq().forEach(timeline => {
-    state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
+    if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`)))
+      state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
   });
 
   // Remove reblogs of deleted status
   references.forEach(ref => {
-    state = deleteStatus(state, ref[0], ref[1], []);
+    state = deleteStatus(state, ref[0], ref[1], [], exclude_account);
   });
 
   return state;
@@ -99,7 +100,7 @@ const filterTimelines = (state, relationship, statuses) => {
     }
 
     references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => [item.get('id'), item.get('account')]);
-    state      = deleteStatus(state, status.get('id'), status.get('account'), references);
+    state      = deleteStatus(state, status.get('id'), status.get('account'), references, relationship.id);
   });
 
   return state;
diff --git a/app/javascript/flavours/glitch/styles/_mixins.scss b/app/javascript/flavours/glitch/styles/_mixins.scss
index 586802185..d542b1083 100644
--- a/app/javascript/flavours/glitch/styles/_mixins.scss
+++ b/app/javascript/flavours/glitch/styles/_mixins.scss
@@ -1,6 +1,5 @@
 @mixin avatar-radius() {
   border-radius: $ui-avatar-border-size;
-  background: transparent no-repeat;
   background-position: 50%;
   background-clip: padding-box;
 }
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index 42f53f525..05c7821e4 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/admin.scss
@@ -220,6 +220,11 @@ $content-width: 840px;
       color: $error-value-color;
       font-weight: 500;
     }
+
+    .neutral-hint {
+      color: $dark-text-color;
+      font-weight: 500;
+    }
   }
 
   @media screen and (max-width: $no-columns-breakpoint) {
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index 0b7b58bb0..00380c575 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -79,68 +79,8 @@
   background: lighten($ui-base-color, 4%);
 }
 
-.account__header {
-  flex: 0 0 auto;
-  background: lighten($ui-base-color, 4%);
-  text-align: center;
-  background-size: cover;
-  background-position: center;
-  position: relative;
-
-  .account__avatar {
-    @include avatar-radius();
-    @include avatar-size(90px);
-    display: block;
-    margin: 0 auto 10px;
-    overflow: hidden;
-  }
-
-  &.inactive {
-    opacity: 0.5;
-
-    .account__header__avatar {
-      filter: grayscale(100%);
-    }
-
-    .account__header__username {
-      color: $secondary-text-color;
-    }
-  }
-
-  & > div {
-    background: rgba(lighten($ui-base-color, 4%), 0.9);
-    padding: 20px 10px;
-  }
-
-  .account__header__content {
-    color: $secondary-text-color;
-  }
-
-  .account__header__display-name {
-    color: $primary-text-color;
-    display: inline-block;
-    width: 100%;
-    font-size: 20px;
-    line-height: 27px;
-    font-weight: 500;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-
-  .account__header__username {
-    color: $highlight-text-color;
-    font-size: 14px;
-    font-weight: 400;
-    display: block;
-    margin-bottom: 10px;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-}
-
 .account__disclaimer {
   padding: 10px;
-  border-top: 1px solid lighten($ui-base-color, 8%);
   color: $dark-text-color;
 
   strong {
@@ -166,39 +106,6 @@
   }
 }
 
-.account__header__content {
-  color: $darker-text-color;
-  font-size: 14px;
-  font-weight: 400;
-  overflow: hidden;
-  word-break: normal;
-  word-wrap: break-word;
-
-  p {
-    margin-bottom: 20px;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  a {
-    color: inherit;
-    text-decoration: underline;
-
-    &:hover {
-      text-decoration: none;
-    }
-  }
-}
-
-.account__header__display-name {
-  .emojione {
-    width: 25px;
-    height: 25px;
-  }
-}
-
 .account__action-bar {
   border-top: 1px solid lighten($ui-base-color, 8%);
   border-bottom: 1px solid lighten($ui-base-color, 8%);
@@ -208,24 +115,6 @@
   display: flex;
 }
 
-.account__action-bar-dropdown {
-  padding: 10px;
-
-  .dropdown--active {
-    .dropdown__content.dropdown__right {
-      left: 6px;
-      right: initial;
-    }
-
-    &::after {
-      bottom: initial;
-      margin-left: 11px;
-      margin-top: -7px;
-      right: initial;
-    }
-  }
-}
-
 .account__action-bar-links {
   display: flex;
   flex: 1 1 auto;
@@ -241,6 +130,10 @@
   padding: 10px 0;
   border-bottom: 4px solid transparent;
 
+  &:first-child {
+    border-left: 0;
+  }
+
   &.active {
     border-bottom: 4px solid $ui-highlight-color;
   }
@@ -270,15 +163,6 @@
   }
 }
 
-.account__header__avatar {
-  background-size: 90px 90px;
-  display: block;
-  height: 90px;
-  margin: 0 auto 10px;
-  overflow: hidden;
-  width: 90px;
-}
-
 .account-authorize {
   padding: 14px 10px;
 
@@ -427,42 +311,22 @@
   }
 }
 
-.account--follows-info {
+.relationship-tag {
   color: $primary-text-color;
-  position: absolute;
-  top: 10px;
-  left: 10px;
-  opacity: 0.7;
-  display: inline-block;
+  margin-bottom: 4px;
+  display: block;
   vertical-align: top;
-  background-color: rgba($base-overlay-background, 0.4);
+  background-color: $base-overlay-background;
   text-transform: uppercase;
   font-size: 11px;
   font-weight: 500;
   padding: 4px;
   border-radius: 4px;
-}
-
-.account--muting-info {
-  color: $primary-text-color;
-  position: absolute;
-  top: 40px;
-  left: 10px;
   opacity: 0.7;
-  display: inline-block;
-  vertical-align: top;
-  background-color: rgba($base-overlay-background, 0.4);
-  text-transform: uppercase;
-  font-size: 11px;
-  font-weight: 500;
-  padding: 4px;
-  border-radius: 4px;
-}
 
-.account--action-button {
-  position: absolute;
-  top: 10px;
-  right: 20px;
+  &:hover {
+    opacity: 1;
+  }
 }
 
 .account-gallery__container {
@@ -614,8 +478,193 @@
   }
 }
 
-.account__header .roles {
-  margin-top: 20px;
-  margin-bottom: 20px;
-  padding: 0 15px;
+.account__header__content {
+  color: $darker-text-color;
+  font-size: 14px;
+  font-weight: 400;
+  overflow: hidden;
+  word-break: normal;
+  word-wrap: break-word;
+
+  p {
+    margin-bottom: 20px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  a {
+    color: inherit;
+    text-decoration: underline;
+
+    &:hover {
+      text-decoration: none;
+    }
+  }
+}
+
+.account__header {
+  overflow: hidden;
+
+  &.inactive {
+    opacity: 0.5;
+
+    .account__header__image,
+    .account__avatar {
+      filter: grayscale(100%);
+    }
+  }
+
+  &__info {
+    position: absolute;
+    top: 10px;
+    left: 10px;
+  }
+
+  &__image {
+    overflow: hidden;
+    height: 145px;
+    position: relative;
+    background: darken($ui-base-color, 4%);
+
+    img {
+      object-fit: cover;
+      display: block;
+      width: 100%;
+      height: 100%;
+      margin: 0;
+    }
+  }
+
+  &__bar {
+    position: relative;
+    background: lighten($ui-base-color, 4%);
+    padding: 5px;
+    border-bottom: 1px solid lighten($ui-base-color, 12%);
+
+    .avatar {
+      display: block;
+      flex: 0 0 auto;
+      width: 94px;
+      margin-left: -2px;
+
+      .account__avatar {
+        background: darken($ui-base-color, 8%);
+        border: 2px solid lighten($ui-base-color, 4%);
+      }
+    }
+  }
+
+  &__tabs {
+    display: flex;
+    align-items: flex-start;
+    padding: 7px 5px;
+    margin-top: -55px;
+
+    &__buttons {
+      display: flex;
+      align-items: center;
+      padding-top: 55px;
+      overflow: hidden;
+
+      .icon-button {
+        border: 1px solid lighten($ui-base-color, 12%);
+        border-radius: 4px;
+        box-sizing: content-box;
+        padding: 2px;
+      }
+
+      .button {
+        margin: 0 8px;
+      }
+    }
+
+    &__name {
+      padding: 5px;
+
+      .account-role {
+        vertical-align: top;
+      }
+
+      .emojione {
+        width: 22px;
+        height: 22px;
+      }
+
+      h1 {
+        font-size: 16px;
+        line-height: 24px;
+        color: $primary-text-color;
+        font-weight: 500;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+
+        small {
+          display: block;
+          font-size: 14px;
+          color: $darker-text-color;
+          font-weight: 400;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+    }
+
+    .spacer {
+      flex: 1 1 auto;
+    }
+  }
+
+  &__bio {
+    overflow: hidden;
+    margin: 0 -5px;
+
+    .account__header__content {
+      padding: 20px 15px;
+      padding-bottom: 5px;
+      color: $primary-text-color;
+    }
+
+    .account__header__fields {
+      margin: 0;
+      border-top: 1px solid lighten($ui-base-color, 12%);
+
+      a {
+        color: lighten($ui-highlight-color, 8%);
+      }
+
+      dl:first-child .verified {
+        border-radius: 0 4px 0 0;
+      }
+
+      .verified a {
+        color: $valid-value-color;
+      }
+    }
+  }
+
+  &__extra {
+    margin-top: 4px;
+
+    &__links {
+      font-size: 14px;
+      color: $darker-text-color;
+
+      a {
+        display: inline-block;
+        color: $darker-text-color;
+        text-decoration: none;
+        padding: 10px;
+        padding-top: 20px;
+        font-weight: 500;
+
+        strong {
+          font-weight: 700;
+          color: $primary-text-color;
+        }
+      }
+    }
+  }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss
index f4931c36c..d22783b94 100644
--- a/app/javascript/flavours/glitch/styles/components/drawer.scss
+++ b/app/javascript/flavours/glitch/styles/components/drawer.scss
@@ -196,43 +196,35 @@
   overflow-y: auto;
 
   & > header {
-    border-bottom: 1px solid darken($ui-base-color, 4%);
-    padding: 15px 10px;
     color: $dark-text-color;
     background: lighten($ui-base-color, 2%);
-    font-size: 14px;
+    padding: 15px;
     font-weight: 500;
+    font-size: 16px;
+    cursor: default;
+
+    .fa {
+      display: inline-block;
+      margin-right: 5px;
+    }
   }
 
   & > section {
-    background: $ui-base-color;
-    margin-bottom: 20px;
+    margin-bottom: 5px;
 
     h5 {
-      position: relative;
-
-      &::before {
-        content: "";
-        display: block;
-        position: absolute;
-        left: 0;
-        right: 0;
-        top: 50%;
-        width: 100%;
-        height: 0;
-        border-top: 1px solid lighten($ui-base-color, 8%);
-      }
+      background: darken($ui-base-color, 4%);
+      border-bottom: 1px solid lighten($ui-base-color, 8%);
+      cursor: default;
+      display: flex;
+      padding: 15px;
+      font-weight: 500;
+      font-size: 16px;
+      color: $dark-text-color;
 
-      span {
+      .fa {
         display: inline-block;
-        background: $ui-base-color;
-        color: $darker-text-color;
-        font-size: 14px;
-        font-weight: 500;
-        padding: 10px;
-        position: relative;
-        z-index: 1;
-        cursor: default;
+        margin-right: 5px;
       }
     }
 
diff --git a/app/javascript/flavours/glitch/styles/components/emoji.scss b/app/javascript/flavours/glitch/styles/components/emoji.scss
index ccfd42f28..dd386d698 100644
--- a/app/javascript/flavours/glitch/styles/components/emoji.scss
+++ b/app/javascript/flavours/glitch/styles/components/emoji.scss
@@ -44,11 +44,11 @@
   box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
   overflow: hidden;
 
-  li {
+  button {
     display: block;
     cursor: pointer;
     border: 0;
-    padding: 3px 8px;
+    padding: 4px 8px;
     background: transparent;
 
     &:hover,
diff --git a/app/javascript/flavours/glitch/styles/components/emoji_picker.scss b/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
index 171623352..dcc551c5b 100644
--- a/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
+++ b/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
@@ -1,5 +1,3 @@
-@import '~emoji-mart/css/emoji-mart.css';
-
 .emoji-mart {
   &,
   * {
@@ -53,14 +51,6 @@
 
   &:hover {
     color: darken($lighter-text-color, 4%);
-
-    svg {
-      fill: darken($lighter-text-color, 4%);
-    }
-  }
-
-  svg {
-    fill: $lighter-text-color;
   }
 }
 
@@ -69,19 +59,11 @@
 
   &:hover {
     color: darken($highlight-text-color, 4%);
-
-    svg {
-      fill: darken($highlight-text-color, 4%);
-    }
   }
 
   .emoji-mart-anchor-bar {
     bottom: 0;
   }
-
-  svg {
-    fill: $highlight-text-color;
-  }
 }
 
 .emoji-mart-anchor-bar {
@@ -101,6 +83,7 @@
   }
 
   svg {
+    fill: currentColor;
     max-height: 18px;
   }
 }
@@ -120,14 +103,15 @@
 }
 
 .emoji-mart-search {
-  margin: 10px 40px 10px 5px;
+  padding: 10px;
+  padding-right: 45px;
   background: $simple-background-color;
 
   input {
     font-size: 14px;
     font-weight: 400;
     padding: 7px 9px;
-    font-family: $font-sans-serif;
+    font-family: inherit;
     display: block;
     width: 100%;
     background: rgba($ui-secondary-color, 0.3);
@@ -182,7 +166,6 @@
     font-weight: 500;
     padding: 5px 6px;
     background: $simple-background-color;
-    font-family: $font-sans-serif;
   }
 }
 
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index b9811f25c..b098676b0 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -35,6 +35,17 @@
     transition: all 200ms ease-out;
   }
 
+  &--destructive {
+    transition: none;
+
+    &:active,
+    &:focus,
+    &:hover {
+      background-color: $error-red;
+      transition: none;
+    }
+  }
+
   &:disabled {
     background-color: $ui-primary-color;
     cursor: default;
@@ -269,9 +280,7 @@
 
 .display-name {
   display: block;
-  padding: 6px 0;
   max-width: 100%;
-  height: 36px;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
@@ -1263,7 +1272,6 @@ noscript {
 @import 'domains';
 @import 'status';
 @import 'modal';
-@import 'metadata';
 @import 'composer';
 @import 'columns';
 @import 'regeneration_indicator';
diff --git a/app/javascript/flavours/glitch/styles/components/metadata.scss b/app/javascript/flavours/glitch/styles/components/metadata.scss
index da045574a..e69de29bb 100644
--- a/app/javascript/flavours/glitch/styles/components/metadata.scss
+++ b/app/javascript/flavours/glitch/styles/components/metadata.scss
@@ -1,45 +0,0 @@
-.account__header .account__header__fields {
-  font-size: 15px;
-  line-height: 20px;
-  overflow: hidden;
-  margin: 20px -10px -20px;
-  border-bottom: 0;
-  border-top: 0;
-
-  dl {
-    background: $ui-base-color;
-    border-top: 1px solid lighten($ui-base-color, 4%);
-    border-bottom: 0;
-    display: flex;
-  }
-
-  dt,
-  dd {
-    box-sizing: border-box;
-    padding: 14px 5px;
-    text-align: center;
-    max-height: 48px;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-  }
-
-  dt {
-    color: $darker-text-color;
-    background: lighten($ui-base-color, 13%);
-    width: 120px;
-    flex: 0 0 auto;
-    font-weight: 500;
-  }
-
-  dd {
-    flex: 1 1 auto;
-    color: $primary-text-color;
-    background: $ui-base-color;
-
-    &.verified {
-      border: 1px solid rgba($valid-value-color, 0.5);
-      background: rgba($valid-value-color, 0.25);
-    }
-  }
-}
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 3598959e7..fece8593b 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -671,6 +671,7 @@
 
 .confirmation-modal__action-bar,
 .mute-modal__action-bar {
+  .confirmation-modal__secondary-button,
   .confirmation-modal__cancel-button,
   .mute-modal__cancel-button {
     background-color: transparent;
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index 9d2757065..b73dd3d09 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -357,6 +357,7 @@
 
 .status__info__account {
   display: flex;
+  align-items: center;
 }
 
 .status-check-box {
diff --git a/app/javascript/flavours/glitch/styles/containers.scss b/app/javascript/flavours/glitch/styles/containers.scss
index fd334f869..b27524739 100644
--- a/app/javascript/flavours/glitch/styles/containers.scss
+++ b/app/javascript/flavours/glitch/styles/containers.scss
@@ -10,12 +10,10 @@
 }
 
 .logo-container {
-  margin: 100px auto;
-  margin-bottom: 50px;
+  margin: 100px auto 50px;
 
-  @media screen and (max-width: 400px) {
-    margin: 30px auto;
-    margin-bottom: 20px;
+  @media screen and (max-width: 500px) {
+    margin: 40px auto 0;
   }
 
   h1 {
@@ -683,6 +681,7 @@
           color: $darker-text-color;
           text-decoration: none;
           padding: 15px;
+          font-weight: 500;
 
           strong {
             font-weight: 700;
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index 9ef45e425..91888d305 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -475,6 +475,42 @@ code {
       }
     }
   }
+
+  &__overlay-area {
+    position: relative;
+
+    &__overlay {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background: rgba($ui-base-color, 0.65);
+      backdrop-filter: blur(2px);
+      border-radius: 4px;
+
+      &__content {
+        text-align: center;
+
+        &.rich-formatting {
+          &,
+          p {
+            color: $primary-text-color;
+          }
+        }
+      }
+    }
+  }
+}
+
+.block-icon {
+  display: block;
+  margin: 0 auto;
+  margin-bottom: 10px;
+  font-size: 24px;
 }
 
 .flash-message {
@@ -818,13 +854,19 @@ code {
     flex: 1;
     flex-direction: column;
     flex-shrink: 1;
+    max-width: 50%;
 
     &-sep {
+      align-self: center;
       flex-grow: 0;
       overflow: visible;
       position: relative;
       z-index: 1;
     }
+
+    p {
+      word-break: break-word;
+    }
   }
 
   .account__avatar {
@@ -846,12 +888,13 @@ code {
       height: 100%;
       left: 50%;
       position: absolute;
+      top: 0;
       width: 1px;
     }
   }
 
   &__row {
-    align-items: center;
+    align-items: flex-start;
     display: flex;
     flex-direction: row;
   }
diff --git a/app/javascript/flavours/glitch/styles/metadata.scss b/app/javascript/flavours/glitch/styles/metadata.scss
deleted file mode 100644
index 280848959..000000000
--- a/app/javascript/flavours/glitch/styles/metadata.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-.account__header__fields {
-  $meta-table-border: lighten($ui-base-color, 8%);
-  padding: 0;
-  margin: 15px -15px -15px -15px;
-  border: 0 none;
-  border-top: 1px solid $meta-table-border;
-  border-bottom: 1px solid $meta-table-border;
-  font-size: 14px;
-  line-height: 20px;
-
-  dl {
-    display: flex;
-    border-bottom: 1px solid $meta-table-border;
-  }
-
-  dt,
-  dd {
-    box-sizing: border-box;
-    padding: 14px;
-    text-align: center;
-    max-height: 48px;
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-  }
-
-  dt {
-    padding-left: 15px;
-    font-weight: 500;
-    text-align: center;
-    width: 120px;
-    flex: 0 0 auto;
-    color: $secondary-text-color;
-    background: darken($ui-base-color, 8%);
-  }
-
-  dd {
-    flex: 1 1 auto;
-    color: $darker-text-color;
-  }
-
-  a {
-    color: $highlight-text-color;
-    text-decoration: none;
-
-    &:hover,
-    &:focus,
-    &:active {
-      text-decoration: underline;
-    }
-  }
-
-  dl:last-child {
-    border-bottom: 0;
-  }
-}
diff --git a/app/javascript/flavours/glitch/styles/stream_entries.scss b/app/javascript/flavours/glitch/styles/stream_entries.scss
index 45dcf70ed..6735049b9 100644
--- a/app/javascript/flavours/glitch/styles/stream_entries.scss
+++ b/app/javascript/flavours/glitch/styles/stream_entries.scss
@@ -192,6 +192,7 @@
     .status__info .status__display-name {
       display: block;
       max-width: 100%;
+      padding: 6px 0;
       padding-right: 25px;
       margin: initial;
 
diff --git a/app/javascript/flavours/glitch/styles/widgets.scss b/app/javascript/flavours/glitch/styles/widgets.scss
index 645192ea4..307e509d5 100644
--- a/app/javascript/flavours/glitch/styles/widgets.scss
+++ b/app/javascript/flavours/glitch/styles/widgets.scss
@@ -352,6 +352,7 @@
     border-radius: 50%;
     position: relative;
     margin-left: -10px;
+    background: darken($ui-base-color, 8%);
     border: 2px solid $ui-base-color;
 
     &:nth-child(1) {
diff --git a/app/javascript/flavours/glitch/util/api.js b/app/javascript/flavours/glitch/util/api.js
index 033d2d67b..c59a24518 100644
--- a/app/javascript/flavours/glitch/util/api.js
+++ b/app/javascript/flavours/glitch/util/api.js
@@ -13,10 +13,14 @@ export const getLinks = response => {
 };
 
 let csrfHeader = {};
+
 function setCSRFHeader() {
-  const csrfToken = document.querySelector('meta[name=csrf-token]').content;
-  csrfHeader['X-CSRF-Token'] = csrfToken;
+  const csrfToken = document.querySelector('meta[name=csrf-token]');
+  if (csrfToken) {
+    csrfHeader['X-CSRF-Token'] = csrfToken.content;
+  }
 }
+
 ready(setCSRFHeader);
 
 export default getState => axios.create({
diff --git a/app/javascript/flavours/glitch/util/emoji/emoji_picker.js b/app/javascript/flavours/glitch/util/emoji/emoji_picker.js
index 73fcaa8c8..044d38cb2 100644
--- a/app/javascript/flavours/glitch/util/emoji/emoji_picker.js
+++ b/app/javascript/flavours/glitch/util/emoji/emoji_picker.js
@@ -1,5 +1,5 @@
-import Picker from 'emoji-mart/dist-modern/components/picker/picker';
-import Emoji from 'emoji-mart/dist-modern/components/emoji/emoji';
+import Picker from 'emoji-mart/dist-es/components/picker/picker';
+import Emoji from 'emoji-mart/dist-es/components/emoji/emoji';
 
 export {
   Picker,