about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features/composer/upload_form
diff options
context:
space:
mode:
authorkibigo! <marrus-sh@users.noreply.github.com>2017-12-23 22:16:45 -0800
committerkibigo! <marrus-sh@users.noreply.github.com>2018-01-04 18:21:59 -0800
commit924ffe81d477a8cf890c8117efb94b908760bccc (patch)
treeacefef7362929f4495424fbb037c3be59cca318f /app/javascript/flavours/glitch/features/composer/upload_form
parentfc884d015a1a2d6c31976af3d63039390fa15939 (diff)
WIPgit status <Compose> Refactor; <Composer> ed.
Diffstat (limited to 'app/javascript/flavours/glitch/features/composer/upload_form')
-rw-r--r--app/javascript/flavours/glitch/features/composer/upload_form/index.js54
-rw-r--r--app/javascript/flavours/glitch/features/composer/upload_form/item/index.js176
-rw-r--r--app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js52
3 files changed, 282 insertions, 0 deletions
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/index.js
new file mode 100644
index 000000000..ab46a3046
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/composer/upload_form/index.js
@@ -0,0 +1,54 @@
+//  Package imports.
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+
+//  Components.
+import ComposerUploadFormItem from './item';
+import ComposerUploadFormProgress from './progress';
+
+//  The component.
+export default function ComposerUploadForm ({
+  active,
+  intl,
+  media,
+  onChangeDescription,
+  onRemove,
+  progress,
+}) {
+  const computedClass = classNames('composer--upload_form', { uploading: active });
+
+  //  We need `media` in order to be able to render.
+  if (!media) {
+    return null;
+  }
+
+  //  The result.
+  return (
+    <div className={computedClass}>
+      {active ? <ComposerUploadFormProgress progress={progress} /> : null}
+      {media.map(item => (
+        <ComposerUploadFormItem
+          description={item.get('description')}
+          key={item.get('id')}
+          id={item.get('id')}
+          intl={intl}
+          preview={item.get('preview_url')}
+          onChangeDescription={onChangeDescription}
+          onRemove={onRemove}
+        />
+      ))}
+    </div>
+  );
+}
+
+//  Props.
+ComposerUploadForm.propTypes = {
+  active: PropTypes.bool,
+  intl: PropTypes.object.isRequired,
+  media: ImmutablePropTypes.list,
+  onChangeDescription: PropTypes.func,
+  onRemove: PropTypes.func,
+  progress: PropTypes.number,
+};
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
new file mode 100644
index 000000000..bd67e7227
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
@@ -0,0 +1,176 @@
+//  Package imports.
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {
+  FormattedMessage,
+  defineMessages,
+} from 'react-intl';
+import spring from 'react-motion/lib/spring';
+
+//  Components.
+import IconButton from 'flavours/glitch/components/icon_button';
+
+//  Utils.
+import Motion from 'flavours/glitch/util/optional_motion';
+import { assignHandlers } from 'flavours/glitch/util/react_helpers';
+
+//  Messages.
+const messages = defineMessages({
+  undo: {
+    defaultMessage: 'Undo',
+    id: 'upload_form.undo',
+  },
+  description: {
+    defaultMessage: 'Describe for the visually impaired',
+    id: 'upload_form.description',
+  },
+});
+
+//  Handlers.
+const handlers = {
+
+  //  On blur, we save the description for the media item.
+  blur () {
+    const {
+      id,
+      onChangeDescription,
+    } = this.props;
+    const { dirtyDescription } = this.state;
+    if (id && onChangeDescription && dirtyDescription !== null) {
+      this.setState({
+        dirtyDescription: null,
+        focused: false,
+      });
+      onChangeDescription(id, dirtyDescription);
+    }
+  },
+
+  //  When the value of our description changes, we store it in the
+  //  temp value `dirtyDescription` in our state.
+  change ({ target: { value } }) {
+    this.setState({ dirtyDescription: value });
+  },
+
+  //  Records focus on the media item.
+  focus () {
+    this.setState({ focused: true });
+  },
+
+  //  Records the start of a hover over the media item.
+  mouseEnter () {
+    this.setState({ hovered: true });
+  },
+
+  //  Records the end of a hover over the media item.
+  mouseLeave () {
+    this.setState({ hovered: false });
+  },
+
+  //  Removes the media item.
+  remove () {
+    const {
+      id,
+      onRemove,
+    } = this.props;
+    if (id && onRemove) {
+      onRemove(id);
+    }
+  },
+};
+
+//  The component.
+export default class ComposerUploadFormItem extends React.PureComponent {
+
+  //  Constructor.
+  constructor (props) {
+    super(props);
+    assignHandlers(handlers);
+    this.state = {
+      hovered: false,
+      focused: false,
+      dirtyDescription: null,
+    };
+  }
+
+  //  Rendering.
+  render () {
+    const {
+      blur,
+      change,
+      focus,
+      mouseEnter,
+      mouseLeave,
+      remove,
+    } = this.handlers;
+    const {
+      description,
+      intl,
+      preview,
+    } = this.props;
+    const {
+      focused,
+      hovered,
+      dirtyDescription,
+    } = this.state;
+    const computedClass = classNames('composer--upload_form--item', { active: hovered || focused });
+
+    //  The result.
+    return (
+      <div
+        className={computedClass}
+        onMouseEnter={mouseEnter}
+        onMouseLeave={mouseLeave}
+      >
+        <Motion
+          defaultStyle={{ scale: 0.8 }}
+          style={{
+            scale: spring(1, {
+              stiffness: 180,
+              damping: 12,
+            }),
+          }}
+        >
+          {({ scale }) => (
+            <div
+              style={{
+                transform: `scale(${scale})`,
+                backgroundImage: preview ? `url(${preview})` : null,
+              }}
+            >
+              <IconButton
+                icon='times'
+                onClick={remove}
+                size={36}
+                title={intl.formatMessage(messages.undo)}
+              />
+              <label>
+                <span style={{ display: 'none' }}><FormattedMessage {...messages.description} /></span>
+                <input
+                  maxLength={420}
+                  onBlur={blur}
+                  onChange={change}
+                  onFocus={focus}
+                  placeholder={intl.formatMessage(messages.description)}
+                  type='text'
+                  value={dirtyDescription || description || ''}
+                />
+              </label>
+            </div>
+          )}
+        </Motion>
+      </div>
+    );
+  }
+
+}
+
+//  Props.
+ComposerUploadFormItem.propTypes = {
+  description: PropTypes.string,
+  id: PropTypes.number,
+  intl: PropTypes.object.isRequired,
+  onChangeDescription: PropTypes.func,
+  onRemove: PropTypes.func,
+  preview: PropTypes.string,
+};
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js
new file mode 100644
index 000000000..9dac6acf9
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js
@@ -0,0 +1,52 @@
+//  Package imports.
+import PropTypes from 'prop-types';
+import React from 'react';
+import {
+  defineMessages,
+  FormattedMessage,
+} from 'react-intl';
+import spring from 'react-motion/lib/spring';
+
+//  Components.
+import Icon from 'flavours/glitch/components/icon';
+
+//  Utils.
+import Motion from 'flavours/glitch/util/optional_motion';
+
+//  Messages.
+const messages = defineMessages({
+  upload: {
+    defaultMessage: 'Uploading...',
+    id: 'upload_progress.label',
+  },
+});
+
+//  The component.
+export default function ComposerUploadFormProgress ({ progress }) {
+
+  //  The result.
+  return (
+    <div className='composer--upload_form--progress'>
+      <Icon icon='upload' />
+      <div className='message'>
+        <FormattedMessage {...messages.upload} />
+        <div className='backdrop'>
+          <Motion
+            defaultStyle={{ width: 0 }}
+            style={{ width: spring(progress) }}
+          >
+            {({ width }) =>
+              <div
+                className='tracker'
+                style={{ width: `${width}%` }}
+              />
+            }
+          </Motion>
+        </div>
+      </div>
+    </div>
+  );
+}
+
+//  Props.
+ComposerUploadFormProgress.propTypes = { progress: PropTypes.number };