diff --git a/.codeclimate.yml b/.codeclimate.yml
index dc8ca9a6f..2d1de877c 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -30,7 +30,7 @@ plugins:
channel: eslint-7
rubocop:
enabled: true
- channel: rubocop-0-92
+ channel: rubocop-1-70
sass-lint:
enabled: true
exclude_patterns:
diff --git a/.rubocop.yml b/.rubocop.yml
index 14728bf0e..cb7c73ec7 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -2,17 +2,18 @@ require:
- rubocop-rails
AllCops:
- TargetRubyVersion: 2.4
+ TargetRubyVersion: 2.5
+ NewCops: disable
Exclude:
- - 'spec/**/*'
- - 'db/**/*'
- - 'app/views/**/*'
- - 'config/**/*'
- - 'bin/*'
- - 'Rakefile'
- - 'node_modules/**/*'
- - 'Vagrantfile'
- - 'vendor/**/*'
+ - 'spec/**/*'
+ - 'db/**/*'
+ - 'app/views/**/*'
+ - 'config/**/*'
+ - 'bin/*'
+ - 'Rakefile'
+ - 'node_modules/**/*'
+ - 'Vagrantfile'
+ - 'vendor/**/*'
- 'lib/json_ld/*'
- 'lib/templates/**/*'
diff --git a/Dockerfile b/Dockerfile
index 95d45bab4..136b2a35a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM ubuntu:20.04 as build-dep
# Use bash for the shell
-SHELL ["bash", "-c"]
+SHELL ["/usr/bin/bash", "-c"]
# Install Node v12 (LTS)
ENV NODE_VER="12.20.0"
diff --git a/Gemfile b/Gemfile
index cef134f27..75ecbda04 100644
--- a/Gemfile
+++ b/Gemfile
@@ -64,7 +64,7 @@ gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.3.1', require: 'mime/types/columnar'
gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b176939f851338d0a4971a532'
-gem 'nokogiri', '~> 1.10'
+gem 'nokogiri', '~> 1.11'
gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.10'
gem 'ox', '~> 2.14'
@@ -80,7 +80,7 @@ gem 'rails-settings-cached', '~> 0.6'
gem 'redis', '~> 4.2', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 1.2'
-gem 'ruby-progressbar', '~> 1.10'
+gem 'ruby-progressbar', '~> 1.11'
gem 'sanitize', '~> 5.2'
gem 'scenic', '~> 1.5'
gem 'sidekiq', '~> 6.1'
@@ -123,7 +123,7 @@ group :test do
gem 'microformats', '~> 4.2'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.1'
- gem 'simplecov', '~> 0.20', require: false
+ gem 'simplecov', '~> 0.21', require: false
gem 'webmock', '~> 3.11'
gem 'parallel_tests', '~> 3.4'
gem 'rspec_junit_formatter', '~> 0.4'
@@ -133,7 +133,7 @@ group :development do
gem 'active_record_query_trace', '~> 1.8'
gem 'annotate', '~> 3.1'
gem 'better_errors', '~> 2.9'
- gem 'binding_of_caller', '~> 0.7'
+ gem 'binding_of_caller', '~> 1.0'
gem 'bullet', '~> 6.1'
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.4'
diff --git a/Gemfile.lock b/Gemfile.lock
index 388a8477d..ab8a5ef98 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -100,7 +100,7 @@ GEM
erubi (>= 1.0.0)
rack (>= 0.9.0)
bindata (2.4.8)
- binding_of_caller (0.8.0)
+ binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
blurhash (0.1.4)
ffi (~> 1.10.0)
@@ -165,7 +165,7 @@ GEM
crass (1.0.6)
css_parser (1.7.1)
addressable
- debug_inspector (0.0.3)
+ debug_inspector (1.0.0)
devise (4.7.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
@@ -184,7 +184,7 @@ GEM
diff-lcs (1.4.4)
discard (1.2.0)
activerecord (>= 4.2, < 7)
- docile (1.3.2)
+ docile (1.3.4)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.4.0)
@@ -236,7 +236,7 @@ GEM
fugit (1.3.9)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.3)
- fuubar (2.5.0)
+ fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
globalid (0.4.2)
@@ -349,7 +349,7 @@ GEM
mime-types-data (3.2020.0512)
mimemagic (0.3.5)
mini_mime (1.0.2)
- mini_portile2 (2.4.0)
+ mini_portile2 (2.5.0)
minitest (5.14.2)
msgpack (1.3.3)
multi_json (1.15.0)
@@ -359,8 +359,9 @@ GEM
net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.1.0)
nio4r (2.5.4)
- nokogiri (1.10.10)
- mini_portile2 (~> 2.4.0)
+ nokogiri (1.11.0)
+ mini_portile2 (~> 2.5.0)
+ racc (~> 1.4)
nokogumbo (2.0.2)
nokogiri (~> 1.8, >= 1.8.4)
nsa (0.2.7)
@@ -433,6 +434,7 @@ GEM
pundit (2.1.0)
activesupport (>= 3.0.0)
raabro (1.3.3)
+ racc (1.5.2)
rack (2.2.3)
rack-attack (6.3.1)
rack (>= 1.0, < 3)
@@ -551,7 +553,7 @@ GEM
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 0.90.0, < 2.0)
- ruby-progressbar (1.10.1)
+ ruby-progressbar (1.11.0)
ruby-saml (1.11.0)
nokogiri (>= 1.5.10)
rufus-scheduler (3.6.0)
@@ -589,7 +591,7 @@ GEM
simple_form (5.0.3)
actionpack (>= 5.0)
activemodel (>= 5.0)
- simplecov (0.20.0)
+ simplecov (0.21.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
@@ -639,7 +641,7 @@ GEM
unf (~> 0.1.0)
tzinfo (1.2.9)
thread_safe (~> 0.1)
- tzinfo-data (1.2020.5)
+ tzinfo-data (1.2020.6)
tzinfo (>= 1.0.0)
unf (0.1.4)
unf_ext
@@ -688,7 +690,7 @@ DEPENDENCIES
annotate (~> 3.1)
aws-sdk-s3 (~> 1.87)
better_errors (~> 2.9)
- binding_of_caller (~> 0.7)
+ binding_of_caller (~> 1.0)
blurhash (~> 0.1)
bootsnap (~> 1.5)
brakeman (~> 4.10)
@@ -745,7 +747,7 @@ DEPENDENCIES
mime-types (~> 3.3.1)
net-ldap (~> 0.17)
nilsimsa!
- nokogiri (~> 1.10)
+ nokogiri (~> 1.11)
nsa (~> 0.2)
oj (~> 3.10)
omniauth (~> 1.9)
@@ -786,7 +788,7 @@ DEPENDENCIES
rspec_junit_formatter (~> 0.4)
rubocop (~> 1.7)
rubocop-rails (~> 2.9)
- ruby-progressbar (~> 1.10)
+ ruby-progressbar (~> 1.11)
sanitize (~> 5.2)
scenic (~> 1.5)
sidekiq (~> 6.1)
@@ -795,7 +797,7 @@ DEPENDENCIES
sidekiq-unique-jobs (~> 6.0)
simple-navigation (~> 4.1)
simple_form (~> 5.0)
- simplecov (~> 0.20)
+ simplecov (~> 0.21)
sprockets (~> 3.7.2)
sprockets-rails (~> 3.2)
stackprof
diff --git a/Procfile.dev b/Procfile.dev
index e589bbf63..ba04fb661 100644
--- a/Procfile.dev
+++ b/Procfile.dev
@@ -1,4 +1,4 @@
-web: env PORT=3000 bundle exec puma -C config/puma.rb
-sidekiq: env PORT=3000 bundle exec sidekiq
+web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
+sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
stream: env PORT=4000 yarn run start
webpack: ./bin/webpack-dev-server --listen-host 0.0.0.0
diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb
index d3044f180..92dcb5ac7 100644
--- a/app/controllers/activitypub/inboxes_controller.rb
+++ b/app/controllers/activitypub/inboxes_controller.rb
@@ -5,7 +5,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
include JsonLdHelper
include AccountOwnedConcern
- before_action :skip_unknown_actor_delete
+ before_action :skip_unknown_actor_activity
before_action :require_signature!
skip_before_action :authenticate_user!
@@ -18,13 +18,13 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
private
- def skip_unknown_actor_delete
- head 202 if unknown_deleted_account?
+ def skip_unknown_actor_activity
+ head 202 if unknown_affected_account?
end
- def unknown_deleted_account?
+ def unknown_affected_account?
json = Oj.load(body, mode: :strict)
- json.is_a?(Hash) && json['type'] == 'Delete' && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
+ json.is_a?(Hash) && %w(Delete Update).include?(json['type']) && json['actor'].present? && json['actor'] == value_or_id(json['object']) && !Account.where(uri: json['actor']).exists?
rescue Oj::ParseError
false
end
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 3e66ff212..953874e1a 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -42,7 +42,7 @@ class Api::V1::AccountsController < Api::BaseController
end
def mute
- MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration] || 0))
+ MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0))
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
end
diff --git a/app/controllers/api/v1/crypto/keys/claims_controller.rb b/app/controllers/api/v1/crypto/keys/claims_controller.rb
index 34b21a380..f9d202d67 100644
--- a/app/controllers/api/v1/crypto/keys/claims_controller.rb
+++ b/app/controllers/api/v1/crypto/keys/claims_controller.rb
@@ -12,7 +12,7 @@ class Api::V1::Crypto::Keys::ClaimsController < Api::BaseController
private
def set_claim_results
- @claim_results = devices.map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) }.compact
+ @claim_results = devices.filter_map { |device_params| ::Keys::ClaimService.new.call(current_account, device_params[:account_id], device_params[:device_id]) }
end
def resource_params
diff --git a/app/controllers/api/v1/crypto/keys/queries_controller.rb b/app/controllers/api/v1/crypto/keys/queries_controller.rb
index 0851d797d..e6ce9f919 100644
--- a/app/controllers/api/v1/crypto/keys/queries_controller.rb
+++ b/app/controllers/api/v1/crypto/keys/queries_controller.rb
@@ -17,7 +17,7 @@ class Api::V1::Crypto::Keys::QueriesController < Api::BaseController
end
def set_query_results
- @query_results = @accounts.map { |account| ::Keys::QueryService.new.call(account) }.compact
+ @query_results = @accounts.filter_map { |account| ::Keys::QueryService.new.call(account) }
end
def account_ids
diff --git a/app/controllers/api/v1/markers_controller.rb b/app/controllers/api/v1/markers_controller.rb
index 28c2ec791..867e6facf 100644
--- a/app/controllers/api/v1/markers_controller.rb
+++ b/app/controllers/api/v1/markers_controller.rb
@@ -7,7 +7,7 @@ class Api::V1::MarkersController < Api::BaseController
before_action :require_user!
def index
- @markers = current_user.markers.where(timeline: Array(params[:timeline])).each_with_object({}) { |marker, h| h[marker.timeline] = marker }
+ @markers = current_user.markers.where(timeline: Array(params[:timeline])).index_by(&:timeline)
render json: serialize_map(@markers)
end
diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb
index abbdb410a..3fb4b962a 100644
--- a/app/controllers/concerns/cache_concern.rb
+++ b/app/controllers/concerns/cache_concern.rb
@@ -38,14 +38,14 @@ module CacheConcern
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
unless uncached_ids.empty?
- uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item }
+ uncached = klass.where(id: uncached_ids).with_includes.index_by(&:id)
uncached.each_value do |item|
Rails.cache.write(item, item)
end
end
- raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
+ raw.filter_map { |item| cached_keys_with_value[item.id] || uncached[item.id] }
end
def cache_collection_paginated_by_id(raw, klass, limit, options)
diff --git a/app/controllers/settings/pictures_controller.rb b/app/controllers/settings/pictures_controller.rb
index 28df65f8f..58a432530 100644
--- a/app/controllers/settings/pictures_controller.rb
+++ b/app/controllers/settings/pictures_controller.rb
@@ -7,8 +7,12 @@ module Settings
def destroy
if valid_picture?
- msg = I18n.t('generic.changes_saved_msg') if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
- redirect_to settings_profile_path, notice: msg, status: 303
+ if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
+ ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
+ redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg'), status: 303
+ else
+ redirect_to settings_profile_path
+ end
else
bad_request
end
diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.js
index afed6868e..81f54578d 100644
--- a/app/javascript/mastodon/containers/media_container.js
+++ b/app/javascript/mastodon/containers/media_container.js
@@ -1,8 +1,8 @@
-import React, { PureComponent, Fragment } from 'react';
+import React, { Fragment, PureComponent } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
-import { IntlProvider, addLocaleData } from 'react-intl';
-import { List as ImmutableList, fromJS } from 'immutable';
+import { addLocaleData, IntlProvider } from 'react-intl';
+import { fromJS } from 'immutable';
import { getLocale } from 'mastodon/locales';
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
import MediaGallery from 'mastodon/components/media_gallery';
@@ -27,10 +27,11 @@ export default class MediaContainer extends PureComponent {
};
state = {
- media: null,
- index: null,
- time: null,
+ media : null,
+ index : null,
+ time : null,
backgroundColor: null,
+ options : null,
};
handleOpenMedia = (media, index) => {
@@ -38,26 +39,29 @@ export default class MediaContainer extends PureComponent {
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
this.setState({ media, index });
- }
+ };
- handleOpenVideo = (video, time) => {
- const media = ImmutableList([video]);
+ handleOpenVideo = (options) => {
+ const { components } = this.props;
+ const { media } = JSON.parse(components[options.componetIndex].getAttribute('data-props'));
+ const mediaList = fromJS(media);
document.body.classList.add('with-modals--active');
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
- this.setState({ media, time });
- }
+ this.setState({ media: mediaList, options });
+ };
handleCloseMedia = () => {
document.body.classList.remove('with-modals--active');
document.documentElement.style.marginRight = 0;
this.setState({
- media: null,
- index: null,
- time: null,
+ media : null,
+ index : null,
+ time : null,
backgroundColor: null,
+ options : null,
});
}
@@ -83,7 +87,8 @@ export default class MediaContainer extends PureComponent {
...(hashtag ? { hashtag: fromJS(hashtag) } : {}),
...(componentName === 'Video' ? {
- onOpenVideo: this.handleOpenVideo,
+ componetIndex: i,
+ onOpenVideo : this.handleOpenVideo,
} : {
onOpenMedia: this.handleOpenMedia,
}),
@@ -100,7 +105,9 @@ export default class MediaContainer extends PureComponent {
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index 36ed81fe2..2fcf982c2 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -92,7 +92,7 @@ class ComposeForm extends ImmutablePureComponent {
const fulltext = this.getFulltextForCharacterCounting();
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
- return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (isOnlyWhitespace && !anyMedia));
+ return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > this.props.maxTootCharsLimit || (isOnlyWhitespace && !anyMedia));
}
handleSubmit = () => {
@@ -258,8 +258,11 @@ class ComposeForm extends ImmutablePureComponent {
-
-
+
+
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index 02b018247..3596bd90c 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -6,11 +6,11 @@ import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnBackButton from '../../components/column_back_button';
import ColumnHeader from '../../components/column_header';
-import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
-import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
+import { addColumn, moveColumn, removeColumn } from '../../actions/columns';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { connectListStream } from '../../actions/streaming';
import { expandListTimeline } from '../../actions/timelines';
-import { fetchList, deleteList, updateList } from '../../actions/lists';
+import { deleteList, fetchList, updateList } from '../../actions/lists';
import { openModal } from '../../actions/modal';
import MissingIndicator from '../../components/missing_indicator';
import LoadingIndicator from '../../components/loading_indicator';
@@ -194,7 +194,14 @@ class ListTimeline extends React.PureComponent {
{ ['none', 'list', 'followed'].map(policy => (
-
+
))}
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index 578375a7f..74ae860ca 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import classNames from 'classnames';
import { changeUploadCompose, uploadThumbnail } from '../../../actions/compose';
import { getPointerPosition } from '../../video';
-import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import IconButton from 'mastodon/components/icon_button';
import Button from 'mastodon/components/button';
import Video from 'mastodon/features/video';
@@ -226,9 +226,9 @@ class FocalPointModal extends ImmutablePureComponent {
fetchTesseract().then(({ createWorker }) => {
const worker = createWorker({
workerPath: tesseractWorkerPath,
- corePath: tesseractCorePath,
- langPath: assetHost,
- logger: ({ status, progress }) => {
+ corePath : tesseractCorePath,
+ langPath : `${assetHost}/ocr/lang-data/`,
+ logger : ({ status, progress }) => {
if (status === 'recognizing text') {
this.setState({ ocrStatus: 'detecting', progress });
} else {
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js
index 7fe7ed094..b51179fd5 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -26,12 +26,15 @@ export default @injectIntl
class MediaModal extends ImmutablePureComponent {
static propTypes = {
- media: ImmutablePropTypes.list.isRequired,
- statusId: PropTypes.string,
- index: PropTypes.number.isRequired,
- onClose: PropTypes.func.isRequired,
- intl: PropTypes.object.isRequired,
+ media : ImmutablePropTypes.list.isRequired,
+ statusId : PropTypes.string,
+ index : PropTypes.number.isRequired,
+ onClose : PropTypes.func.isRequired,
+ intl : PropTypes.object.isRequired,
onChangeBackgroundColor: PropTypes.func.isRequired,
+ currentTime : PropTypes.number,
+ autoPlay : PropTypes.bool,
+ volume : PropTypes.number,
};
static contextTypes = {
@@ -183,7 +186,7 @@ class MediaModal extends ImmutablePureComponent {
/>
);
} else if (image.get('type') === 'video') {
- const { time } = this.props;
+ const { currentTime, autoPlay, volume } = this.props;
return (