about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features
diff options
context:
space:
mode:
authorpluralcafe-docker <git@plural.cafe>2018-11-27 00:54:40 +0000
committerpluralcafe-docker <git@plural.cafe>2018-11-27 00:54:40 +0000
commit384a602fd4117a73338542c59985f54acf5fb3f8 (patch)
tree2885959ad124784985daa7709cd645a5a8802a07 /app/javascript/flavours/glitch/features
parent72444f73ef4e04118f9c66b965c3c903cff8ef37 (diff)
parent6b6e633c095485f95350c4308a942192e5fe8806 (diff)
Merge branch 'glitch'
Diffstat (limited to 'app/javascript/flavours/glitch/features')
-rw-r--r--app/javascript/flavours/glitch/features/account/components/action_bar.js15
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/header.js6
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js6
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.js3
-rw-r--r--app/javascript/flavours/glitch/features/composer/direct_warning/index.js4
-rw-r--r--app/javascript/flavours/glitch/features/composer/warning/index.js7
-rw-r--r--app/javascript/flavours/glitch/features/drawer/account/index.js25
-rw-r--r--app/javascript/flavours/glitch/features/drawer/header/index.js3
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/components/account.js43
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/components/list.js68
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/index.js73
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/navigation/index.js3
-rw-r--r--app/javascript/flavours/glitch/features/notifications/index.js1
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js17
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.js2
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js10
18 files changed, 268 insertions, 25 deletions
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 3d6eeb06a..ffa5b7e5e 100644
--- a/app/javascript/flavours/glitch/features/account/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/account/components/action_bar.js
@@ -5,6 +5,7 @@ import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_cont
 import { NavLink } from 'react-router-dom';
 import { defineMessages, 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}' },
@@ -25,6 +26,7 @@ const messages = defineMessages({
   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}' },
 });
 
@@ -43,6 +45,7 @@ export default class ActionBar extends React.PureComponent {
     onBlockDomain: PropTypes.func.isRequired,
     onUnblockDomain: PropTypes.func.isRequired,
     onEndorseToggle: PropTypes.func.isRequired,
+    onAddToList: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
   };
 
@@ -75,7 +78,9 @@ export default class ActionBar extends React.PureComponent {
     menu.push(null);
 
     if (account.get('id') === me) {
-      menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
+      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'])) {
@@ -85,6 +90,7 @@ export default class ActionBar extends React.PureComponent {
         }
 
         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);
       }
 
@@ -128,9 +134,12 @@ export default class ActionBar extends React.PureComponent {
       }
     }
 
-    if (account.get('id') !== me && isStaff) {
+    if (account.get('id') !== me && isStaff && (accountAdminLink !== undefined)) {
       menu.push(null);
-      menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
+      menu.push({
+        text: intl.formatMessage(messages.admin_account, { name: account.get('username') }),
+        href: accountAdminLink(account.get('id')),
+      });
     }
 
     return (
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 89b9be92b..8dc0be93e 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
@@ -23,6 +23,7 @@ export default class Header extends ImmutablePureComponent {
     onBlockDomain: PropTypes.func.isRequired,
     onUnblockDomain: PropTypes.func.isRequired,
     onEndorseToggle: PropTypes.func.isRequired,
+    onAddToList: PropTypes.func.isRequired,
     hideTabs: PropTypes.bool,
   };
 
@@ -78,6 +79,10 @@ export default class Header extends ImmutablePureComponent {
     this.props.onEndorseToggle(this.props.account);
   }
 
+  handleAddToList = () => {
+    this.props.onAddToList(this.props.account);
+  }
+
   render () {
     const { account, hideTabs } = this.props;
 
@@ -106,6 +111,7 @@ export default class Header extends ImmutablePureComponent {
           onBlockDomain={this.handleBlockDomain}
           onUnblockDomain={this.handleUnblockDomain}
           onEndorseToggle={this.handleEndorseToggle}
+          onAddToList={this.handleAddToList}
         />
 
         {!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 f5f56d85c..e333c31a1 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
@@ -120,6 +120,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(unblockDomain(domain));
   },
 
+  onAddToList(account){
+    dispatch(openModal('LIST_ADDER', {
+      accountId: account.get('id'),
+    }));
+  },
+
 });
 
 export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.js
index 2216f9153..6f887a145 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.js
@@ -11,6 +11,7 @@ import HeaderContainer from './containers/header_container';
 import ColumnBackButton from '../../components/column_back_button';
 import { List as ImmutableList } from 'immutable';
 import ImmutablePureComponent from 'react-immutable-pure-component';
+import { FormattedMessage } from 'react-intl';
 
 const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
   const path = withReplies ? `${accountId}:with_replies` : accountId;
@@ -77,12 +78,14 @@ export default class AccountTimeline extends ImmutablePureComponent {
 
         <StatusList
           prepend={<HeaderContainer accountId={this.props.params.accountId} />}
+          alwaysPrepend
           scrollKey='account_timeline'
           statusIds={statusIds}
           featuredStatusIds={featuredStatusIds}
           isLoading={isLoading}
           hasMore={hasMore}
           onLoadMore={this.handleLoadMore}
+          emptyMessage={<FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />}
         />
       </Column>
     );
diff --git a/app/javascript/flavours/glitch/features/composer/direct_warning/index.js b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js
index d1febdd1b..3b1369acd 100644
--- a/app/javascript/flavours/glitch/features/composer/direct_warning/index.js
+++ b/app/javascript/flavours/glitch/features/composer/direct_warning/index.js
@@ -2,6 +2,7 @@ import React from 'react';
 import Motion from 'flavours/glitch/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 import { defineMessages, FormattedMessage } from 'react-intl';
+import { termsLink} from 'flavours/glitch/util/backend_links';
 
 //  This is the spring used with our motion.
 const motionSpring = spring(1, { damping: 35, stiffness: 400 });
@@ -42,7 +43,8 @@ export default function ComposerDirectWarning () {
           }}
         >
           <span>
-            <FormattedMessage {...messages.disclaimer} /> <a href='/terms' target='_blank'><FormattedMessage {...messages.learn_more} /></a>
+            <FormattedMessage {...messages.disclaimer} />
+            { termsLink !== undefined && <a href={termsLink} target='_blank'><FormattedMessage {...messages.learn_more} /></a> }
           </span>
         </div>
       )}
diff --git a/app/javascript/flavours/glitch/features/composer/warning/index.js b/app/javascript/flavours/glitch/features/composer/warning/index.js
index c225b50e8..8be8acbce 100644
--- a/app/javascript/flavours/glitch/features/composer/warning/index.js
+++ b/app/javascript/flavours/glitch/features/composer/warning/index.js
@@ -2,6 +2,7 @@ import React from 'react';
 import Motion from 'flavours/glitch/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 import { defineMessages, FormattedMessage } from 'react-intl';
+import { profileLink } from 'flavours/glitch/util/backend_links';
 
 //  This is the spring used with our motion.
 const motionSpring = spring(1, { damping: 35, stiffness: 400 });
@@ -20,6 +21,10 @@ const messages = defineMessages({
 
 //  The component.
 export default function ComposerWarning () {
+  let lockedLink = <FormattedMessage {...messages.locked} />;
+  if (profileLink !== undefined) {
+    lockedLink = <a href={profileLink}>{lockedLink}</a>;
+  }
   return (
     <Motion
       defaultStyle={{
@@ -43,7 +48,7 @@ export default function ComposerWarning () {
         >
           <FormattedMessage
             {...messages.disclaimer}
-            values={{ locked: <a href='/settings/profile'><FormattedMessage {...messages.locked} /></a> }}
+            values={{ locked: lockedLink }}
           />
         </div>
       )}
diff --git a/app/javascript/flavours/glitch/features/drawer/account/index.js b/app/javascript/flavours/glitch/features/drawer/account/index.js
index 168d0c2cf..552848641 100644
--- a/app/javascript/flavours/glitch/features/drawer/account/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/account/index.js
@@ -12,6 +12,7 @@ import Permalink from 'flavours/glitch/components/permalink';
 
 //  Utils.
 import { hiddenComponent } from 'flavours/glitch/util/react_helpers';
+import { profileLink } from 'flavours/glitch/util/backend_links';
 
 //  Messages.
 const messages = defineMessages({
@@ -28,12 +29,14 @@ export default function DrawerAccount ({ account }) {
   if (!account) {
     return (
       <div className='drawer--account'>
-        <a
-          className='edit'
-          href='/settings/profile'
-        >
-          <FormattedMessage {...messages.edit} />
-        </a>
+        { profileLink !== undefined && (
+          <a
+            className='edit'
+            href={ profileLink }
+          >
+            <FormattedMessage {...messages.edit} />
+          </a>
+        )}
       </div>
     );
   }
@@ -59,10 +62,12 @@ export default function DrawerAccount ({ account }) {
       >
         <strong>@{account.get('acct')}</strong>
       </Permalink>
-      <a
-        className='edit'
-        href='/settings/profile'
-      ><FormattedMessage {...messages.edit} /></a>
+      { profileLink !== undefined && (
+        <a
+          className='edit'
+          href={ profileLink }
+        ><FormattedMessage {...messages.edit} /></a>
+      )}
     </div>
   );
 }
diff --git a/app/javascript/flavours/glitch/features/drawer/header/index.js b/app/javascript/flavours/glitch/features/drawer/header/index.js
index 7fefd32c9..da5599732 100644
--- a/app/javascript/flavours/glitch/features/drawer/header/index.js
+++ b/app/javascript/flavours/glitch/features/drawer/header/index.js
@@ -10,6 +10,7 @@ import Icon from 'flavours/glitch/components/icon';
 
 //  Utils.
 import { conditionalRender } from 'flavours/glitch/util/react_helpers';
+import { signOutLink } from 'flavours/glitch/util/backend_links';
 
 //  Messages.
 const messages = defineMessages({
@@ -109,7 +110,7 @@ export default function DrawerHeader ({
       <a
         aria-label={intl.formatMessage(messages.logout)}
         data-method='delete'
-        href='/auth/sign_out'
+        href={ signOutLink }
         title={intl.formatMessage(messages.logout)}
       ><Icon icon='sign-out' /></a>
     </nav>
diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.js
index ce1ae50e4..c87f76c5e 100644
--- a/app/javascript/flavours/glitch/features/getting_started/index.js
+++ b/app/javascript/flavours/glitch/features/getting_started/index.js
@@ -13,6 +13,7 @@ import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
 import { List as ImmutableList } from 'immutable';
 import { createSelector } from 'reselect';
 import { fetchLists } from 'flavours/glitch/actions/lists';
+import { preferencesLink, profileLink, signOutLink } from 'flavours/glitch/util/backend_links';
 
 const messages = defineMessages({
   heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
@@ -157,9 +158,9 @@ export default class GettingStarted extends ImmutablePureComponent {
             <ColumnSubheading text={intl.formatMessage(messages.lists_subheading)} />
             {listItems}
             <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} />
-            <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
+            { preferencesLink !== undefined && <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href={preferencesLink} /> }
             <ColumnLink icon='cogs' text={intl.formatMessage(messages.settings)} onClick={openSettings} />
-            <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
+            <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href={signOutLink} method='delete' />
           </div>
 
           <div className='getting-started__footer'>
diff --git a/app/javascript/flavours/glitch/features/list_adder/components/account.js b/app/javascript/flavours/glitch/features/list_adder/components/account.js
new file mode 100644
index 000000000..1369aac07
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/list_adder/components/account.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { makeGetAccount } from '../../../selectors';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import Avatar from '../../../components/avatar';
+import DisplayName from '../../../components/display_name';
+import { injectIntl } from 'react-intl';
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { accountId }) => ({
+    account: getAccount(state, accountId),
+  });
+
+  return mapStateToProps;
+};
+
+
+export default @connect(makeMapStateToProps)
+@injectIntl
+class Account extends ImmutablePureComponent {
+
+  static propTypes = {
+    account: ImmutablePropTypes.map.isRequired,
+  };
+
+  render () {
+    const { account } = this.props;
+    return (
+      <div className='account'>
+        <div className='account__wrapper'>
+          <div className='account__display-name'>
+            <div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
+            <DisplayName account={account} />
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/list_adder/components/list.js b/app/javascript/flavours/glitch/features/list_adder/components/list.js
new file mode 100644
index 000000000..cb8eb7d7a
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/list_adder/components/list.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import IconButton from '../../../components/icon_button';
+import { defineMessages, injectIntl } from 'react-intl';
+import { removeFromListAdder, addToListAdder } from '../../../actions/lists';
+
+const messages = defineMessages({
+  remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
+  add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
+});
+
+const MapStateToProps = (state, { listId, added }) => ({
+  list: state.get('lists').get(listId),
+  added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
+});
+
+const mapDispatchToProps = (dispatch, { listId }) => ({
+  onRemove: () => dispatch(removeFromListAdder(listId)),
+  onAdd: () => dispatch(addToListAdder(listId)),
+});
+
+export default @connect(MapStateToProps, mapDispatchToProps)
+@injectIntl
+class List extends ImmutablePureComponent {
+
+  static propTypes = {
+    list: ImmutablePropTypes.map.isRequired,
+    intl: PropTypes.object.isRequired,
+    onRemove: PropTypes.func.isRequired,
+    onAdd: PropTypes.func.isRequired,
+    added: PropTypes.bool,
+  };
+
+  static defaultProps = {
+    added: false,
+  };
+
+  render () {
+    const { list, intl, onRemove, onAdd, added } = this.props;
+
+    let button;
+
+    if (added) {
+      button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
+    } else {
+      button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
+    }
+
+    return (
+      <div className='list'>
+        <div className='list__wrapper'>
+          <div className='list__display-name'>
+            <i className='fa fa-fw fa-list-ul column-link__icon' />
+            {list.get('title')}
+          </div>
+
+          <div className='account__relationship'>
+            {button}
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/list_adder/index.js b/app/javascript/flavours/glitch/features/list_adder/index.js
new file mode 100644
index 000000000..cb8a15e8c
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/list_adder/index.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { connect } from 'react-redux';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import { injectIntl } from 'react-intl';
+import { setupListAdder, resetListAdder } from '../../actions/lists';
+import { createSelector } from 'reselect';
+import List from './components/list';
+import Account from './components/account';
+import NewListForm from '../lists/components/new_list_form';
+// hack
+
+const getOrderedLists = createSelector([state => state.get('lists')], lists => {
+  if (!lists) {
+    return lists;
+  }
+
+  return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
+});
+
+const mapStateToProps = state => ({
+  listIds: getOrderedLists(state).map(list=>list.get('id')),
+});
+
+const mapDispatchToProps = dispatch => ({
+  onInitialize: accountId => dispatch(setupListAdder(accountId)),
+  onReset: () => dispatch(resetListAdder()),
+});
+
+export default @connect(mapStateToProps, mapDispatchToProps)
+@injectIntl
+class ListAdder extends ImmutablePureComponent {
+
+  static propTypes = {
+    accountId: PropTypes.string.isRequired,
+    onClose: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    onInitialize: PropTypes.func.isRequired,
+    onReset: PropTypes.func.isRequired,
+    listIds: ImmutablePropTypes.list.isRequired,
+  };
+
+  componentDidMount () {
+    const { onInitialize, accountId } = this.props;
+    onInitialize(accountId);
+  }
+
+  componentWillUnmount () {
+    const { onReset } = this.props;
+    onReset();
+  }
+
+  render () {
+    const { accountId, listIds } = this.props;
+
+    return (
+      <div className='modal-root__modal list-adder'>
+        <div className='list-adder__account'>
+          <Account accountId={accountId} />
+        </div>
+
+        <NewListForm />
+
+
+        <div className='list-adder__lists'>
+          {listIds.map(ListId => <List key={ListId} listId={ListId} />)}
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
index cf02101cf..ce10e3f51 100644
--- a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
@@ -5,6 +5,7 @@ import { injectIntl, defineMessages } from 'react-intl';
 
 //  Our imports
 import LocalSettingsNavigationItem from './item';
+import { preferencesLink } from 'flavours/glitch/util/backend_links';
 
 //  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
@@ -71,7 +72,7 @@ export default class LocalSettingsNavigation extends React.PureComponent {
         />
         <LocalSettingsNavigationItem
           active={index === 5}
-          href='/settings/preferences'
+          href={ preferencesLink }
           index={5}
           icon='sliders'
           title={intl.formatMessage(messages.preferences)}
diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js
index 13ed26865..0e73f02d8 100644
--- a/app/javascript/flavours/glitch/features/notifications/index.js
+++ b/app/javascript/flavours/glitch/features/notifications/index.js
@@ -187,6 +187,7 @@ export default class Notifications extends React.PureComponent {
         scrollKey={`notifications-${columnId}`}
         trackScroll={!pinned}
         isLoading={isLoading}
+        showLoading={isLoading && notifications.size === 0}
         hasMore={hasMore}
         emptyMessage={emptyMessage}
         onLoadMore={this.handleLoadOlder}
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 009aa49eb..be82bca5b 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 import { defineMessages, injectIntl } from 'react-intl';
 import { me, isStaff } from 'flavours/glitch/util/initial_state';
+import { accountAdminLink, statusAdminLink } from 'flavours/glitch/util/backend_links';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -148,10 +149,20 @@ export default class ActionBar extends React.PureComponent {
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
       menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-      if (isStaff) {
+      if (isStaff && (accountAdminLink || statusAdminLink)) {
         menu.push(null);
-        menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
-        menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
+        if (accountAdminLink !== undefined) {
+          menu.push({
+            text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
+            href: accountAdminLink(status.getIn(['account', 'id'])),
+          });
+        }
+        if (statusAdminLink !== undefined) {
+          menu.push({
+            text: intl.formatMessage(messages.admin_status),
+            href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')),
+          });
+        }
       }
     }
 
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index 1fe0c069d..436c2df0a 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -65,6 +65,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
             sensitive={status.get('sensitive')}
             letterbox={settings.getIn(['media', 'letterbox'])}
             fullwidth={settings.getIn(['media', 'fullwidth'])}
+            preventPlayback={!expanded}
             onOpenVideo={this.handleOpenVideo}
             autoplay
           />
@@ -78,6 +79,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
             media={status.get('media_attachments')}
             letterbox={settings.getIn(['media', 'letterbox'])}
             fullwidth={settings.getIn(['media', 'fullwidth'])}
+            hidden={!expanded}
             onOpenMedia={this.props.onOpenMedia}
           />
         );
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
index c9f54804a..303e05db6 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
@@ -19,6 +19,7 @@ import {
   SettingsModal,
   EmbedModal,
   ListEditor,
+  ListAdder,
   PinnedAccountsEditor,
 } from 'flavours/glitch/util/async-components';
 
@@ -36,6 +37,7 @@ const MODAL_COMPONENTS = {
   'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
   'EMBED': EmbedModal,
   'LIST_EDITOR': ListEditor,
+  'LIST_ADDER':ListAdder,
   'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
   'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor,
 };
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 4f95aea96..4c2e5e62b 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -120,7 +120,7 @@ export default class Video extends React.PureComponent {
   setPlayerRef = c => {
     this.player = c;
 
-    if (c) {
+    if (c && c.offsetWidth && c.offsetWidth != this.state.containerWidth) {
       this.setState({
         containerWidth: c.offsetWidth,
       });
@@ -220,7 +220,7 @@ export default class Video extends React.PureComponent {
   }
 
   componentDidUpdate (prevProps) {
-    if (this.player && this.player.offsetWidth && !this.state.fullscreen) {
+    if (this.player && this.player.offsetWidth && this.player.offsetWidth != this.state.containerWidth && !this.state.fullscreen) {
       this.setState({
         containerWidth: this.player.offsetWidth,
       });
@@ -294,6 +294,8 @@ export default class Video extends React.PureComponent {
     const progress = (currentTime / duration) * 100;
     const playerStyle = {};
 
+    const computedClass = classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth });
+
     let { width, height } = this.props;
 
     if (inline && containerWidth) {
@@ -302,6 +304,8 @@ export default class Video extends React.PureComponent {
 
       playerStyle.width  = width;
       playerStyle.height = height;
+    } else if (inline) {
+      return (<div className={computedClass} ref={this.setPlayerRef} tabindex={0}></div>);
     }
 
     let warning;
@@ -322,7 +326,7 @@ export default class Video extends React.PureComponent {
 
     return (
       <div
-        className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth })}
+        className={computedClass}
         style={playerStyle}
         ref={this.setPlayerRef}
         onMouseEnter={this.handleMouseEnter}