Merge branch 'scalybiz-3.3' into HEAD
This commit is contained in:
commit
8a8a6b559b
|
@ -86,7 +86,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
const fulltext = this.getFulltextForCharacterCounting();
|
const fulltext = this.getFulltextForCharacterCounting();
|
||||||
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
||||||
|
|
||||||
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (isOnlyWhitespace && !anyMedia));
|
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 4096 || (isOnlyWhitespace && !anyMedia));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
|
@ -196,6 +196,11 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
render () {
|
render () {
|
||||||
const { intl, onPaste, showSearch } = this.props;
|
const { intl, onPaste, showSearch } = this.props;
|
||||||
const disabled = this.props.isSubmitting;
|
const disabled = this.props.isSubmitting;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||||
|
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 4096 || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||||
|
>>>>>>> scalybiz-3.2rc
|
||||||
let publishText = '';
|
let publishText = '';
|
||||||
|
|
||||||
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||||
|
@ -257,7 +262,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
<PrivacyDropdownContainer />
|
<PrivacyDropdownContainer />
|
||||||
<SpoilerButtonContainer />
|
<SpoilerButtonContainer />
|
||||||
</div>
|
</div>
|
||||||
<div className='character-counter__wrapper'><CharacterCounter max={500} text={this.getFulltextForCharacterCounting()} /></div>
|
<div className='character-counter__wrapper'><CharacterCounter max={4096} text={this.getFulltextForCharacterCounting()} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='compose-form__publish'>
|
<div className='compose-form__publish'>
|
||||||
|
|
|
@ -102,7 +102,7 @@ class Option extends React.PureComponent {
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className='poll__cancel'>
|
<div className='poll__cancel'>
|
||||||
<IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} />
|
<IconButton disabled={index < 1} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -157,7 +157,7 @@ class PollForm extends ImmutablePureComponent {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className='poll__footer'>
|
<div className='poll__footer'>
|
||||||
<button disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button>
|
<button disabled={options.size >= 12} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button>
|
||||||
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||||
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
||||||
|
|
|
@ -132,7 +132,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
# If there is at least one silent mention, then the status can be considered
|
# If there is at least one silent mention, then the status can be considered
|
||||||
# as a limited-audience status, and not strictly a direct message, but only
|
# as a limited-audience status, and not strictly a direct message, but only
|
||||||
# if we considered a direct message in the first place
|
# if we considered a direct message in the first place
|
||||||
next unless @params[:visibility] == :direct
|
next unless @params[:visibility] == :direct && direct_message.nil?
|
||||||
|
|
||||||
@params[:visibility] = :limited
|
@params[:visibility] = :limited
|
||||||
end
|
end
|
||||||
|
@ -143,7 +143,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
|
|
||||||
@mentions << Mention.new(account_id: @options[:delivered_to_account_id], silent: true)
|
@mentions << Mention.new(account_id: @options[:delivered_to_account_id], silent: true)
|
||||||
|
|
||||||
return unless @params[:visibility] == :direct
|
return unless @params[:visibility] == :direct && direct_message.nil?
|
||||||
|
|
||||||
@params[:visibility] = :limited
|
@params[:visibility] = :limited
|
||||||
end
|
end
|
||||||
|
@ -154,7 +154,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
delivered_to_account = Account.find(@options[:delivered_to_account_id])
|
delivered_to_account = Account.find(@options[:delivered_to_account_id])
|
||||||
|
|
||||||
@status.mentions.create(account: delivered_to_account, silent: true)
|
@status.mentions.create(account: delivered_to_account, silent: true)
|
||||||
@status.update(visibility: :limited) if @status.direct_visibility?
|
@status.update(visibility: :limited) if @status.direct_visibility? && direct_message.nil?
|
||||||
|
|
||||||
return unless delivered_to_account.following?(@account)
|
return unless delivered_to_account.following?(@account)
|
||||||
|
|
||||||
|
@ -353,6 +353,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
:unlisted
|
:unlisted
|
||||||
elsif audience_to.include?(@account.followers_url)
|
elsif audience_to.include?(@account.followers_url)
|
||||||
:private
|
:private
|
||||||
|
elsif direct_message == false
|
||||||
|
:limited
|
||||||
else
|
else
|
||||||
:direct
|
:direct
|
||||||
end
|
end
|
||||||
|
@ -363,6 +365,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
audience_to.include?(uri) || audience_cc.include?(uri)
|
audience_to.include?(uri) || audience_cc.include?(uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def direct_message
|
||||||
|
@object['directMessage']
|
||||||
|
end
|
||||||
|
|
||||||
def replied_to_status
|
def replied_to_status
|
||||||
return @replied_to_status if defined?(@replied_to_status)
|
return @replied_to_status if defined?(@replied_to_status)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
CONTEXT_EXTENSION_MAP = {
|
CONTEXT_EXTENSION_MAP = {
|
||||||
|
direct_message: { 'litepub': 'http://litepub.social/ns#', 'directMessage': 'litepub:directMessage' },
|
||||||
manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' },
|
manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' },
|
||||||
sensitive: { 'sensitive' => 'as:sensitive' },
|
sensitive: { 'sensitive' => 'as:sensitive' },
|
||||||
hashtag: { 'Hashtag' => 'as:Hashtag' },
|
hashtag: { 'Hashtag' => 'as:Hashtag' },
|
||||||
|
|
|
@ -88,9 +88,9 @@ class Account < ApplicationRecord
|
||||||
validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, if: -> { !local? && will_save_change_to_username? }
|
validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, if: -> { !local? && will_save_change_to_username? }
|
||||||
|
|
||||||
# Local user validations
|
# Local user validations
|
||||||
validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
|
validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 100 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
|
||||||
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
|
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
|
||||||
validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
|
validates :display_name, length: { maximum: 100 }, if: -> { local? && will_save_change_to_display_name? }
|
||||||
validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? }
|
validates :note, note_length: { maximum: 500 }, if: -> { local? && will_save_change_to_note? }
|
||||||
validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
|
validates :fields, length: { maximum: 4 }, if: -> { local? && will_save_change_to_fields? }
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
context_extensions :atom_uri, :conversation, :sensitive, :voters_count
|
context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :direct_message
|
||||||
|
|
||||||
attributes :id, :type, :summary,
|
attributes :id, :type, :summary,
|
||||||
:in_reply_to, :published, :url,
|
:in_reply_to, :published, :url,
|
||||||
|
@ -12,6 +12,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
attribute :content
|
attribute :content
|
||||||
attribute :content_map, if: :language?
|
attribute :content_map, if: :language?
|
||||||
|
|
||||||
|
attribute :direct_message, if: :non_public?
|
||||||
|
|
||||||
has_many :media_attachments, key: :attachment
|
has_many :media_attachments, key: :attachment
|
||||||
has_many :virtual_tags, key: :tag
|
has_many :virtual_tags, key: :tag
|
||||||
|
|
||||||
|
@ -37,6 +39,14 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
object.spoiler_text.presence
|
object.spoiler_text.presence
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def direct_message
|
||||||
|
object.direct_visibility?
|
||||||
|
end
|
||||||
|
|
||||||
|
def non_public?
|
||||||
|
!object.distributable?
|
||||||
|
end
|
||||||
|
|
||||||
def content
|
def content
|
||||||
Formatter.instance.format(object)
|
Formatter.instance.format(object)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class PollValidator < ActiveModel::Validator
|
class PollValidator < ActiveModel::Validator
|
||||||
MAX_OPTIONS = 4
|
MAX_OPTIONS = 20
|
||||||
MAX_OPTION_CHARS = 50
|
MAX_OPTION_CHARS = 50
|
||||||
MAX_EXPIRATION = 1.month.freeze
|
MAX_EXPIRATION = 1.month.freeze
|
||||||
MIN_EXPIRATION = 5.minutes.freeze
|
MIN_EXPIRATION = 5.minutes.freeze
|
||||||
|
@ -9,7 +9,6 @@ class PollValidator < ActiveModel::Validator
|
||||||
def validate(poll)
|
def validate(poll)
|
||||||
current_time = Time.now.utc
|
current_time = Time.now.utc
|
||||||
|
|
||||||
poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1
|
|
||||||
poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS
|
poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS
|
||||||
poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS }
|
poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS }
|
||||||
poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size
|
poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StatusLengthValidator < ActiveModel::Validator
|
class StatusLengthValidator < ActiveModel::Validator
|
||||||
MAX_CHARS = 500
|
MAX_CHARS = 4096
|
||||||
URL_PLACEHOLDER = "\1#{'x' * 23}"
|
URL_PLACEHOLDER = "\1#{'x' * 23}"
|
||||||
|
|
||||||
def validate(status)
|
def validate(status)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
.fields-row
|
.fields-row
|
||||||
.fields-row__column.fields-group.fields-row__column-6
|
.fields-row__column.fields-group.fields-row__column-6
|
||||||
= f.input :display_name, wrapper: :with_label, input_html: { maxlength: 30, data: { default: @account.username } }, hint: false
|
= f.input :display_name, wrapper: :with_label, input_html: { maxlength: 100, data: { default: @account.username } }, hint: false
|
||||||
= f.input :note, wrapper: :with_label, input_html: { maxlength: 500 }, hint: false
|
= f.input :note, wrapper: :with_label, input_html: { maxlength: 500 }, hint: false
|
||||||
|
|
||||||
.fields-row
|
.fields-row
|
||||||
|
|
|
@ -240,6 +240,31 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'limited when direct message assertion is false' do
|
||||||
|
let(:recipient) { Fabricate(:account) }
|
||||||
|
|
||||||
|
let(:object_json) do
|
||||||
|
{
|
||||||
|
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||||
|
type: 'Note',
|
||||||
|
content: 'Lorem ipsum',
|
||||||
|
directMessage: false,
|
||||||
|
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||||
|
tag: {
|
||||||
|
type: 'Mention',
|
||||||
|
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates status' do
|
||||||
|
status = sender.statuses.first
|
||||||
|
|
||||||
|
expect(status).to_not be_nil
|
||||||
|
expect(status.visibility).to eq 'limited'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'direct' do
|
context 'direct' do
|
||||||
let(:recipient) { Fabricate(:account) }
|
let(:recipient) { Fabricate(:account) }
|
||||||
|
|
||||||
|
@ -264,6 +289,27 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'direct when direct message assertion is true' do
|
||||||
|
let(:recipient) { Fabricate(:account) }
|
||||||
|
|
||||||
|
let(:object_json) do
|
||||||
|
{
|
||||||
|
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||||
|
type: 'Note',
|
||||||
|
content: 'Lorem ipsum',
|
||||||
|
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||||
|
directMessage: true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates status' do
|
||||||
|
status = sender.statuses.first
|
||||||
|
|
||||||
|
expect(status).to_not be_nil
|
||||||
|
expect(status.visibility).to eq 'direct'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'as a reply' do
|
context 'as a reply' do
|
||||||
let(:original_status) { Fabricate(:status) }
|
let(:original_status) { Fabricate(:status) }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue