about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch/features')
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/action_bar.js66
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/navigation_bar.js14
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/navigation_container.js21
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js7
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js15
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/header.js31
6 files changed, 146 insertions, 8 deletions
diff --git a/app/javascript/flavours/glitch/features/compose/components/action_bar.js b/app/javascript/flavours/glitch/features/compose/components/action_bar.js
new file mode 100644
index 000000000..267c0ba69
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/compose/components/action_bar.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
+import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
+import { defineMessages, injectIntl } from 'react-intl';
+import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links';
+
+const messages = defineMessages({
+  edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
+  pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
+  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' },
+  filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
+  logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
+  bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
+});
+
+export default @injectIntl
+class ActionBar extends React.PureComponent {
+
+  static propTypes = {
+    account: ImmutablePropTypes.map.isRequired,
+    onLogout: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  handleLogout = () => {
+    this.props.onLogout();
+  }
+
+  render () {
+    const { intl } = this.props;
+
+    let menu = [];
+
+    menu.push({ text: intl.formatMessage(messages.edit_profile), href: profileLink });
+    menu.push({ text: intl.formatMessage(messages.preferences), href: preferencesLink });
+    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.bookmarks), to: '/bookmarks' });
+    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' });
+    menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
+    menu.push(null);
+    menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
+
+    return (
+      <div className='compose__action-bar'>
+        <div className='compose__action-bar-dropdown'>
+          <DropdownMenuContainer items={menu} icon='ellipsis-v' size={18} direction='right' />
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js
index ba73ed553..1a68f1e12 100644
--- a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js
+++ b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js
@@ -1,5 +1,7 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import ActionBar from './action_bar';
 import Avatar from 'flavours/glitch/components/avatar';
 import Permalink from 'flavours/glitch/components/permalink';
 import { FormattedMessage } from 'react-intl';
@@ -10,11 +12,12 @@ export default class NavigationBar extends ImmutablePureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
+    onLogout: PropTypes.func.isRequired,
   };
 
   render () {
     return (
-      <div className='drawer--account'>
+      <div className='navigation-bar'>
         <Permalink className='avatar' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}>
           <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
           <Avatar account={this.props.account} size={48} />
@@ -28,11 +31,16 @@ export default class NavigationBar extends ImmutablePureComponent {
           { profileLink !== undefined && (
             <a
               className='edit'
-              href={ profileLink }
+              href={profileLink}
             ><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
           )}
         </div>
+
+        <div className='navigation-bar__actions'>
+          <ActionBar account={this.props.account} onLogout={this.props.onLogout} />
+        </div>
       </div>
     );
-  };
+  }
+
 }
diff --git a/app/javascript/flavours/glitch/features/compose/containers/navigation_container.js b/app/javascript/flavours/glitch/features/compose/containers/navigation_container.js
index 0e1400261..89036adcd 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/navigation_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/navigation_container.js
@@ -1,11 +1,30 @@
 import { connect }   from 'react-redux';
+import { defineMessages, injectIntl } from 'react-intl';
 import NavigationBar from '../components/navigation_bar';
+import { logOut } from 'flavours/glitch/utils/log_out';
+import { openModal } from 'flavours/glitch/actions/modal';
 import { me } from 'flavours/glitch/initial_state';
 
+const messages = defineMessages({
+  logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
+  logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
+});
+
 const mapStateToProps = state => {
   return {
     account: state.getIn(['accounts', me]),
   };
 };
 
-export default connect(mapStateToProps)(NavigationBar);
+const mapDispatchToProps = (dispatch, { intl }) => ({
+  onLogout () {
+    dispatch(openModal('CONFIRM', {
+      message: intl.formatMessage(messages.logoutMessage),
+      confirm: intl.formatMessage(messages.logoutConfirm),
+      closeWhenConfirm: false,
+      onConfirm: () => logOut(),
+    }));
+  },
+});
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NavigationBar));
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 46770930f..7d2c2aace 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -34,6 +34,7 @@ class DetailedStatus extends ImmutablePureComponent {
     onOpenMedia: PropTypes.func.isRequired,
     onOpenVideo: PropTypes.func.isRequired,
     onToggleHidden: PropTypes.func,
+    onTranslate: PropTypes.func.isRequired,
     expanded: PropTypes.bool,
     measureHeight: PropTypes.bool,
     onHeightChange: PropTypes.func,
@@ -112,6 +113,11 @@ class DetailedStatus extends ImmutablePureComponent {
     window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
   }
 
+  handleTranslate = () => {
+    const { onTranslate, status } = this.props;
+    onTranslate(status);
+  }
+
   render () {
     const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
     const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props;
@@ -305,6 +311,7 @@ class DetailedStatus extends ImmutablePureComponent {
             expanded={expanded}
             collapsed={false}
             onExpandedToggle={onToggleHidden}
+            onTranslate={this.handleTranslate}
             parseClick={this.parseClick}
             onUpdate={this.handleChildUpdate}
             tagLinks={settings.get('tag_misleading_links')}
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index aaa9c7928..e190652b0 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -33,7 +33,9 @@ import {
   deleteStatus,
   editStatus,
   hideStatus,
-  revealStatus
+  revealStatus,
+  translateStatus,
+  undoStatusTranslation,
 } from 'flavours/glitch/actions/statuses';
 import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
@@ -437,6 +439,16 @@ class Status extends ImmutablePureComponent {
     this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded });
   }
 
+  handleTranslate = status => {
+    const { dispatch } = this.props;
+
+    if (status.get('translation')) {
+      dispatch(undoStatusTranslation(status.get('id')));
+    } else {
+      dispatch(translateStatus(status.get('id')));
+    }
+  }
+
   handleBlockClick = (status) => {
     const { dispatch } = this.props;
     const account = status.get('account');
@@ -666,6 +678,7 @@ class Status extends ImmutablePureComponent {
                   onOpenMedia={this.handleOpenMedia}
                   expanded={isExpanded}
                   onToggleHidden={this.handleToggleHidden}
+                  onTranslate={this.handleTranslate}
                   domain={domain}
                   showMedia={this.state.showMedia}
                   onToggleMediaVisibility={this.handleToggleMediaVisibility}
diff --git a/app/javascript/flavours/glitch/features/ui/components/header.js b/app/javascript/flavours/glitch/features/ui/components/header.js
index 891f7fc07..d9ad94961 100644
--- a/app/javascript/flavours/glitch/features/ui/components/header.js
+++ b/app/javascript/flavours/glitch/features/ui/components/header.js
@@ -7,6 +7,7 @@ import Avatar from 'flavours/glitch/components/avatar';
 import Permalink from 'flavours/glitch/components/permalink';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
+import { openModal } from 'flavours/glitch/actions/modal';
 
 const Account = connect(state => ({
   account: state.getIn(['accounts', me]),
@@ -16,7 +17,14 @@ const Account = connect(state => ({
   </Permalink>
 ));
 
-export default @withRouter
+const mapDispatchToProps = (dispatch) => ({
+  openClosedRegistrationsModal() {
+    dispatch(openModal('CLOSED_REGISTRATIONS'));
+  },
+});
+
+export default @connect(null, mapDispatchToProps)
+@withRouter
 class Header extends React.PureComponent {
 
   static contextTypes = {
@@ -24,12 +32,13 @@ class Header extends React.PureComponent {
   };
 
   static propTypes = {
+    openClosedRegistrationsModal: PropTypes.func,
     location: PropTypes.object,
   };
 
   render () {
     const { signedIn } = this.context.identity;
-    const { location } = this.props;
+    const { location, openClosedRegistrationsModal } = this.props;
 
     let content;
 
@@ -41,10 +50,26 @@ class Header extends React.PureComponent {
         </>
       );
     } else {
+      let signupButton;
+
+      if (registrationsOpen) {
+        signupButton = (
+          <a href='/auth/sign_up' className='button button-tertiary'>
+            <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
+          </a>
+        );
+      } else {
+        signupButton = (
+          <button className='button button-tertiary' onClick={openClosedRegistrationsModal}>
+            <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
+          </button>
+        );
+      }
+
       content = (
         <>
           <a href='/auth/sign_in' className='button'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
-          <a href={registrationsOpen ? '/auth/sign_up' : 'https://joinmastodon.org/servers'} className='button button-tertiary'><FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /></a>
+          {signupButton}
         </>
       );
     }