forked from cybrespace/mastodon
		
	Remove text requirement when media attached from statuses (#6672)
This commit is contained in:
		
							parent
							
								
									e26d5ca923
								
							
						
					
					
						commit
						cfa9b6e13a
					
				
					 8 changed files with 28 additions and 18 deletions
				
			
		| 
						 | 
				
			
			@ -96,8 +96,9 @@ export function mentionCompose(account, router) {
 | 
			
		|||
export function submitCompose() {
 | 
			
		||||
  return function (dispatch, getState) {
 | 
			
		||||
    const status = getState().getIn(['compose', 'text'], '');
 | 
			
		||||
    const media  = getState().getIn(['compose', 'media_attachments']);
 | 
			
		||||
 | 
			
		||||
    if (!status || !status.length) {
 | 
			
		||||
    if ((!status || !status.length) && media.size === 0) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,7 @@ export function submitCompose() {
 | 
			
		|||
    api(getState).post('/api/v1/statuses', {
 | 
			
		||||
      status,
 | 
			
		||||
      in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
 | 
			
		||||
      media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
 | 
			
		||||
      media_ids: media.map(item => item.get('id')),
 | 
			
		||||
      sensitive: getState().getIn(['compose', 'sensitive']),
 | 
			
		||||
      spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
 | 
			
		||||
      visibility: getState().getIn(['compose', 'privacy']),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,11 @@ export default class StatusContent extends React.PureComponent {
 | 
			
		|||
 | 
			
		||||
  _updateStatusLinks () {
 | 
			
		||||
    const node = this.node;
 | 
			
		||||
 | 
			
		||||
    if (!node) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const links = node.querySelectorAll('a');
 | 
			
		||||
 | 
			
		||||
    for (var i = 0; i < links.length; ++i) {
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +120,10 @@ export default class StatusContent extends React.PureComponent {
 | 
			
		|||
  render () {
 | 
			
		||||
    const { status } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (status.get('content').length === 0) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
 | 
			
		||||
 | 
			
		||||
    const content = { __html: status.get('contentHtml') };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ export default class ComposeForm extends ImmutablePureComponent {
 | 
			
		|||
    onPaste: PropTypes.func.isRequired,
 | 
			
		||||
    onPickEmoji: PropTypes.func.isRequired,
 | 
			
		||||
    showSearch: PropTypes.bool,
 | 
			
		||||
    anyMedia: PropTypes.bool,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static defaultProps = {
 | 
			
		||||
| 
						 | 
				
			
			@ -142,10 +143,10 @@ export default class ComposeForm extends ImmutablePureComponent {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { intl, onPaste, showSearch } = this.props;
 | 
			
		||||
    const { intl, onPaste, showSearch, anyMedia } = this.props;
 | 
			
		||||
    const disabled = this.props.is_submitting;
 | 
			
		||||
    const text     = [this.props.spoiler_text, countableText(this.props.text)].join('');
 | 
			
		||||
 | 
			
		||||
    const disabledButton = disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
 | 
			
		||||
    let publishText = '';
 | 
			
		||||
 | 
			
		||||
    if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +204,7 @@ export default class ComposeForm extends ImmutablePureComponent {
 | 
			
		|||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div className='compose-form__publish'>
 | 
			
		||||
          <div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabled || this.props.is_uploading || length(text) > 500 || (text.length !== 0 && text.trim().length === 0)} block /></div>
 | 
			
		||||
          <div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabledButton} block /></div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ const mapStateToProps = state => ({
 | 
			
		|||
  is_submitting: state.getIn(['compose', 'is_submitting']),
 | 
			
		||||
  is_uploading: state.getIn(['compose', 'is_uploading']),
 | 
			
		||||
  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
 | 
			
		||||
  anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const mapDispatchToProps = (dispatch) => ({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,7 +90,6 @@ function appendMedia(state, media) {
 | 
			
		|||
    map.update('media_attachments', list => list.push(media));
 | 
			
		||||
    map.set('is_uploading', false);
 | 
			
		||||
    map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
 | 
			
		||||
    map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`);
 | 
			
		||||
    map.set('focusDate', new Date());
 | 
			
		||||
    map.set('idempotencyKey', uuid());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,12 +100,10 @@ function appendMedia(state, media) {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
function removeMedia(state, mediaId) {
 | 
			
		||||
  const media    = state.get('media_attachments').find(item => item.get('id') === mediaId);
 | 
			
		||||
  const prevSize = state.get('media_attachments').size;
 | 
			
		||||
 | 
			
		||||
  return state.withMutations(map => {
 | 
			
		||||
    map.update('media_attachments', list => list.filterNot(item => item.get('id') === mediaId));
 | 
			
		||||
    map.update('text', text => text.replace(media.get('text_url'), '').trim());
 | 
			
		||||
    map.set('idempotencyKey', uuid());
 | 
			
		||||
 | 
			
		||||
    if (prevSize === 1) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,8 @@ class Formatter
 | 
			
		|||
 | 
			
		||||
    raw_content = status.text
 | 
			
		||||
 | 
			
		||||
    return '' if raw_content.blank?
 | 
			
		||||
 | 
			
		||||
    unless status.local?
 | 
			
		||||
      html = reformat(raw_content)
 | 
			
		||||
      html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ class Status < ApplicationRecord
 | 
			
		|||
  has_one :stream_entry, as: :activity, inverse_of: :status
 | 
			
		||||
 | 
			
		||||
  validates :uri, uniqueness: true, presence: true, unless: :local?
 | 
			
		||||
  validates :text, presence: true, unless: :reblog?
 | 
			
		||||
  validates :text, presence: true, unless: -> { with_media? || reblog? }
 | 
			
		||||
  validates_with StatusLengthValidator
 | 
			
		||||
  validates :reblog, uniqueness: { scope: :account }, if: :reblog?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -150,8 +150,12 @@ class Status < ApplicationRecord
 | 
			
		|||
    private_visibility? || direct_visibility?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def with_media?
 | 
			
		||||
    media_attachments.any?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def non_sensitive_with_media?
 | 
			
		||||
    !sensitive? && media_attachments.any?
 | 
			
		||||
    !sensitive? && with_media?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def emojis
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,17 +21,17 @@ class PostStatusService < BaseService
 | 
			
		|||
 | 
			
		||||
    media  = validate_media!(options[:media_ids])
 | 
			
		||||
    status = nil
 | 
			
		||||
    text   = options.delete(:spoiler_text) if text.blank? && options[:spoiler_text].present?
 | 
			
		||||
 | 
			
		||||
    ApplicationRecord.transaction do
 | 
			
		||||
      status = account.statuses.create!(text: text,
 | 
			
		||||
                                        media_attachments: media || [],
 | 
			
		||||
                                        thread: in_reply_to,
 | 
			
		||||
                                        sensitive: options[:sensitive],
 | 
			
		||||
                                        spoiler_text: options[:spoiler_text] || '',
 | 
			
		||||
                                        visibility: options[:visibility] || account.user&.setting_default_privacy,
 | 
			
		||||
                                        language: LanguageDetector.instance.detect(text, account),
 | 
			
		||||
                                        application: options[:application])
 | 
			
		||||
 | 
			
		||||
      attach_media(status, media)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    process_mentions_service.call(status)
 | 
			
		||||
| 
						 | 
				
			
			@ -64,11 +64,6 @@ class PostStatusService < BaseService
 | 
			
		|||
    media
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def attach_media(status, media)
 | 
			
		||||
    return if media.nil?
 | 
			
		||||
    media.update(status_id: status.id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def process_mentions_service
 | 
			
		||||
    ProcessMentionsService.new
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue