about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features/composer
diff options
context:
space:
mode:
authorpluralcafe-docker <docker@plural.cafe>2018-08-20 02:29:25 +0000
committerpluralcafe-docker <docker@plural.cafe>2018-08-20 02:29:25 +0000
commitc339d49d82b3fd2873831029d9c92b3fbf1236e3 (patch)
tree5a8528475f089f1e592dfbedd37ee91e6f748ffc /app/javascript/flavours/glitch/features/composer
parentb6e9537de469a178ce195b75fe8eaf03735e785a (diff)
parentf4d28ccfa3f9fd38a6d219fcde2d1a8bfcf6b306 (diff)
Merge branch 'glitch'
Diffstat (limited to 'app/javascript/flavours/glitch/features/composer')
-rw-r--r--app/javascript/flavours/glitch/features/composer/index.js110
-rw-r--r--app/javascript/flavours/glitch/features/composer/spoiler/index.js7
-rw-r--r--app/javascript/flavours/glitch/features/composer/upload_form/index.js9
-rw-r--r--app/javascript/flavours/glitch/features/composer/upload_form/item/index.js57
4 files changed, 142 insertions, 41 deletions
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js
index 2678ffd53..f312e9d59 100644
--- a/app/javascript/flavours/glitch/features/composer/index.js
+++ b/app/javascript/flavours/glitch/features/composer/index.js
@@ -95,28 +95,71 @@ function mapStateToProps (state) {
 };
 
 //  Dispatch mapping.
-const mapDispatchToProps = {
-  onCancelReply: cancelReplyCompose,
-  onChangeAdvancedOption: changeComposeAdvancedOption,
-  onChangeDescription: changeUploadCompose,
-  onChangeSensitivity: changeComposeSensitivity,
-  onChangeSpoilerText: changeComposeSpoilerText,
-  onChangeSpoilerness: changeComposeSpoilerness,
-  onChangeText: changeCompose,
-  onChangeVisibility: changeComposeVisibility,
-  onClearSuggestions: clearComposeSuggestions,
-  onCloseModal: closeModal,
-  onFetchSuggestions: fetchComposeSuggestions,
-  onInsertEmoji: insertEmojiCompose,
-  onMount: mountCompose,
-  onOpenActionsModal: openModal.bind(null, 'ACTIONS'),
-  onOpenDoodleModal: openModal.bind(null, 'DOODLE', { noEsc: true }),
-  onSelectSuggestion: selectComposeSuggestion,
-  onSubmit: submitCompose,
-  onUndoUpload: undoUploadCompose,
-  onUnmount: unmountCompose,
-  onUpload: uploadCompose,
-};
+const mapDispatchToProps = (dispatch) => ({
+  onCancelReply() {
+    dispatch(cancelReplyCompose());
+  },
+  onChangeAdvancedOption(option, value) {
+    dispatch(changeComposeAdvancedOption(option, value));
+  },
+  onChangeDescription(id, description) {
+    dispatch(changeUploadCompose(id, { description }));
+  },
+  onChangeSensitivity() {
+    dispatch(changeComposeSensitivity());
+  },
+  onChangeSpoilerText(text) {
+    dispatch(changeComposeSpoilerText(text));
+  },
+  onChangeSpoilerness() {
+    dispatch(changeComposeSpoilerness());
+  },
+  onChangeText(text) {
+    dispatch(changeCompose(text));
+  },
+  onChangeVisibility(value) {
+    dispatch(changeComposeVisibility(value));
+  },
+  onClearSuggestions() {
+    dispatch(clearComposeSuggestions());
+  },
+  onCloseModal() {
+    dispatch(closeModal());
+  },
+  onFetchSuggestions(token) {
+    dispatch(fetchComposeSuggestions(token));
+  },
+  onInsertEmoji(position, emoji) {
+    dispatch(insertEmojiCompose(position, emoji));
+  },
+  onMount() {
+    dispatch(mountCompose());
+  },
+  onOpenActionModal(props) {
+    dispatch(openModal('ACTIONS', props));
+  },
+  onOpenDoodleModal() {
+    dispatch(openModal('DOODLE', { noEsc: true }));
+  },
+  onOpenFocalPointModal(id) {
+    dispatch(openModal('FOCAL_POINT', { id }));
+  },
+  onSelectSuggestion(position, token, suggestion) {
+    dispatch(selectComposeSuggestion(position, token, suggestion));
+  },
+  onSubmit() {
+    dispatch(submitCompose());
+  },
+  onUndoUpload(id) {
+    dispatch(undoUploadCompose(id));
+  },
+  onUnmount() {
+    dispatch(unmountCompose());
+  },
+  onUpload(files) {
+    dispatch(uploadCompose(files));
+  },
+});
 
 //  Handlers.
 const handlers = {
@@ -194,6 +237,13 @@ const handlers = {
       this.textarea = textareaComponent.textarea;
     }
   },
+
+  //  Sets a reference to the CW field.
+  handleRefSpoilerText (spoilerComponent) {
+    if (spoilerComponent) {
+      this.spoilerText = spoilerComponent.spoilerText;
+    }
+  }
 };
 
 //  The component.
@@ -206,6 +256,7 @@ class Composer extends React.Component {
 
     //  Instance variables.
     this.textarea = null;
+    this.spoilerText = null;
   }
 
   //  Tells our state the composer has been mounted.
@@ -234,6 +285,7 @@ class Composer extends React.Component {
   componentDidUpdate (prevProps) {
     const {
       textarea,
+      spoilerText,
     } = this;
     const {
       focusDate,
@@ -265,6 +317,16 @@ class Composer extends React.Component {
     //  Refocuses the textarea after submitting.
     } else if (textarea && prevProps.isSubmitting && !isSubmitting) {
       textarea.focus();
+    } else if (this.props.spoiler !== prevProps.spoiler) {
+      if (this.props.spoiler) {
+        if (spoilerText) {
+          spoilerText.focus();
+        }
+      } else {
+        if (textarea) {
+          textarea.focus();
+        }
+      }
     }
   }
 
@@ -276,6 +338,7 @@ class Composer extends React.Component {
       handleSelect,
       handleSubmit,
       handleRefTextarea,
+      handleRefSpoilerText,
     } = this.handlers;
     const {
       acceptContentTypes,
@@ -299,6 +362,7 @@ class Composer extends React.Component {
       onFetchSuggestions,
       onOpenActionsModal,
       onOpenDoodleModal,
+      onOpenFocalPointModal,
       onUndoUpload,
       onUpload,
       privacy,
@@ -334,6 +398,7 @@ class Composer extends React.Component {
           onChange={handleChangeSpoiler}
           onSubmit={handleSubmit}
           text={spoilerText}
+          ref={handleRefSpoilerText}
         />
         <ComposerTextarea
           advancedOptions={advancedOptions}
@@ -357,6 +422,7 @@ class Composer extends React.Component {
             intl={intl}
             media={media}
             onChangeDescription={onChangeDescription}
+            onOpenFocalPointModal={onOpenFocalPointModal}
             onRemove={onUndoUpload}
             progress={progress}
             uploading={isUploading}
diff --git a/app/javascript/flavours/glitch/features/composer/spoiler/index.js b/app/javascript/flavours/glitch/features/composer/spoiler/index.js
index d0e74b957..a7fecbcf5 100644
--- a/app/javascript/flavours/glitch/features/composer/spoiler/index.js
+++ b/app/javascript/flavours/glitch/features/composer/spoiler/index.js
@@ -33,6 +33,10 @@ const handlers = {
       onSubmit();
     }
   },
+
+  handleRefSpoilerText (spoilerText) {
+    this.spoilerText = spoilerText;
+  },
 };
 
 //  The component.
@@ -46,7 +50,7 @@ export default class ComposerSpoiler extends React.PureComponent {
 
   //  Rendering.
   render () {
-    const { handleKeyDown } = this.handlers;
+    const { handleKeyDown, handleRefSpoilerText } = this.handlers;
     const {
       hidden,
       intl,
@@ -68,6 +72,7 @@ export default class ComposerSpoiler extends React.PureComponent {
             placeholder={intl.formatMessage(messages.placeholder)}
             type='text'
             value={text}
+            ref={handleRefSpoilerText}
           />
         </label>
       </div>
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/index.js
index 53b14acc7..f3cadc2f5 100644
--- a/app/javascript/flavours/glitch/features/composer/upload_form/index.js
+++ b/app/javascript/flavours/glitch/features/composer/upload_form/index.js
@@ -13,6 +13,7 @@ export default function ComposerUploadForm ({
   intl,
   media,
   onChangeDescription,
+  onOpenFocalPointModal,
   onRemove,
   progress,
   uploading,
@@ -31,8 +32,12 @@ export default function ComposerUploadForm ({
               key={item.get('id')}
               id={item.get('id')}
               intl={intl}
+              focusX={item.getIn(['meta', 'focus', 'x'])}
+              focusY={item.getIn(['meta', 'focus', 'y'])}
+              mediaType={item.get('type')}
               preview={item.get('preview_url')}
               onChangeDescription={onChangeDescription}
+              onOpenFocalPointModal={onOpenFocalPointModal}
               onRemove={onRemove}
             />
           ))}
@@ -46,8 +51,8 @@ export default function ComposerUploadForm ({
 ComposerUploadForm.propTypes = {
   intl: PropTypes.object.isRequired,
   media: ImmutablePropTypes.list,
-  onChangeDescription: PropTypes.func,
-  onRemove: PropTypes.func,
+  onChangeDescription: PropTypes.func.isRequired,
+  onRemove: PropTypes.func.isRequired,
   progress: PropTypes.number,
   uploading: PropTypes.bool,
 };
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
index ec67b8ef8..5addccfb1 100644
--- a/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
+++ b/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
@@ -25,6 +25,10 @@ const messages = defineMessages({
     defaultMessage: 'Describe for the visually impaired',
     id: 'upload_form.description',
   },
+  crop: {
+    defaultMessage: 'Crop',
+    id: 'upload_form.focus',
+  },
 });
 
 //  Handlers.
@@ -37,11 +41,10 @@ const handlers = {
       onChangeDescription,
     } = this.props;
     const { dirtyDescription } = this.state;
+
+    this.setState({ dirtyDescription: null, focused: false });
+
     if (id && onChangeDescription && dirtyDescription !== null) {
-      this.setState({
-        dirtyDescription: null,
-        focused: false,
-      });
       onChangeDescription(id, dirtyDescription);
     }
   },
@@ -77,6 +80,17 @@ const handlers = {
       onRemove(id);
     }
   },
+
+  //  Opens the focal point modal.
+  handleFocalPointClick () {
+    const {
+      id,
+      onOpenFocalPointModal,
+    } = this.props;
+    if (id && onOpenFocalPointModal) {
+      onOpenFocalPointModal(id);
+    }
+  },
 };
 
 //  The component.
@@ -102,18 +116,25 @@ export default class ComposerUploadFormItem extends React.PureComponent {
       handleMouseEnter,
       handleMouseLeave,
       handleRemove,
+      handleFocalPointClick,
     } = this.handlers;
     const {
-      description,
       intl,
       preview,
+      focusX,
+      focusY,
+      mediaType,
     } = this.props;
     const {
       focused,
       hovered,
       dirtyDescription,
     } = this.state;
-    const computedClass = classNames('composer--upload_form--item', { active: hovered || focused });
+    const active = hovered || focused;
+    const computedClass = classNames('composer--upload_form--item', { active });
+    const x = ((focusX /  2) + .5) * 100;
+    const y = ((focusY / -2) + .5) * 100;
+    const description = dirtyDescription || (dirtyDescription !== '' && this.props.description) || '';
 
     //  The result.
     return (
@@ -136,15 +157,15 @@ export default class ComposerUploadFormItem extends React.PureComponent {
               style={{
                 transform: `scale(${scale})`,
                 backgroundImage: preview ? `url(${preview})` : null,
+                backgroundPosition: `${x}% ${y}%`
               }}
             >
-              <IconButton
-                className='close'
-                icon='times'
-                onClick={handleRemove}
-                size={36}
-                title={intl.formatMessage(messages.undo)}
-              />
+              <div className={classNames('composer--upload_form--actions', { active })}>
+                <button className='icon-button' onClick={handleRemove}>
+                  <i className='fa fa-times' /> <FormattedMessage {...messages.undo} />
+                </button>
+                {mediaType === 'image' && <button className='icon-button' onClick={handleFocalPointClick}><i className='fa fa-crosshairs' /> <FormattedMessage {...messages.crop} /></button>}
+              </div>
               <label>
                 <span style={{ display: 'none' }}><FormattedMessage {...messages.description} /></span>
                 <input
@@ -154,7 +175,7 @@ export default class ComposerUploadFormItem extends React.PureComponent {
                   onFocus={handleFocus}
                   placeholder={intl.formatMessage(messages.description)}
                   type='text'
-                  value={dirtyDescription || description || ''}
+                  value={description}
                 />
               </label>
             </div>
@@ -171,7 +192,11 @@ ComposerUploadFormItem.propTypes = {
   description: PropTypes.string,
   id: PropTypes.string,
   intl: PropTypes.object.isRequired,
-  onChangeDescription: PropTypes.func,
-  onRemove: PropTypes.func,
+  onChangeDescription: PropTypes.func.isRequired,
+  onOpenFocalPointModal: PropTypes.func.isRequired,
+  onRemove: PropTypes.func.isRequired,
+  focusX: PropTypes.number,
+  focusY: PropTypes.number,
+  mediaType: PropTypes.string,
   preview: PropTypes.string,
 };