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/features/account/components/header.js5
-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.js8
-rw-r--r--app/javascript/flavours/glitch/features/report/components/status_check_box.js3
-rw-r--r--app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js125
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/link_footer.js4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.js2
-rw-r--r--app/javascript/flavours/glitch/util/backend_links.js2
8 files changed, 150 insertions, 5 deletions
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js
index 53170b7a6..cc2a7d4d4 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.js
@@ -51,6 +51,7 @@ const messages = defineMessages({
   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}' },
   add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' },
+  languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
 });
 
 const dateFormatOptions = {
@@ -85,6 +86,7 @@ class Header extends ImmutablePureComponent {
     onEndorseToggle: PropTypes.func.isRequired,
     onAddToList: PropTypes.func.isRequired,
     onEditAccountNote: PropTypes.func.isRequired,
+    onChangeLanguages: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
     domain: PropTypes.string.isRequired,
     hidden: PropTypes.bool,
@@ -215,6 +217,9 @@ class Header extends ImmutablePureComponent {
           } else {
             menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
           }
+
+          menu.push({ text: intl.formatMessage(messages.languages), action: this.props.onChangeLanguages });
+          menu.push(null);
         }
 
         menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
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 645ff29ea..783d318f9 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 {
     onUnblockDomain: PropTypes.func.isRequired,
     onEndorseToggle: PropTypes.func.isRequired,
     onAddToList: PropTypes.func.isRequired,
+    onChangeLanguages: PropTypes.func.isRequired,
     hideTabs: PropTypes.bool,
     domain: PropTypes.string.isRequired,
     hidden: PropTypes.bool,
@@ -92,6 +93,10 @@ export default class Header extends ImmutablePureComponent {
     this.props.onEditAccountNote(this.props.account);
   }
 
+  handleChangeLanguages = () => {
+    this.props.onChangeLanguages(this.props.account);
+  }
+
   render () {
     const { account, hidden, hideTabs } = this.props;
 
@@ -118,6 +123,7 @@ export default class Header extends ImmutablePureComponent {
           onEndorseToggle={this.handleEndorseToggle}
           onAddToList={this.handleAddToList}
           onEditAccountNote={this.handleEditAccountNote}
+          onChangeLanguages={this.handleChangeLanguages}
           domain={this.props.domain}
           hidden={hidden}
         />
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 3fa7c1448..dc8b2d55a 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
@@ -130,12 +130,18 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(unblockDomain(domain));
   },
 
-  onAddToList(account){
+  onAddToList (account) {
     dispatch(openModal('LIST_ADDER', {
       accountId: account.get('id'),
     }));
   },
 
+  onChangeLanguages (account) {
+    dispatch(openModal('SUBSCRIBED_LANGUAGES', {
+      accountId: account.get('id'),
+    }));
+  },
+
 });
 
 export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
diff --git a/app/javascript/flavours/glitch/features/report/components/status_check_box.js b/app/javascript/flavours/glitch/features/report/components/status_check_box.js
index 76bf0eb85..2231fc0ce 100644
--- a/app/javascript/flavours/glitch/features/report/components/status_check_box.js
+++ b/app/javascript/flavours/glitch/features/report/components/status_check_box.js
@@ -7,6 +7,7 @@ import DisplayName from 'flavours/glitch/components/display_name';
 import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
 import Option from './option';
 import MediaAttachments from 'flavours/glitch/components/media_attachments';
+import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
 
 export default class StatusCheckBox extends React.PureComponent {
 
@@ -36,7 +37,7 @@ export default class StatusCheckBox extends React.PureComponent {
             <Avatar account={status.get('account')} size={46} />
           </div>
 
-          <div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div>
+          <div><DisplayName account={status.get('account')} /> · <VisibilityIcon visibility={status.get('visibility')} /><RelativeTimestamp timestamp={status.get('created_at')} /></div>
         </div>
 
         <StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} />
diff --git a/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js b/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js
new file mode 100644
index 000000000..3047b434c
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js
@@ -0,0 +1,125 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { connect } from 'react-redux';
+import { createSelector } from 'reselect';
+import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
+import { languages as preloadedLanguages } from 'flavours/glitch/util/initial_state';
+import Option from 'flavours/glitch/features/report/components/option';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
+import IconButton from 'flavours/glitch/components/icon_button';
+import Button from 'flavours/glitch/components/button';
+import { followAccount } from 'flavours/glitch/actions/accounts';
+
+const messages = defineMessages({
+  close: { id: 'lightbox.close', defaultMessage: 'Close' },
+});
+
+const getAccountLanguages = createSelector([
+  (state, accountId) => state.getIn(['timelines', `account:${accountId}`, 'items'], ImmutableList()),
+  state => state.get('statuses'),
+], (statusIds, statuses) =>
+  new ImmutableSet(statusIds.map(statusId => statuses.get(statusId)).filter(status => !status.get('reblog')).map(status => status.get('language'))));
+
+const mapStateToProps = (state, { accountId }) => ({
+  acct: state.getIn(['accounts', accountId, 'acct']),
+  availableLanguages: getAccountLanguages(state, accountId),
+  selectedLanguages: ImmutableSet(state.getIn(['relationships', accountId, 'languages']) || ImmutableList()),
+});
+
+const mapDispatchToProps = (dispatch, { accountId }) => ({
+
+  onSubmit (languages) {
+    dispatch(followAccount(accountId, { languages }));
+  },
+
+});
+
+export default @connect(mapStateToProps, mapDispatchToProps)
+@injectIntl
+class SubscribedLanguagesModal extends ImmutablePureComponent {
+
+  static propTypes = {
+    accountId: PropTypes.string.isRequired,
+    acct: PropTypes.string.isRequired,
+    availableLanguages: ImmutablePropTypes.setOf(PropTypes.string),
+    selectedLanguages: ImmutablePropTypes.setOf(PropTypes.string),
+    onClose: PropTypes.func.isRequired,
+    languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
+    intl: PropTypes.object.isRequired,
+    submit: PropTypes.func.isRequired,
+  };
+
+  static defaultProps = {
+    languages: preloadedLanguages,
+  };
+
+  state = {
+    selectedLanguages: this.props.selectedLanguages,
+  };
+
+  handleLanguageToggle = (value, checked) => {
+    const { selectedLanguages } = this.state;
+
+    if (checked) {
+      this.setState({ selectedLanguages: selectedLanguages.add(value) });
+    } else {
+      this.setState({ selectedLanguages: selectedLanguages.delete(value) });
+    }
+  };
+
+  handleSubmit = () => {
+    this.props.onSubmit(this.state.selectedLanguages.toArray());
+    this.props.onClose();
+  }
+
+  renderItem (value) {
+    const language = this.props.languages.find(language => language[0] === value);
+    const checked = this.state.selectedLanguages.includes(value);
+
+    if (!language) {
+      return null;
+    }
+
+    return (
+      <Option
+        key={value}
+        name='languages'
+        value={value}
+        label={language[1]}
+        checked={checked}
+        onToggle={this.handleLanguageToggle}
+        multiple
+      />
+    );
+  }
+
+  render () {
+    const { acct, availableLanguages, selectedLanguages, intl, onClose } = this.props;
+
+    return (
+      <div className='modal-root__modal report-dialog-modal'>
+        <div className='report-modal__target'>
+          <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
+          <FormattedMessage id='subscribed_languages.target' defaultMessage='Change subscribed languages for {target}' values={{ target: <strong>{acct}</strong> }} />
+        </div>
+
+        <div className='report-dialog-modal__container'>
+          <p className='report-dialog-modal__lead'><FormattedMessage id='subscribed_languages.lead' defaultMessage='Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.' /></p>
+
+          <div>
+            {availableLanguages.union(selectedLanguages).delete(null).map(value => this.renderItem(value))}
+          </div>
+
+          <div className='flex-spacer' />
+
+          <div className='report-dialog-modal__actions'>
+            <Button disabled={is(this.state.selectedLanguages, this.props.selectedLanguages)} onClick={this.handleSubmit}><FormattedMessage id='subscribed_languages.save' defaultMessage='Save changes' /></Button>
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js
index 3abdaad4b..d91f9b8c4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js
+++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import { Link } from 'react-router-dom';
 import { limitedFederationMode, version, repository, source_url } from 'flavours/glitch/util/initial_state';
-import { signOutLink, securityLink } from 'flavours/glitch/util/backend_links';
+import { signOutLink, securityLink, privacyPolicyLink } from 'flavours/glitch/util/backend_links';
 import { logOut } from 'flavours/glitch/util/log_out';
 import { openModal } from 'flavours/glitch/actions/modal';
 import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions';
@@ -55,7 +55,7 @@ class LinkFooter extends React.PureComponent {
           {!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>}
           {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
           <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
-          <li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
+          <li><a href={privacyPolicyLink} target='_blank'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></a> · </li>
           <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
           <li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
           <li><a href={signOutLink} onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
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 4df3a0dee..7e8082f99 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
@@ -13,6 +13,7 @@ import FavouriteModal from './favourite_modal';
 import AudioModal from './audio_modal';
 import DoodleModal from './doodle_modal';
 import ConfirmationModal from './confirmation_modal';
+import SubscribedLanguagesModal from 'flavours/glitch/features/subscribed_languages_modal';
 import FocalPointModal from './focal_point_modal';
 import DeprecatedSettingsModal from './deprecated_settings_modal';
 import {
@@ -51,6 +52,7 @@ const MODAL_COMPONENTS = {
   'PINNED_ACCOUNTS_EDITOR': PinnedAccountsEditor,
   'COMPARE_HISTORY': CompareHistoryModal,
   'FILTER': FilterModal,
+  'SUBSCRIBED_LANGUAGES': () => Promise.resolve({ default: SubscribedLanguagesModal }),
 };
 
 export default class ModalRoot extends React.PureComponent {
diff --git a/app/javascript/flavours/glitch/util/backend_links.js b/app/javascript/flavours/glitch/util/backend_links.js
index 5b2dd8dbf..d0ae63419 100644
--- a/app/javascript/flavours/glitch/util/backend_links.js
+++ b/app/javascript/flavours/glitch/util/backend_links.js
@@ -1,7 +1,7 @@
 export const preferencesLink = '/settings/preferences';
 export const profileLink = '/settings/profile';
 export const signOutLink = '/auth/sign_out';
-export const termsLink = '/terms';
+export const privacyPolicyLink = '/privacy-policy';
 export const accountAdminLink = (id) => `/admin/accounts/${id}`;
 export const statusAdminLink = (account_id, status_id) => `/admin/accounts/${account_id}/statuses?id=${status_id}`;
 export const filterEditLink = (id) => `/filters/${id}/edit`;