347 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
//  Package imports.
 | 
						|
import PropTypes from 'prop-types';
 | 
						|
import React from 'react';
 | 
						|
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
						|
import {
 | 
						|
  FormattedMessage,
 | 
						|
  defineMessages,
 | 
						|
} from 'react-intl';
 | 
						|
import spring from 'react-motion/lib/spring';
 | 
						|
 | 
						|
//  Components.
 | 
						|
import IconButton from 'flavours/glitch/components/icon_button';
 | 
						|
import TextIconButton from 'flavours/glitch/components/text_icon_button';
 | 
						|
import Dropdown from './dropdown';
 | 
						|
 | 
						|
//  Utils.
 | 
						|
import Motion from 'flavours/glitch/util/optional_motion';
 | 
						|
import {
 | 
						|
  assignHandlers,
 | 
						|
  hiddenComponent,
 | 
						|
} from 'flavours/glitch/util/react_helpers';
 | 
						|
 | 
						|
//  Messages.
 | 
						|
const messages = defineMessages({
 | 
						|
  advanced_options_icon_title: {
 | 
						|
    defaultMessage: 'Advanced options',
 | 
						|
    id: 'advanced_options.icon_title',
 | 
						|
  },
 | 
						|
  attach: {
 | 
						|
    defaultMessage: 'Attach...',
 | 
						|
    id: 'compose.attach',
 | 
						|
  },
 | 
						|
  change_privacy: {
 | 
						|
    defaultMessage: 'Adjust status privacy',
 | 
						|
    id: 'privacy.change',
 | 
						|
  },
 | 
						|
  direct_long: {
 | 
						|
    defaultMessage: 'Post to mentioned users only',
 | 
						|
    id: 'privacy.direct.long',
 | 
						|
  },
 | 
						|
  direct_short: {
 | 
						|
    defaultMessage: 'Direct',
 | 
						|
    id: 'privacy.direct.short',
 | 
						|
  },
 | 
						|
  doodle: {
 | 
						|
    defaultMessage: 'Draw something',
 | 
						|
    id: 'compose.attach.doodle',
 | 
						|
  },
 | 
						|
  local_only_long: {
 | 
						|
    defaultMessage: 'Do not post to other instances',
 | 
						|
    id: 'advanced_options.local-only.long',
 | 
						|
  },
 | 
						|
  local_only_short: {
 | 
						|
    defaultMessage: 'Local-only',
 | 
						|
    id: 'advanced_options.local-only.short',
 | 
						|
  },
 | 
						|
  private_long: {
 | 
						|
    defaultMessage: 'Post to followers only',
 | 
						|
    id: 'privacy.private.long',
 | 
						|
  },
 | 
						|
  private_short: {
 | 
						|
    defaultMessage: 'Followers-only',
 | 
						|
    id: 'privacy.private.short',
 | 
						|
  },
 | 
						|
  public_long: {
 | 
						|
    defaultMessage: 'Post to public timelines',
 | 
						|
    id: 'privacy.public.long',
 | 
						|
  },
 | 
						|
  public_short: {
 | 
						|
    defaultMessage: 'Public',
 | 
						|
    id: 'privacy.public.short',
 | 
						|
  },
 | 
						|
  sensitive: {
 | 
						|
    defaultMessage: 'Mark media as sensitive',
 | 
						|
    id: 'compose_form.sensitive',
 | 
						|
  },
 | 
						|
  spoiler: {
 | 
						|
    defaultMessage: 'Hide text behind warning',
 | 
						|
    id: 'compose_form.spoiler',
 | 
						|
  },
 | 
						|
  threaded_mode_long: {
 | 
						|
    defaultMessage: 'Automatically opens a reply on posting',
 | 
						|
    id: 'advanced_options.threaded_mode.long',
 | 
						|
  },
 | 
						|
  threaded_mode_short: {
 | 
						|
    defaultMessage: 'Threaded mode',
 | 
						|
    id: 'advanced_options.threaded_mode.short',
 | 
						|
  },
 | 
						|
  unlisted_long: {
 | 
						|
    defaultMessage: 'Do not show in public timelines',
 | 
						|
    id: 'privacy.unlisted.long',
 | 
						|
  },
 | 
						|
  unlisted_short: {
 | 
						|
    defaultMessage: 'Unlisted',
 | 
						|
    id: 'privacy.unlisted.short',
 | 
						|
  },
 | 
						|
  upload: {
 | 
						|
    defaultMessage: 'Upload a file',
 | 
						|
    id: 'compose.attach.upload',
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
//  Handlers.
 | 
						|
const handlers = {
 | 
						|
 | 
						|
  //  Handles file selection.
 | 
						|
  handleChangeFiles ({ target: { files } }) {
 | 
						|
    const { onUpload } = this.props;
 | 
						|
    if (files.length && onUpload) {
 | 
						|
      onUpload(files);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  //  Handles attachment clicks.
 | 
						|
  handleClickAttach (name) {
 | 
						|
    const { fileElement } = this;
 | 
						|
    const { onDoodleOpen } = this.props;
 | 
						|
 | 
						|
    //  We switch over the name of the option.
 | 
						|
    switch (name) {
 | 
						|
    case 'upload':
 | 
						|
      if (fileElement) {
 | 
						|
        fileElement.click();
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    case 'doodle':
 | 
						|
      if (onDoodleOpen) {
 | 
						|
        onDoodleOpen();
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  //  Handles a ref to the file input.
 | 
						|
  handleRefFileElement (fileElement) {
 | 
						|
    this.fileElement = fileElement;
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
//  The component.
 | 
						|
export default class ComposerOptions extends React.PureComponent {
 | 
						|
 | 
						|
  //  Constructor.
 | 
						|
  constructor (props) {
 | 
						|
    super(props);
 | 
						|
    assignHandlers(this, handlers);
 | 
						|
 | 
						|
    //  Instance variables.
 | 
						|
    this.fileElement = null;
 | 
						|
  }
 | 
						|
 | 
						|
  //  Rendering.
 | 
						|
  render () {
 | 
						|
    const {
 | 
						|
      handleChangeFiles,
 | 
						|
      handleClickAttach,
 | 
						|
      handleRefFileElement,
 | 
						|
    } = this.handlers;
 | 
						|
    const {
 | 
						|
      acceptContentTypes,
 | 
						|
      advancedOptions,
 | 
						|
      disabled,
 | 
						|
      full,
 | 
						|
      hasMedia,
 | 
						|
      intl,
 | 
						|
      onChangeAdvancedOption,
 | 
						|
      onChangeSensitivity,
 | 
						|
      onChangeVisibility,
 | 
						|
      onModalClose,
 | 
						|
      onModalOpen,
 | 
						|
      onToggleSpoiler,
 | 
						|
      privacy,
 | 
						|
      resetFileKey,
 | 
						|
      sensitive,
 | 
						|
      spoiler,
 | 
						|
    } = this.props;
 | 
						|
 | 
						|
    //  We predefine our privacy items so that we can easily pick the
 | 
						|
    //  dropdown icon later.
 | 
						|
    const privacyItems = {
 | 
						|
      direct: {
 | 
						|
        icon: 'envelope',
 | 
						|
        meta: <FormattedMessage {...messages.direct_long} />,
 | 
						|
        name: 'direct',
 | 
						|
        text: <FormattedMessage {...messages.direct_short} />,
 | 
						|
      },
 | 
						|
      private: {
 | 
						|
        icon: 'lock',
 | 
						|
        meta: <FormattedMessage {...messages.private_long} />,
 | 
						|
        name: 'private',
 | 
						|
        text: <FormattedMessage {...messages.private_short} />,
 | 
						|
      },
 | 
						|
      public: {
 | 
						|
        icon: 'globe',
 | 
						|
        meta: <FormattedMessage {...messages.public_long} />,
 | 
						|
        name: 'public',
 | 
						|
        text: <FormattedMessage {...messages.public_short} />,
 | 
						|
      },
 | 
						|
      unlisted: {
 | 
						|
        icon: 'unlock-alt',
 | 
						|
        meta: <FormattedMessage {...messages.unlisted_long} />,
 | 
						|
        name: 'unlisted',
 | 
						|
        text: <FormattedMessage {...messages.unlisted_short} />,
 | 
						|
      },
 | 
						|
    };
 | 
						|
 | 
						|
    //  The result.
 | 
						|
    return (
 | 
						|
      <div className='composer--options'>
 | 
						|
        <input
 | 
						|
          accept={acceptContentTypes}
 | 
						|
          disabled={disabled || full}
 | 
						|
          key={resetFileKey}
 | 
						|
          onChange={handleChangeFiles}
 | 
						|
          ref={handleRefFileElement}
 | 
						|
          type='file'
 | 
						|
          {...hiddenComponent}
 | 
						|
        />
 | 
						|
        <Dropdown
 | 
						|
          disabled={disabled || full}
 | 
						|
          icon='paperclip'
 | 
						|
          items={[
 | 
						|
            {
 | 
						|
              icon: 'cloud-upload',
 | 
						|
              name: 'upload',
 | 
						|
              text: <FormattedMessage {...messages.upload} />,
 | 
						|
            },
 | 
						|
            {
 | 
						|
              icon: 'paint-brush',
 | 
						|
              name: 'doodle',
 | 
						|
              text: <FormattedMessage {...messages.doodle} />,
 | 
						|
            },
 | 
						|
          ]}
 | 
						|
          onChange={handleClickAttach}
 | 
						|
          onModalClose={onModalClose}
 | 
						|
          onModalOpen={onModalOpen}
 | 
						|
          title={intl.formatMessage(messages.attach)}
 | 
						|
        />
 | 
						|
        <Motion
 | 
						|
          defaultStyle={{ scale: 0.87 }}
 | 
						|
          style={{
 | 
						|
            scale: spring(hasMedia ? 1 : 0.87, {
 | 
						|
              stiffness: 200,
 | 
						|
              damping: 3,
 | 
						|
            }),
 | 
						|
          }}
 | 
						|
        >
 | 
						|
          {({ scale }) => (
 | 
						|
            <div
 | 
						|
              style={{
 | 
						|
                display: hasMedia ? null : 'none',
 | 
						|
                transform: `scale(${scale})`,
 | 
						|
              }}
 | 
						|
            >
 | 
						|
              <IconButton
 | 
						|
                active={sensitive}
 | 
						|
                className='sensitive'
 | 
						|
                disabled={spoiler}
 | 
						|
                icon={sensitive ? 'eye-slash' : 'eye'}
 | 
						|
                inverted
 | 
						|
                onClick={onChangeSensitivity}
 | 
						|
                size={18}
 | 
						|
                style={{
 | 
						|
                  height: null,
 | 
						|
                  lineHeight: null,
 | 
						|
                }}
 | 
						|
                title={intl.formatMessage(messages.sensitive)}
 | 
						|
              />
 | 
						|
            </div>
 | 
						|
          )}
 | 
						|
        </Motion>
 | 
						|
        <hr />
 | 
						|
        <Dropdown
 | 
						|
          disabled={disabled}
 | 
						|
          icon={(privacyItems[privacy] || {}).icon}
 | 
						|
          items={[
 | 
						|
            privacyItems.public,
 | 
						|
            privacyItems.unlisted,
 | 
						|
            privacyItems.private,
 | 
						|
            privacyItems.direct,
 | 
						|
          ]}
 | 
						|
          onChange={onChangeVisibility}
 | 
						|
          onModalClose={onModalClose}
 | 
						|
          onModalOpen={onModalOpen}
 | 
						|
          title={intl.formatMessage(messages.change_privacy)}
 | 
						|
          value={privacy}
 | 
						|
        />
 | 
						|
        {onToggleSpoiler && (
 | 
						|
          <TextIconButton
 | 
						|
            active={spoiler}
 | 
						|
            ariaControls='glitch.composer.spoiler.input'
 | 
						|
            label='CW'
 | 
						|
            onClick={onToggleSpoiler}
 | 
						|
            title={intl.formatMessage(messages.spoiler)}
 | 
						|
          />
 | 
						|
        )}
 | 
						|
        <Dropdown
 | 
						|
          active={advancedOptions && advancedOptions.some(value => !!value)}
 | 
						|
          disabled={disabled}
 | 
						|
          icon='ellipsis-h'
 | 
						|
          items={advancedOptions ? [
 | 
						|
            {
 | 
						|
              meta: <FormattedMessage {...messages.local_only_long} />,
 | 
						|
              name: 'do_not_federate',
 | 
						|
              on: advancedOptions.get('do_not_federate'),
 | 
						|
              text: <FormattedMessage {...messages.local_only_short} />,
 | 
						|
            },
 | 
						|
            {
 | 
						|
              meta: <FormattedMessage {...messages.threaded_mode_long} />,
 | 
						|
              name: 'threaded_mode',
 | 
						|
              on: advancedOptions.get('threaded_mode'),
 | 
						|
              text: <FormattedMessage {...messages.threaded_mode_short} />,
 | 
						|
            },
 | 
						|
          ] : null}
 | 
						|
          onChange={onChangeAdvancedOption}
 | 
						|
          onModalClose={onModalClose}
 | 
						|
          onModalOpen={onModalOpen}
 | 
						|
          title={intl.formatMessage(messages.advanced_options_icon_title)}
 | 
						|
        />
 | 
						|
      </div>
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//  Props.
 | 
						|
ComposerOptions.propTypes = {
 | 
						|
  acceptContentTypes: PropTypes.string,
 | 
						|
  advancedOptions: ImmutablePropTypes.map,
 | 
						|
  disabled: PropTypes.bool,
 | 
						|
  full: PropTypes.bool,
 | 
						|
  hasMedia: PropTypes.bool,
 | 
						|
  intl: PropTypes.object.isRequired,
 | 
						|
  onChangeAdvancedOption: PropTypes.func,
 | 
						|
  onChangeSensitivity: PropTypes.func,
 | 
						|
  onChangeVisibility: PropTypes.func,
 | 
						|
  onDoodleOpen: PropTypes.func,
 | 
						|
  onModalClose: PropTypes.func,
 | 
						|
  onModalOpen: PropTypes.func,
 | 
						|
  onToggleSpoiler: PropTypes.func,
 | 
						|
  onUpload: PropTypes.func,
 | 
						|
  privacy: PropTypes.string,
 | 
						|
  resetFileKey: PropTypes.number,
 | 
						|
  sensitive: PropTypes.bool,
 | 
						|
  spoiler: PropTypes.bool,
 | 
						|
};
 |