~cytrogen/masto-fe

5d8c65f2a745efe3429319387cb8aaed46cac382 — Claire 2 years ago 526f457 + 398635c
Merge commit '398635c0c4987ec44d937e98431ff5dee331ea94' into glitch-soc/merge-upstream
M app/javascript/mastodon/features/compose/components/search.jsx => app/javascript/mastodon/features/compose/components/search.jsx +15 -13
@@ 8,7 8,7 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';

import { Icon }  from 'mastodon/components/icon';
import { searchEnabled } from 'mastodon/initial_state';
import { domain, searchEnabled } from 'mastodon/initial_state';
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';

const messages = defineMessages({


@@ 354,18 354,20 @@ class Search extends PureComponent {
            </>
          )}

          {searchEnabled && (
            <>
              <h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>

              <div className='search__popout__menu'>
                {this.defaultOptions.map(({ key, label, action }, i) => (
                  <button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === (options.length + i) })}>
                    {label}
                  </button>
                ))}
              </div>
            </>
          <h4><FormattedMessage id='search_popout.options' defaultMessage='Search options' /></h4>

          {searchEnabled ? (
            <div className='search__popout__menu'>
              {this.defaultOptions.map(({ key, label, action }, i) => (
                <button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === (options.length + i) })}>
                  {label}
                </button>
              ))}
            </div>
          ) : (
            <div className='search__popout__menu__message'>
              <FormattedMessage id='search_popout.full_text_search_disabled_message' defaultMessage='Not available on {domain}.' values={{ domain }} />
            </div>
          )}
        </div>
      </div>

M app/javascript/mastodon/locales/en.json => app/javascript/mastodon/locales/en.json +1 -0
@@ 590,6 590,7 @@
  "search.quick_action.open_url": "Open URL in Mastodon",
  "search.quick_action.status_search": "Posts matching {x}",
  "search.search_or_paste": "Search or paste URL",
  "search_popout.full_text_search_disabled_message": "Not available on {domain}.",
  "search_popout.language_code": "ISO language code",
  "search_popout.options": "Search options",
  "search_popout.quick_actions": "Quick actions",

M app/javascript/styles/mastodon/components.scss => app/javascript/styles/mastodon/components.scss +1 -1
@@ 836,7 836,7 @@ body > [data-popper-placement] {
  }

  p {
    margin-bottom: 20px;
    margin-bottom: 22px;
    white-space: pre-wrap;
    unicode-bidi: plaintext;


R app/models/account_statuses_filter.rb => app/lib/account_statuses_filter.rb +8 -1
@@ 55,7 55,14 @@ class AccountStatusesFilter
  end

  def filtered_reblogs_scope
    Status.left_outer_joins(:reblog).where(reblog_of_id: nil).or(Status.where.not(reblogs_statuses: { account_id: current_account.excluded_from_timeline_account_ids }))
    scope = Status.left_outer_joins(reblog: :account)
    scope
      .where(reblog_of_id: nil)
      .or(
        scope
          .where.not(reblog: { account_id: current_account.excluded_from_timeline_account_ids })
          .where.not(reblog: { accounts: { domain: current_account.excluded_from_timeline_domains } })
      )
  end

  def only_media_scope

A app/lib/admin/account_statuses_filter.rb => app/lib/admin/account_statuses_filter.rb +9 -0
@@ 0,0 1,9 @@
# frozen_string_literal: true

class Admin::AccountStatusesFilter < AccountStatusesFilter
  private

  def blocked?
    false
  end
end

M app/models/admin/status_batch_action.rb => app/models/admin/status_batch_action.rb +1 -1
@@ 140,6 140,6 @@ class Admin::StatusBatchAction
  end

  def allowed_status_ids
    AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id)
    Admin::AccountStatusesFilter.new(@report.target_account, current_account).results.with_discarded.where(id: status_ids).pluck(:id)
  end
end

M app/policies/admin/status_policy.rb => app/policies/admin/status_policy.rb +7 -1
@@ 12,7 12,7 @@ class Admin::StatusPolicy < ApplicationPolicy
  end

  def show?
    role.can?(:manage_reports, :manage_users) && (record.public_visibility? || record.unlisted_visibility? || record.reported?)
    role.can?(:manage_reports, :manage_users) && (record.public_visibility? || record.unlisted_visibility? || record.reported? || viewable_through_normal_policy?)
  end

  def destroy?


@@ 26,4 26,10 @@ class Admin::StatusPolicy < ApplicationPolicy
  def review?
    role.can?(:manage_taxonomies)
  end

  private

  def viewable_through_normal_policy?
    StatusPolicy.new(current_account, record, @preloaded_relations).show?
  end
end

M app/views/admin/trends/tags/_tag.html.haml => app/views/admin/trends/tags/_tag.html.haml +2 -1
@@ 10,7 10,8 @@

      %br/

      = t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })
      = link_to tag_path(tag), target: '_blank' do
        = t('admin.trends.tags.used_by_over_week', count: tag.history.reduce(0) { |sum, day| sum + day.accounts })

      - if tag.trendable? && (rank = Trends.tags.rank(tag.id))
        ·

M app/views/admin_mailer/new_appeal.text.erb => app/views/admin_mailer/new_appeal.text.erb +1 -1
@@ 1,6 1,6 @@
<%= raw t('application_mailer.salutation', name: display_name(@me)) %>

<%= raw t('admin_mailer.new_appeal.body', target: @appeal.account.username, action_taken_by: @appeal.strike.account.username, date: l(@appeal.strike.created_at), type: t(@appeal.strike.action, scope: 'admin_mailer.new_appeal.actions')) %>
<%= raw t('admin_mailer.new_appeal.body', target: @appeal.account.username, action_taken_by: @appeal.strike.account.username, date: l(@appeal.strike.created_at, format: :with_time_zone), type: t(@appeal.strike.action, scope: 'admin_mailer.new_appeal.actions')) %>

> <%= raw word_wrap(@appeal.text, break_sequence: "\n> ") %>


M app/views/notification_mailer/_status.html.haml => app/views/notification_mailer/_status.html.haml +1 -1
@@ 42,4 42,4 @@
                                        = link_to a.remote_url, a.remote_url

                              %p.status-footer
                                = link_to l(status.created_at.in_time_zone(time_zone.presence)), web_url("@#{status.account.pretty_acct}/#{status.id}")
                                = link_to l(status.created_at.in_time_zone(time_zone.presence), format: :with_time_zone), web_url("@#{status.account.pretty_acct}/#{status.id}")

M app/views/user_mailer/appeal_approved.html.haml => app/views/user_mailer/appeal_approved.html.haml +1 -1
@@ 36,7 36,7 @@
                        %tbody
                          %tr
                            %td.column-cell.text-center
                              %p= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence))
                              %p= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)

%table.email-table{ cellspacing: 0, cellpadding: 0 }
  %tbody

M app/views/user_mailer/appeal_approved.text.erb => app/views/user_mailer/appeal_approved.text.erb +1 -1
@@ 2,6 2,6 @@

===

<%= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %>
<%= t 'user_mailer.appeal_approved.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>

=> <%= root_url %>

M app/views/user_mailer/appeal_rejected.html.haml => app/views/user_mailer/appeal_rejected.html.haml +1 -1
@@ 36,7 36,7 @@
                        %tbody
                          %tr
                            %td.column-cell.text-center
                              %p= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence))
                              %p= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)

%table.email-table{ cellspacing: 0, cellpadding: 0 }
  %tbody

M app/views/user_mailer/appeal_rejected.text.erb => app/views/user_mailer/appeal_rejected.text.erb +1 -1
@@ 2,6 2,6 @@

===

<%= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence)), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence)) %>
<%= t 'user_mailer.appeal_rejected.explanation', appeal_date: l(@appeal.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone), strike_date: l(@appeal.strike.created_at.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>

=> <%= root_url %>

M app/views/user_mailer/suspicious_sign_in.html.haml => app/views/user_mailer/suspicious_sign_in.html.haml +1 -1
@@ 47,7 47,7 @@
                                %strong= "#{t('sessions.browser')}:"
                                %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s), platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s)
                                %br/
                                = l(@timestamp.in_time_zone(@resource.time_zone.presence))
                                = l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)

%table.email-table{ cellspacing: 0, cellpadding: 0 }
  %tbody

M app/views/user_mailer/suspicious_sign_in.text.erb => app/views/user_mailer/suspicious_sign_in.text.erb +1 -1
@@ 8,7 8,7 @@

<%= t('sessions.ip') %>: <%= @remote_ip %>
<%= t('sessions.browser') %>: <%= t('sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}")) %>
<%= l(@timestamp.in_time_zone(@resource.time_zone.presence)) %>
<%= l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>

<%= t 'user_mailer.suspicious_sign_in.further_actions_html', action: t('user_mailer.suspicious_sign_in.change_password') %>


M config/locales/en.yml => config/locales/en.yml +1 -0
@@ 1739,6 1739,7 @@ en:
      default: "%b %d, %Y, %H:%M"
      month: "%b %Y"
      time: "%H:%M"
      with_time_zone: "%b %d, %Y, %H:%M %Z"
  translation:
    errors:
      quota_exceeded: The server-wide usage quota for the translation service has been exceeded.

M spec/controllers/admin/statuses_controller_spec.rb => spec/controllers/admin/statuses_controller_spec.rb +16 -4
@@ 52,24 52,36 @@ describe Admin::StatusesController do
  end

  describe 'POST #batch' do
    before do
      post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } }
    end
    subject { post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } } }

    let(:status_ids) { [media_attached_status.id] }

    context 'when action is report' do
    shared_examples 'when action is report' do
      let(:action) { 'report' }

      it 'creates a report' do
        subject

        report = Report.last
        expect(report.target_account_id).to eq account.id
        expect(report.status_ids).to eq status_ids
      end

      it 'redirects to report page' do
        subject

        expect(response).to redirect_to(admin_report_path(Report.last.id))
      end
    end

    it_behaves_like 'when action is report'

    context 'when the moderator is blocked by the author' do
      before do
        account.block!(user.account)
      end

      it_behaves_like 'when action is report'
    end
  end
end

M spec/models/account_statuses_filter_spec.rb => spec/models/account_statuses_filter_spec.rb +14 -0
@@ 199,6 199,20 @@ RSpec.describe AccountStatusesFilter do
        end
      end

      context 'when blocking a reblogged domain' do
        let(:other_account) { Fabricate(:account, domain: 'example.com') }
        let(:reblogging_status) { Fabricate(:status, account: other_account) }
        let(:reblog) { Fabricate(:status, account: account, visibility: 'public', reblog: reblogging_status) }

        before do
          current_account.block_domain!(other_account.domain)
        end

        it 'does not return reblog of blocked domain' do
          expect(subject.results.pluck(:id)).to_not include(reblog.id)
        end
      end

      context 'when muting a reblogged account' do
        let(:reblog) { status_with_reblog!('public') }


M spec/policies/admin/status_policy_spec.rb => spec/policies/admin/status_policy_spec.rb +14 -3
@@ 7,7 7,8 @@ describe Admin::StatusPolicy do
  let(:policy) { described_class }
  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
  let(:john)    { Fabricate(:account) }
  let(:status) { Fabricate(:status) }
  let(:status) { Fabricate(:status, visibility: status_visibility) }
  let(:status_visibility) { :public }

  permissions :index?, :update?, :review?, :destroy? do
    context 'with an admin' do


@@ 26,7 27,7 @@ describe Admin::StatusPolicy do
  permissions :show? do
    context 'with an admin' do
      context 'with a public visible status' do
        before { allow(status).to receive(:public_visibility?).and_return(true) }
        let(:status_visibility) { :public }

        it 'permits' do
          expect(policy).to permit(admin, status)


@@ 34,11 35,21 @@ describe Admin::StatusPolicy do
      end

      context 'with a not public visible status' do
        before { allow(status).to receive(:public_visibility?).and_return(false) }
        let(:status_visibility) { :direct }

        it 'denies' do
          expect(policy).to_not permit(admin, status)
        end

        context 'when the status mentions the admin' do
          before do
            status.mentions.create!(account: admin)
          end

          it 'permits' do
            expect(policy).to permit(admin, status)
          end
        end
      end
    end


M yarn.lock => yarn.lock +27 -17
@@ 1718,9 1718,9 @@
  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==

"@jridgewell/source-map@^0.3.3":
  version "0.3.3"
  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
  integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==
  version "0.3.5"
  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91"
  integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==
  dependencies:
    "@jridgewell/gen-mapping" "^0.3.0"
    "@jridgewell/trace-mapping" "^0.3.9"


@@ 2903,16 2903,11 @@ acorn@^6.4.1:
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
  integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==

acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.9.0:
acorn@^8.0.4, acorn@^8.1.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0:
  version "8.10.0"
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
  integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==

acorn@^8.8.2:
  version "8.8.2"
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
  integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==

agent-base@6:
  version "6.0.2"
  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"


@@ 6107,11 6102,16 @@ fsevents@^1.2.7:
    bindings "^1.5.0"
    nan "^2.12.1"

fsevents@^2.3.2, fsevents@~2.3.2:
fsevents@^2.3.2:
  version "2.3.2"
  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==

fsevents@~2.3.2:
  version "2.3.3"
  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==

function-bind@^1.1.1:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"


@@ 11707,9 11707,9 @@ tapable@^2.2.0:
  integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==

tar@^6.0.2:
  version "6.1.15"
  resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
  integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
  version "6.2.0"
  resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
  integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
  dependencies:
    chownr "^2.0.0"
    fs-minipass "^2.0.0"


@@ 11755,7 11755,7 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
    terser "^5.3.4"
    webpack-sources "^1.4.3"

terser@^5.0.0, terser@^5.3.4:
terser@^5.0.0:
  version "5.18.0"
  resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.0.tgz#dc811fb8e3481a875d545bda247c8730ee4dc76b"
  integrity sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==


@@ 11765,6 11765,16 @@ terser@^5.0.0, terser@^5.3.4:
    commander "^2.20.0"
    source-map-support "~0.5.20"

terser@^5.3.4:
  version "5.19.4"
  resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.4.tgz#941426fa482bf9b40a0308ab2b3cd0cf7c775ebd"
  integrity sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==
  dependencies:
    "@jridgewell/source-map" "^0.3.3"
    acorn "^8.8.2"
    commander "^2.20.0"
    source-map-support "~0.5.20"

tesseract.js-core@^2.2.0:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz#6ef78051272a381969fac3e45a226e85022cffef"


@@ 12562,9 12572,9 @@ webpack-sources@^1.0, webpack-sources@^1.1.0, webpack-sources@^1.4.1, webpack-so
    source-map "~0.6.1"

webpack@^4.46.0:
  version "4.46.0"
  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
  integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
  version "4.47.0"
  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.47.0.tgz#8b8a02152d7076aeb03b61b47dad2eeed9810ebc"
  integrity sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==
  dependencies:
    "@webassemblyjs/ast" "1.9.0"
    "@webassemblyjs/helper-module-context" "1.9.0"