about summary refs log tree commit diff
path: root/app/javascript/mastodon/features/compose/containers
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/mastodon/features/compose/containers')
-rw-r--r--app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js15
-rw-r--r--app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js15
-rw-r--r--app/javascript/mastodon/features/compose/containers/compose_form_container.js64
-rw-r--r--app/javascript/mastodon/features/compose/containers/navigation_container.js10
-rw-r--r--app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js17
-rw-r--r--app/javascript/mastodon/features/compose/containers/reply_indicator_container.js24
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_container.js35
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_results_container.js8
-rw-r--r--app/javascript/mastodon/features/compose/containers/sensitive_button_container.js51
-rw-r--r--app/javascript/mastodon/features/compose/containers/spoiler_button_container.js25
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_button_container.js18
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_form_container.js17
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_progress_container.js9
-rw-r--r--app/javascript/mastodon/features/compose/containers/warning_container.js49
14 files changed, 357 insertions, 0 deletions
diff --git a/app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js b/app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js
new file mode 100644
index 000000000..de76a364d
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/autosuggest_account_container.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import AutosuggestAccount from '../components/autosuggest_account';
+import { makeGetAccount } from '../../../selectors';
+
+const makeMapStateToProps = () => {
+  const getAccount = makeGetAccount();
+
+  const mapStateToProps = (state, { id }) => ({
+    account: getAccount(state, id)
+  });
+
+  return mapStateToProps;
+};
+
+export default connect(makeMapStateToProps)(AutosuggestAccount);
diff --git a/app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js b/app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js
new file mode 100644
index 000000000..ef46eb09c
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/autosuggest_status_container.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import AutosuggestStatus from '../components/autosuggest_status';
+import { makeGetStatus } from '../../../selectors';
+
+const makeMapStateToProps = () => {
+  const getStatus = makeGetStatus();
+
+  const mapStateToProps = (state, { id }) => ({
+    status: getStatus(state, id)
+  });
+
+  return mapStateToProps;
+};
+
+export default connect(makeMapStateToProps)(AutosuggestStatus);
diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
new file mode 100644
index 000000000..892183b83
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
@@ -0,0 +1,64 @@
+import { connect } from 'react-redux';
+import ComposeForm from '../components/compose_form';
+import { uploadCompose } from '../../../actions/compose';
+import {
+  changeCompose,
+  submitCompose,
+  clearComposeSuggestions,
+  fetchComposeSuggestions,
+  selectComposeSuggestion,
+  changeComposeSpoilerText,
+  insertEmojiCompose
+} from '../../../actions/compose';
+
+const mapStateToProps = state => ({
+  text: state.getIn(['compose', 'text']),
+  suggestion_token: state.getIn(['compose', 'suggestion_token']),
+  suggestions: state.getIn(['compose', 'suggestions']),
+  spoiler: state.getIn(['compose', 'spoiler']),
+  spoiler_text: state.getIn(['compose', 'spoiler_text']),
+  privacy: state.getIn(['compose', 'privacy']),
+  focusDate: state.getIn(['compose', 'focusDate']),
+  preselectDate: state.getIn(['compose', 'preselectDate']),
+  is_submitting: state.getIn(['compose', 'is_submitting']),
+  is_uploading: state.getIn(['compose', 'is_uploading']),
+  me: state.getIn(['compose', 'me'])
+});
+
+const mapDispatchToProps = (dispatch) => ({
+
+  onChange (text) {
+    dispatch(changeCompose(text));
+  },
+
+  onSubmit () {
+    dispatch(submitCompose());
+  },
+
+  onClearSuggestions () {
+    dispatch(clearComposeSuggestions());
+  },
+
+  onFetchSuggestions (token) {
+    dispatch(fetchComposeSuggestions(token));
+  },
+
+  onSuggestionSelected (position, token, accountId) {
+    dispatch(selectComposeSuggestion(position, token, accountId));
+  },
+
+  onChangeSpoilerText (checked) {
+    dispatch(changeComposeSpoilerText(checked));
+  },
+
+  onPaste (files) {
+    dispatch(uploadCompose(files));
+  },
+
+  onPickEmoji (position, data) {
+    dispatch(insertEmojiCompose(position, data));
+  },
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
diff --git a/app/javascript/mastodon/features/compose/containers/navigation_container.js b/app/javascript/mastodon/features/compose/containers/navigation_container.js
new file mode 100644
index 000000000..0006608da
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/navigation_container.js
@@ -0,0 +1,10 @@
+import { connect }   from 'react-redux';
+import NavigationBar from '../components/navigation_bar';
+
+const mapStateToProps = (state, props) => {
+  return {
+    account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
+  };
+};
+
+export default connect(mapStateToProps)(NavigationBar);
diff --git a/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js
new file mode 100644
index 000000000..1eee8f84c
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/privacy_dropdown_container.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import PrivacyDropdown from '../components/privacy_dropdown';
+import { changeComposeVisibility } from '../../../actions/compose';
+
+const mapStateToProps = state => ({
+  value: state.getIn(['compose', 'privacy'])
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (value) {
+    dispatch(changeComposeVisibility(value));
+  }
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown);
diff --git a/app/javascript/mastodon/features/compose/containers/reply_indicator_container.js b/app/javascript/mastodon/features/compose/containers/reply_indicator_container.js
new file mode 100644
index 000000000..39b48f3b6
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/reply_indicator_container.js
@@ -0,0 +1,24 @@
+import { connect } from 'react-redux';
+import { cancelReplyCompose } from '../../../actions/compose';
+import { makeGetStatus } from '../../../selectors';
+import ReplyIndicator from '../components/reply_indicator';
+
+const makeMapStateToProps = () => {
+  const getStatus = makeGetStatus();
+
+  const mapStateToProps = (state, props) => ({
+    status: getStatus(state, state.getIn(['compose', 'in_reply_to'])),
+  });
+
+  return mapStateToProps;
+};
+
+const mapDispatchToProps = dispatch => ({
+
+  onCancel () {
+    dispatch(cancelReplyCompose());
+  }
+
+});
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(ReplyIndicator);
diff --git a/app/javascript/mastodon/features/compose/containers/search_container.js b/app/javascript/mastodon/features/compose/containers/search_container.js
new file mode 100644
index 000000000..906c0c28c
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/search_container.js
@@ -0,0 +1,35 @@
+import { connect } from 'react-redux';
+import {
+  changeSearch,
+  clearSearch,
+  submitSearch,
+  showSearch
+} from '../../../actions/search';
+import Search from '../components/search';
+
+const mapStateToProps = state => ({
+  value: state.getIn(['search', 'value']),
+  submitted: state.getIn(['search', 'submitted'])
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (value) {
+    dispatch(changeSearch(value));
+  },
+
+  onClear () {
+    dispatch(clearSearch());
+  },
+
+  onSubmit () {
+    dispatch(submitSearch());
+  },
+
+  onShow () {
+    dispatch(showSearch());
+  }
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Search);
diff --git a/app/javascript/mastodon/features/compose/containers/search_results_container.js b/app/javascript/mastodon/features/compose/containers/search_results_container.js
new file mode 100644
index 000000000..e5911fd38
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/search_results_container.js
@@ -0,0 +1,8 @@
+import { connect } from 'react-redux';
+import SearchResults from '../components/search_results';
+
+const mapStateToProps = state => ({
+  results: state.getIn(['search', 'results'])
+});
+
+export default connect(mapStateToProps)(SearchResults);
diff --git a/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
new file mode 100644
index 000000000..78e40e048
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import TextIconButton from '../components/text_icon_button';
+import { changeComposeSensitivity } from '../../../actions/compose';
+import { Motion, spring } from 'react-motion';
+import { injectIntl, defineMessages } from 'react-intl';
+
+const messages = defineMessages({
+  title: { id: 'compose_form.sensitive', defaultMessage: 'Mark media as sensitive' }
+});
+
+const mapStateToProps = state => ({
+  visible: state.getIn(['compose', 'media_attachments']).size > 0,
+  active: state.getIn(['compose', 'sensitive'])
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onClick () {
+    dispatch(changeComposeSensitivity());
+  }
+
+});
+
+class SensitiveButton extends React.PureComponent {
+
+  render () {
+    const { visible, active, onClick, intl } = this.props;
+
+    return (
+      <Motion defaultStyle={{ scale: 0.87 }} style={{ scale: spring(visible ? 1 : 0.87, { stiffness: 200, damping: 3 }) }}>
+        {({ scale }) =>
+          <div style={{ display: visible ? 'block' : 'none', transform: `translateZ(0) scale(${scale})` }}>
+            <TextIconButton onClick={onClick} label='NSFW' title={intl.formatMessage(messages.title)} active={active} />
+          </div>
+        }
+      </Motion>
+    );
+  }
+
+}
+
+SensitiveButton.propTypes = {
+  visible: PropTypes.bool,
+  active: PropTypes.bool,
+  onClick: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
diff --git a/app/javascript/mastodon/features/compose/containers/spoiler_button_container.js b/app/javascript/mastodon/features/compose/containers/spoiler_button_container.js
new file mode 100644
index 000000000..b1c80fe19
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/spoiler_button_container.js
@@ -0,0 +1,25 @@
+import { connect } from 'react-redux';
+import TextIconButton from '../components/text_icon_button';
+import { changeComposeSpoilerness } from '../../../actions/compose';
+import { injectIntl, defineMessages } from 'react-intl';
+
+const messages = defineMessages({
+  title: { id: 'compose_form.spoiler', defaultMessage: 'Hide text behind warning' }
+});
+
+const mapStateToProps = (state, { intl }) => ({
+  label: 'CW',
+  title: intl.formatMessage(messages.title),
+  active: state.getIn(['compose', 'spoiler']),
+  ariaControls: 'cw-spoiler-input'
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onClick () {
+    dispatch(changeComposeSpoilerness());
+  }
+
+});
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextIconButton));
diff --git a/app/javascript/mastodon/features/compose/containers/upload_button_container.js b/app/javascript/mastodon/features/compose/containers/upload_button_container.js
new file mode 100644
index 000000000..78e5312f5
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/upload_button_container.js
@@ -0,0 +1,18 @@
+import { connect } from 'react-redux';
+import UploadButton from '../components/upload_button';
+import { uploadCompose } from '../../../actions/compose';
+
+const mapStateToProps = state => ({
+  disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
+  resetFileKey: state.getIn(['compose', 'resetFileKey'])
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onSelectFile (files) {
+    dispatch(uploadCompose(files));
+  }
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(UploadButton);
diff --git a/app/javascript/mastodon/features/compose/containers/upload_form_container.js b/app/javascript/mastodon/features/compose/containers/upload_form_container.js
new file mode 100644
index 000000000..a6a202e17
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/upload_form_container.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import UploadForm from '../components/upload_form';
+import { undoUploadCompose } from '../../../actions/compose';
+
+const mapStateToProps = (state, props) => ({
+  media: state.getIn(['compose', 'media_attachments']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onRemoveFile (media_id) {
+    dispatch(undoUploadCompose(media_id));
+  }
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(UploadForm);
diff --git a/app/javascript/mastodon/features/compose/containers/upload_progress_container.js b/app/javascript/mastodon/features/compose/containers/upload_progress_container.js
new file mode 100644
index 000000000..b0f1d4d19
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/upload_progress_container.js
@@ -0,0 +1,9 @@
+import { connect } from 'react-redux';
+import UploadProgress from '../components/upload_progress';
+
+const mapStateToProps = (state, props) => ({
+  active: state.getIn(['compose', 'is_uploading']),
+  progress: state.getIn(['compose', 'progress'])
+});
+
+export default connect(mapStateToProps)(UploadProgress);
diff --git a/app/javascript/mastodon/features/compose/containers/warning_container.js b/app/javascript/mastodon/features/compose/containers/warning_container.js
new file mode 100644
index 000000000..bf5e6a5f8
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/containers/warning_container.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import Warning from '../components/warning';
+import { createSelector } from 'reselect';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+const getMentionedUsernames = createSelector(state => state.getIn(['compose', 'text']), text => text.match(/(?:^|[^\/\w])@([a-z0-9_]+@[a-z0-9\.\-]+)/ig));
+
+const getMentionedDomains = createSelector(getMentionedUsernames, mentionedUsernamesWithDomains => {
+  return mentionedUsernamesWithDomains !== null ? [...new Set(mentionedUsernamesWithDomains.map(item => item.split('@')[2]))] : [];
+});
+
+const mapStateToProps = state => {
+  const mentionedUsernames = getMentionedUsernames(state);
+  const mentionedUsernamesWithDomains = getMentionedDomains(state);
+
+  return {
+    needsLeakWarning: (state.getIn(['compose', 'privacy']) === 'private' || state.getIn(['compose', 'privacy']) === 'direct') && mentionedUsernames !== null,
+    mentionedDomains: mentionedUsernamesWithDomains,
+    needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', state.getIn(['meta', 'me']), 'locked'])
+  };
+};
+
+const WarningWrapper = ({ needsLeakWarning, needsLockWarning, mentionedDomains }) => {
+  if (needsLockWarning) {
+    return <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <a href='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></a> }} />} />;
+  } else if (needsLeakWarning) {
+    return (
+      <Warning
+        message={<FormattedMessage
+          id='compose_form.privacy_disclaimer'
+          defaultMessage='Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.'
+          values={{ domains: <strong>{mentionedDomains.join(', ')}</strong>, domainsCount: mentionedDomains.length }}
+        />}
+      />
+    );
+  }
+
+  return null;
+};
+
+WarningWrapper.propTypes = {
+  needsLeakWarning: PropTypes.bool,
+  needsLockWarning: PropTypes.bool,
+  mentionedDomains: PropTypes.array.isRequired,
+};
+
+export default connect(mapStateToProps)(WarningWrapper);