about summary refs log tree commit diff
path: root/app/javascript/themes/glitch/features/compose/containers
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/themes/glitch/features/compose/containers')
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js20
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js15
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/compose_form_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js82
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/navigation_container.js11
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js24
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_container.js35
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/search_results_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js71
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js25
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_button_container.js18
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_container.js21
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_form_container.js8
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js9
-rw-r--r--app/javascript/themes/glitch/features/compose/containers/warning_container.js24
16 files changed, 466 insertions, 0 deletions
diff --git a/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js b/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
new file mode 100644
index 000000000..9f168942a
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/advanced_options_container.js
@@ -0,0 +1,20 @@
+//  Package imports.
+import { connect } from 'react-redux';
+
+//  Our imports.
+import { toggleComposeAdvancedOption } from 'themes/glitch/actions/compose';
+import ComposeAdvancedOptions from '../components/advanced_options';
+
+const mapStateToProps = state => ({
+  values: state.getIn(['compose', 'advanced_options']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (option) {
+    dispatch(toggleComposeAdvancedOption(option));
+  },
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ComposeAdvancedOptions);
diff --git a/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js b/app/javascript/themes/glitch/features/compose/containers/autosuggest_account_container.js
new file mode 100644
index 000000000..96eb70c18
--- /dev/null
+++ b/app/javascript/themes/glitch/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 'themes/glitch/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/themes/glitch/features/compose/containers/compose_form_container.js b/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
new file mode 100644
index 000000000..7afa988f1
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/compose_form_container.js
@@ -0,0 +1,71 @@
+import { connect } from 'react-redux';
+import ComposeForm from '../components/compose_form';
+import { changeComposeVisibility, uploadCompose } from 'themes/glitch/actions/compose';
+import {
+  changeCompose,
+  submitCompose,
+  clearComposeSuggestions,
+  fetchComposeSuggestions,
+  selectComposeSuggestion,
+  changeComposeSpoilerText,
+  insertEmojiCompose,
+} from 'themes/glitch/actions/compose';
+
+const mapStateToProps = state => ({
+  text: state.getIn(['compose', 'text']),
+  suggestion_token: state.getIn(['compose', 'suggestion_token']),
+  suggestions: state.getIn(['compose', 'suggestions']),
+  advanced_options: state.getIn(['compose', 'advanced_options']),
+  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']),
+  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
+  settings: state.get('local_settings'),
+  filesAttached: state.getIn(['compose', 'media_attachments']).size > 0,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+
+  onChange (text) {
+    dispatch(changeCompose(text));
+  },
+
+  onPrivacyChange (value) {
+    dispatch(changeComposeVisibility(value));
+  },
+
+  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/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
new file mode 100644
index 000000000..55a13bd65
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/emoji_picker_dropdown_container.js
@@ -0,0 +1,82 @@
+import { connect } from 'react-redux';
+import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
+import { changeSetting } from 'themes/glitch/actions/settings';
+import { createSelector } from 'reselect';
+import { Map as ImmutableMap } from 'immutable';
+import { useEmoji } from 'themes/glitch/actions/emojis';
+
+const perLine = 8;
+const lines   = 2;
+
+const DEFAULTS = [
+  '+1',
+  'grinning',
+  'kissing_heart',
+  'heart_eyes',
+  'laughing',
+  'stuck_out_tongue_winking_eye',
+  'sweat_smile',
+  'joy',
+  'yum',
+  'disappointed',
+  'thinking_face',
+  'weary',
+  'sob',
+  'sunglasses',
+  'heart',
+  'ok_hand',
+];
+
+const getFrequentlyUsedEmojis = createSelector([
+  state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()),
+], emojiCounters => {
+  let emojis = emojiCounters
+    .keySeq()
+    .sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b))
+    .reverse()
+    .slice(0, perLine * lines)
+    .toArray();
+
+  if (emojis.length < DEFAULTS.length) {
+    emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length));
+  }
+
+  return emojis;
+});
+
+const getCustomEmojis = createSelector([
+  state => state.get('custom_emojis'),
+], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => {
+  const aShort = a.get('shortcode').toLowerCase();
+  const bShort = b.get('shortcode').toLowerCase();
+
+  if (aShort < bShort) {
+    return -1;
+  } else if (aShort > bShort ) {
+    return 1;
+  } else {
+    return 0;
+  }
+}));
+
+const mapStateToProps = state => ({
+  custom_emojis: getCustomEmojis(state),
+  skinTone: state.getIn(['settings', 'skinTone']),
+  frequentlyUsedEmojis: getFrequentlyUsedEmojis(state),
+});
+
+const mapDispatchToProps = (dispatch, { onPickEmoji }) => ({
+  onSkinTone: skinTone => {
+    dispatch(changeSetting(['skinTone'], skinTone));
+  },
+
+  onPickEmoji: emoji => {
+    dispatch(useEmoji(emoji));
+
+    if (onPickEmoji) {
+      onPickEmoji(emoji);
+    }
+  },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(EmojiPickerDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/navigation_container.js b/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
new file mode 100644
index 000000000..b6d737b46
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/navigation_container.js
@@ -0,0 +1,11 @@
+import { connect }   from 'react-redux';
+import NavigationBar from '../components/navigation_bar';
+import { me } from 'themes/glitch/util/initial_state';
+
+const mapStateToProps = state => {
+  return {
+    account: state.getIn(['accounts', me]),
+  };
+};
+
+export default connect(mapStateToProps)(NavigationBar);
diff --git a/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js b/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
new file mode 100644
index 000000000..9636ceab2
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/privacy_dropdown_container.js
@@ -0,0 +1,24 @@
+import { connect } from 'react-redux';
+import PrivacyDropdown from '../components/privacy_dropdown';
+import { changeComposeVisibility } from 'themes/glitch/actions/compose';
+import { openModal, closeModal } from 'themes/glitch/actions/modal';
+import { isUserTouching } from 'themes/glitch/util/is_mobile';
+
+const mapStateToProps = state => ({
+  isModalOpen: state.get('modal').modalType === 'ACTIONS',
+  value: state.getIn(['compose', 'privacy']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onChange (value) {
+    dispatch(changeComposeVisibility(value));
+  },
+
+  isUserTouching,
+  onModalOpen: props => dispatch(openModal('ACTIONS', props)),
+  onModalClose: () => dispatch(closeModal()),
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(PrivacyDropdown);
diff --git a/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js b/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
new file mode 100644
index 000000000..6dcabb3cd
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/reply_indicator_container.js
@@ -0,0 +1,24 @@
+import { connect } from 'react-redux';
+import { cancelReplyCompose } from 'themes/glitch/actions/compose';
+import { makeGetStatus } from 'themes/glitch/selectors';
+import ReplyIndicator from '../components/reply_indicator';
+
+const makeMapStateToProps = () => {
+  const getStatus = makeGetStatus();
+
+  const mapStateToProps = state => ({
+    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/themes/glitch/features/compose/containers/search_container.js b/app/javascript/themes/glitch/features/compose/containers/search_container.js
new file mode 100644
index 000000000..a450d27e7
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/search_container.js
@@ -0,0 +1,35 @@
+import { connect } from 'react-redux';
+import {
+  changeSearch,
+  clearSearch,
+  submitSearch,
+  showSearch,
+} from 'themes/glitch/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/themes/glitch/features/compose/containers/search_results_container.js b/app/javascript/themes/glitch/features/compose/containers/search_results_container.js
new file mode 100644
index 000000000..16d95d417
--- /dev/null
+++ b/app/javascript/themes/glitch/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/themes/glitch/features/compose/containers/sensitive_button_container.js b/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
new file mode 100644
index 000000000..a710dd104
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/sensitive_button_container.js
@@ -0,0 +1,71 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import IconButton from 'themes/glitch/components/icon_button';
+import { changeComposeSensitivity } from 'themes/glitch/actions/compose';
+import Motion from 'themes/glitch/util/optional_motion';
+import spring from 'react-motion/lib/spring';
+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']),
+  disabled: state.getIn(['compose', 'spoiler']),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onClick () {
+    dispatch(changeComposeSensitivity());
+  },
+
+});
+
+class SensitiveButton extends React.PureComponent {
+
+  static propTypes = {
+    visible: PropTypes.bool,
+    active: PropTypes.bool,
+    disabled: PropTypes.bool,
+    onClick: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  render () {
+    const { visible, active, disabled, onClick, intl } = this.props;
+
+    return (
+      <Motion defaultStyle={{ scale: 0.87 }} style={{ scale: spring(visible ? 1 : 0.87, { stiffness: 200, damping: 3 }) }}>
+        {({ scale }) => {
+          const icon = active ? 'eye-slash' : 'eye';
+          const className = classNames('compose-form__sensitive-button', {
+            'compose-form__sensitive-button--visible': visible,
+          });
+          return (
+            <div className={className} style={{ transform: `scale(${scale})` }}>
+              <IconButton
+                className='compose-form__sensitive-button__icon'
+                title={intl.formatMessage(messages.title)}
+                icon={icon}
+                onClick={onClick}
+                size={18}
+                active={active}
+                disabled={disabled}
+                style={{ lineHeight: null, height: null }}
+                inverted
+              />
+            </div>
+          );
+        }}
+      </Motion>
+    );
+  }
+
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
diff --git a/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js b/app/javascript/themes/glitch/features/compose/containers/spoiler_button_container.js
new file mode 100644
index 000000000..160e71ba9
--- /dev/null
+++ b/app/javascript/themes/glitch/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 'themes/glitch/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/themes/glitch/features/compose/containers/upload_button_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_button_container.js
new file mode 100644
index 000000000..f332eae1a
--- /dev/null
+++ b/app/javascript/themes/glitch/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 'themes/glitch/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/themes/glitch/features/compose/containers/upload_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_container.js
new file mode 100644
index 000000000..eea514bf5
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/upload_container.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux';
+import Upload from '../components/upload';
+import { undoUploadCompose, changeUploadCompose } from 'themes/glitch/actions/compose';
+
+const mapStateToProps = (state, { id }) => ({
+  media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
+});
+
+const mapDispatchToProps = dispatch => ({
+
+  onUndo: id => {
+    dispatch(undoUploadCompose(id));
+  },
+
+  onDescriptionChange: (id, description) => {
+    dispatch(changeUploadCompose(id, description));
+  },
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Upload);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
new file mode 100644
index 000000000..a6798bf51
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/upload_form_container.js
@@ -0,0 +1,8 @@
+import { connect } from 'react-redux';
+import UploadForm from '../components/upload_form';
+
+const mapStateToProps = state => ({
+  mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
+});
+
+export default connect(mapStateToProps)(UploadForm);
diff --git a/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js b/app/javascript/themes/glitch/features/compose/containers/upload_progress_container.js
new file mode 100644
index 000000000..0cfee96da
--- /dev/null
+++ b/app/javascript/themes/glitch/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 => ({
+  active: state.getIn(['compose', 'is_uploading']),
+  progress: state.getIn(['compose', 'progress']),
+});
+
+export default connect(mapStateToProps)(UploadProgress);
diff --git a/app/javascript/themes/glitch/features/compose/containers/warning_container.js b/app/javascript/themes/glitch/features/compose/containers/warning_container.js
new file mode 100644
index 000000000..225d6a1dd
--- /dev/null
+++ b/app/javascript/themes/glitch/features/compose/containers/warning_container.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import Warning from '../components/warning';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+import { me } from 'themes/glitch/util/initial_state';
+
+const mapStateToProps = state => ({
+  needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
+});
+
+const WarningWrapper = ({ needsLockWarning }) => {
+  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> }} />} />;
+  }
+
+  return null;
+};
+
+WarningWrapper.propTypes = {
+  needsLockWarning: PropTypes.bool,
+};
+
+export default connect(mapStateToProps)(WarningWrapper);