Merge commit '06f0656a7b' into kb_development

This commit is contained in:
KMY 2023-05-26 15:19:15 +09:00
commit 7584ebbcc7
529 changed files with 5297 additions and 2819 deletions

View file

@ -98,10 +98,17 @@ module.exports = {
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
'react/jsx-boolean-value': 'error',
'react/display-name': 'off',
'react/jsx-fragments': ['error', 'syntax'],
'react/jsx-equals-spacing': 'error',
'react/jsx-no-bind': 'error',
'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-target-blank': 'off',
'react/jsx-tag-spacing': 'error',
'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/jsx-wrap-multilines': 'error',
'react/no-deprecated': 'off',
'react/no-unknown-property': 'off',
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error',
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/index.js
@ -191,6 +198,55 @@ module.exports = {
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/order': [
'error',
{
alphabetize: { order: 'asc' },
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
'parent',
['index', 'sibling'],
'object',
],
pathGroups: [
// React core packages
{
pattern: '{react,react-dom,react-dom/client,prop-types}',
group: 'builtin',
position: 'after',
},
// I18n
{
pattern: '{react-intl,intl-messageformat}',
group: 'builtin',
position: 'after',
},
// Common React utilities
{
pattern: '{classnames,react-helmet,react-router-dom}',
group: 'external',
position: 'before',
},
// Immutable / Redux / data store
{
pattern: '{immutable,react-redux,react-immutable-proptypes,react-immutable-pure-component,reselect}',
group: 'external',
position: 'before',
},
// Internal packages
{
pattern: '{mastodon/**}',
group: 'internal',
position: 'after',
},
],
pathGroupsExcludedImportTypes: [],
},
],
'promise/always-return': 'off',
'promise/catch-or-return': [
'error',
@ -279,55 +335,6 @@ module.exports = {
rules: {
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'import/order': [
'error',
{
alphabetize: { order: 'asc' },
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
'parent',
['index', 'sibling'],
'object',
],
pathGroups: [
// React core packages
{
pattern: '{react,react-dom,prop-types}',
group: 'builtin',
position: 'after',
},
// I18n
{
pattern: 'react-intl',
group: 'builtin',
position: 'after',
},
// Common React utilities
{
pattern: '{classnames,react-helmet}',
group: 'external',
position: 'before',
},
// Immutable / Redux / data store
{
pattern: '{immutable,react-redux,react-immutable-proptypes,react-immutable-pure-component,reselect}',
group: 'external',
position: 'before',
},
// Internal packages
{
pattern: '{mastodon/**}',
group: 'internal',
position: 'after',
},
],
pathGroupsExcludedImportTypes: [],
},
],
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
@ -339,6 +346,9 @@ module.exports = {
'import/no-default-export': 'warn',
'react/prefer-stateless-function': 'warn',
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/prop-types': 'off',
},
},
{

View file

@ -41,10 +41,6 @@ updates:
- dependency-name: 'webpack-dev-server'
versions:
- '>= 4'
# TODO: This version requires code changes https://github.com/reduxjs/react-redux/releases/tag/v8.0.0
- dependency-name: 'react-redux'
versions:
- '>= 4'
# TODO: This version was ignored in https://github.com/mastodon/mastodon/pull/15238
- dependency-name: 'webpack-cli'
versions:

View file

@ -43,7 +43,7 @@ Layout/LineLength:
- !ruby/regexp / \# .*$/
- !ruby/regexp /^\# .*$/
Exclude:
- lib/**/*cli*.rb
- 'lib/mastodon/cli/*.rb'
- db/*migrate/**/*
- db/seeds/**/*
@ -58,7 +58,7 @@ Lint/UselessAccessModifier:
Metrics/AbcSize:
Exclude:
- 'app/serializers/initial_state_serializer.rb'
- 'lib/**/*cli*.rb'
- 'lib/mastodon/cli/*.rb'
- db/*migrate/**/*
# Reason: Some functions cannot be broken up, but others may be refactor candidates
@ -67,7 +67,7 @@ Metrics/BlockLength:
CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
Exclude:
- 'config/routes.rb'
- 'lib/mastodon/*_cli.rb'
- 'lib/mastodon/cli/*.rb'
- 'lib/tasks/*.rake'
- 'app/models/concerns/account_associations.rb'
- 'app/models/concerns/account_interactions.rb'
@ -96,14 +96,14 @@ Metrics/BlockLength:
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting
Metrics/BlockNesting:
Exclude:
- 'lib/mastodon/*_cli.rb'
- 'lib/mastodon/cli/*.rb'
# Reason: Some Excluded files would be candidates for refactoring but not currently addressed
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
Metrics/ClassLength:
CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
Exclude:
- 'lib/mastodon/*_cli.rb'
- 'lib/mastodon/cli/*.rb'
- 'app/controllers/admin/accounts_controller.rb'
- 'app/controllers/api/base_controller.rb'
- 'app/controllers/api/v1/admin/accounts_controller.rb'
@ -157,7 +157,7 @@ Metrics/CyclomaticComplexity:
- 'app/policies/status_policy.rb'
- 'app/services/activitypub/process_account_service.rb'
- 'app/services/post_status_service.rb'
- lib/mastodon/*cli*.rb
- lib/mastodon/cli/*.rb
- db/*migrate/**/*
# Reason: Currently disabled in .rubocop_todo.yml
@ -165,7 +165,7 @@ Metrics/CyclomaticComplexity:
Metrics/MethodLength:
CountAsOne: [array, heredoc]
Exclude:
- 'lib/mastodon/*_cli.rb'
- 'lib/mastodon/cli/*.rb'
# Reason:
# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
@ -186,13 +186,12 @@ Rails/FilePath:
Rails/HttpStatus:
EnforcedStyle: numeric
# Reason: Allowed only in the `tootctl` CLI application code
# Reason: Allowed in `tootctl` CLI code and in boot ENV checker
# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
Rails/Exit:
Exclude:
- 'lib/mastodon/*_cli.rb'
- 'lib/mastodon/cli_helper.rb'
- 'lib/cli.rb'
- 'config/boot.rb'
- 'lib/mastodon/cli/*.rb'
# Reason: Some single letter camel case files shouldn't be split
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath

View file

@ -21,12 +21,6 @@ Layout/ArgumentAlignment:
- 'config/initializers/cors.rb'
- 'config/initializers/session_store.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Exclude:
- 'config/initializers/omniauth.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
# SupportedHashRocketStyles: key, separator, table
@ -39,12 +33,6 @@ Layout/HashAlignment:
- 'config/initializers/rack_attack.rb'
- 'config/routes.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Width, AllowedPatterns.
Layout/IndentationWidth:
Exclude:
- 'config/initializers/ffmpeg.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment.
Layout/LeadingCommentSpace:
@ -52,14 +40,6 @@ Layout/LeadingCommentSpace:
- 'config/application.rb'
- 'config/initializers/omniauth.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
# SupportedStyles: space, no_space
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceBeforeBlockBraces:
Exclude:
- 'config/initializers/paperclip.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: require_no_space, require_space
@ -68,19 +48,6 @@ Layout/SpaceInLambdaLiteral:
- 'config/environments/production.rb'
- 'config/initializers/content_security_policy.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: space, no_space
Layout/SpaceInsideStringInterpolation:
Exclude:
- 'config/initializers/webauthn.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowInHeredoc.
Layout/TrailingWhitespace:
Exclude:
- 'config/initializers/paperclip.rb'
# Configuration parameters: AllowedMethods, AllowedPatterns.
Lint/AmbiguousBlockAssociation:
Exclude:
@ -269,31 +236,6 @@ Naming/VariableNumber:
- 'spec/models/user_spec.rb'
- 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Performance/MapCompact:
Exclude:
- 'app/lib/admin/metrics/dimension.rb'
- 'app/lib/admin/metrics/measure.rb'
- 'app/lib/feed_manager.rb'
- 'app/models/account.rb'
- 'app/models/account_statuses_cleanup_policy.rb'
- 'app/models/account_suggestions/setting_source.rb'
- 'app/models/account_suggestions/source.rb'
- 'app/models/follow_recommendation_filter.rb'
- 'app/models/notification.rb'
- 'app/models/user_role.rb'
- 'app/models/webhook.rb'
- 'app/services/process_mentions_service.rb'
- 'app/validators/existing_username_validator.rb'
- 'db/migrate/20200407202420_migrate_unavailable_inboxes.rb'
- 'spec/presenters/status_relationships_presenter_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeMultiline.
Performance/StartWith:
Exclude:
- 'app/lib/extractor.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Performance/UnfreezeString:
Exclude:
@ -434,29 +376,6 @@ RSpec/EmptyExampleGroup:
RSpec/ExampleLength:
Max: 22
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: method_call, block
RSpec/ExpectChange:
Exclude:
- 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
- 'spec/controllers/admin/custom_emojis_controller_spec.rb'
- 'spec/controllers/admin/invites_controller_spec.rb'
- 'spec/controllers/admin/report_notes_controller_spec.rb'
- 'spec/controllers/concerns/accountable_concern_spec.rb'
- 'spec/controllers/invites_controller_spec.rb'
- 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
- 'spec/models/admin/account_action_spec.rb'
- 'spec/services/suspend_account_service_spec.rb'
- 'spec/services/unsuspend_account_service_spec.rb'
- 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
RSpec/ExpectInHook:
Exclude:
- 'spec/controllers/api/v1/media_controller_spec.rb'
- 'spec/controllers/settings/applications_controller_spec.rb'
- 'spec/lib/status_filter_spec.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: implicit, each, example
@ -618,26 +537,8 @@ RSpec/NoExpectationExample:
RSpec/PendingWithoutReason:
Exclude:
- 'spec/controllers/statuses_controller_spec.rb'
- 'spec/models/account_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
# SupportedStyles: inflected, explicit
RSpec/PredicateMatcher:
Exclude:
- 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
- 'spec/models/user_spec.rb'
- 'spec/services/post_status_service_spec.rb'
RSpec/RepeatedExample:
Exclude:
- 'spec/policies/status_policy_spec.rb'
RSpec/RepeatedExampleGroupBody:
Exclude:
- 'spec/controllers/statuses_controller_spec.rb'
RSpec/StubbedMock:
Exclude:
- 'spec/controllers/api/base_controller_spec.rb'
@ -779,12 +680,6 @@ Rails/DuplicateAssociation:
- 'app/serializers/activitypub/collection_serializer.rb'
- 'app/serializers/activitypub/note_serializer.rb'
# Configuration parameters: Include.
# Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb
Rails/Exit:
Exclude:
- 'config/boot.rb'
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/HasAndBelongsToMany:
@ -919,9 +814,9 @@ Rails/SkipsModelValidations:
- 'db/post_migrate/20220617202502_migrate_roles.rb'
- 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
- 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
- 'lib/cli.rb'
- 'lib/mastodon/accounts_cli.rb'
- 'lib/mastodon/maintenance_cli.rb'
- 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/main.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
- 'spec/lib/activitypub/activity/follow_spec.rb'
- 'spec/services/follow_service_spec.rb'
@ -1008,7 +903,7 @@ Rails/WhereExists:
- 'app/validators/vote_validator.rb'
- 'app/workers/move_worker.rb'
- 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
- 'lib/mastodon/email_domain_blocks_cli.rb'
- 'lib/mastodon/cli/email_domain_blocks.rb'
- 'lib/tasks/tests.rake'
- 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
- 'spec/controllers/api/v1/tags_controller_spec.rb'
@ -1070,7 +965,7 @@ Style/FormatStringToken:
Exclude:
- 'app/models/privacy_policy.rb'
- 'config/initializers/devise.rb'
- 'lib/mastodon/maintenance_cli.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'lib/paperclip/color_extractor.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
@ -1515,9 +1410,9 @@ Style/GuardClause:
- 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb'
- 'lib/devise/two_factor_ldap_authenticatable.rb'
- 'lib/devise/two_factor_pam_authenticatable.rb'
- 'lib/mastodon/accounts_cli.rb'
- 'lib/mastodon/maintenance_cli.rb'
- 'lib/mastodon/media_cli.rb'
- 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'lib/mastodon/cli/media.rb'
- 'lib/paperclip/attachment_extensions.rb'
- 'lib/tasks/repo.rake'

View file

@ -17,7 +17,7 @@ gem 'makara', '~> 0.5'
gem 'pghero'
gem 'dotenv-rails', '~> 2.8'
gem 'aws-sdk-s3', '~> 1.120', require: false
gem 'aws-sdk-s3', '~> 1.122', require: false
gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b'
@ -75,7 +75,7 @@ gem 'rails-settings-cached', '~> 0.6', git: 'https://github.com/mastodon/rails-s
gem 'redcarpet', '~> 3.6'
gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 2.1'
gem 'rqrcode', '~> 2.2'
gem 'ruby-progressbar', '~> 1.13'
gem 'sanitize', '~> 6.0'
gem 'scenic', '~> 1.7'

View file

@ -109,16 +109,16 @@ GEM
attr_required (1.0.1)
awrence (1.2.1)
aws-eventstream (1.2.0)
aws-partitions (1.752.0)
aws-sdk-core (3.171.0)
aws-partitions (1.761.0)
aws-sdk-core (3.172.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.63.0)
aws-sdk-kms (1.64.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.121.0)
aws-sdk-s3 (1.122.0)
aws-sdk-core (~> 3, >= 3.165.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
@ -151,7 +151,7 @@ GEM
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
capistrano (3.17.2)
capistrano (3.17.3)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
@ -189,7 +189,7 @@ GEM
coderay (1.1.3)
color_diff (0.1)
concurrent-ruby (1.2.2)
connection_pool (2.4.0)
connection_pool (2.4.1)
cose (1.3.0)
cbor (~> 0.5.9)
openssl-signature_algorithm (~> 1.0)
@ -269,7 +269,7 @@ GEM
faraday-rack (1.0.0)
faraday-retry (1.0.3)
fast_blank (1.0.1)
fastimage (2.2.6)
fastimage (2.2.7)
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
@ -398,9 +398,9 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.20.0)
loofah (2.21.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
nokogiri (>= 1.12.0)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
@ -576,7 +576,7 @@ GEM
rexml (3.2.5)
rotp (6.2.2)
rpam2 (4.0.2)
rqrcode (2.1.2)
rqrcode (2.2.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
@ -588,20 +588,20 @@ GEM
rspec-mocks (3.12.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.1)
rspec-rails (6.0.2)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.11)
rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-core (~> 3.12)
rspec-expectations (~> 3.12)
rspec-mocks (~> 3.12)
rspec-support (~> 3.12)
rspec-sidekiq (3.1.0)
rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0)
rspec-support (3.12.0)
rspec_chunked (0.6)
rubocop (1.50.2)
rubocop (1.51.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.2.0.0)
@ -611,11 +611,11 @@ GEM
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.28.0)
rubocop-ast (1.28.1)
parser (>= 3.2.1.0)
rubocop-capybara (2.18.0)
rubocop (~> 1.41)
rubocop-performance (1.17.1)
rubocop-performance (1.18.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.19.1)
@ -761,7 +761,7 @@ GEM
xorcist (1.1.3)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.7)
zeitwerk (2.6.8)
PLATFORMS
ruby
@ -770,7 +770,7 @@ DEPENDENCIES
active_model_serializers (~> 0.10)
addressable (~> 2.8)
annotate (~> 3.2)
aws-sdk-s3 (~> 1.120)
aws-sdk-s3 (~> 1.122)
better_errors (~> 2.9)
binding_of_caller (~> 1.0)
blurhash (~> 0.1)
@ -860,7 +860,7 @@ DEPENDENCIES
redcarpet (~> 3.6)
redis (~> 4.5)
redis-namespace (~> 1.10)
rqrcode (~> 2.1)
rqrcode (~> 2.2)
rspec-rails (~> 6.0)
rspec-sidekiq (~> 3.1)
rspec_chunked (~> 0.6)
@ -894,3 +894,9 @@ DEPENDENCIES
webpacker (~> 5.4)
webpush!
xorcist (~> 1.1)
RUBY VERSION
ruby 3.2.2p53
BUNDLED WITH
2.4.10

View file

@ -29,7 +29,7 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
def create
authorize :domain_allow, :create?
@domain_allow = DomainAllow.find_by(resource_params)
@domain_allow = DomainAllow.find_by(domain: resource_params[:domain])
if @domain_allow.nil?
@domain_allow = DomainAllow.create!(resource_params)

View file

@ -13,7 +13,7 @@ class Api::V1::FeaturedTagsController < Api::BaseController
end
def create
featured_tag = CreateFeaturedTagService.new.call(current_account, featured_tag_params[:name])
featured_tag = CreateFeaturedTagService.new.call(current_account, params.require(:name))
render json: featured_tag, serializer: REST::FeaturedTagSerializer
end
@ -31,8 +31,4 @@ class Api::V1::FeaturedTagsController < Api::BaseController
def set_featured_tags
@featured_tags = current_account.featured_tags.order(statuses_count: :desc)
end
def featured_tag_params
params.permit(:name)
end
end

View file

@ -0,0 +1,45 @@
# frozen_string_literal: true
class Api::V1::ReactionDeckController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index]
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, only: [:create]
before_action :require_user!
before_action :set_deck, only: [:index, :create]
rescue_from ArgumentError do |e|
render json: { error: e.to_s }, status: 422
end
def index
render json: @deck
end
def create
(deck_params['emojis'] || []).each do |data|
raise ArgumentError if data['id'].to_i >= 16 || data['id'].to_i.negative?
exists = @deck.find { |dd| dd['id'] == data['id'] }
if exists
exists['emoji'] = data['emoji'].delete(':')
else
@deck << { id: data['id'], emoji: data['emoji'].delete(':') }
end
end
@deck = @deck.sort_by { |a| a['id'].to_i }
current_user.settings['reaction_deck'] = @deck.to_json
current_user.save!
render json: @deck
end
private
def set_deck
@deck = current_user.setting_reaction_deck ? JSON.parse(current_user.setting_reaction_deck) : []
end
def deck_params
params
end
end

View file

@ -127,7 +127,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end
def set_sessions
@sessions = current_user.session_activations
@sessions = current_user.session_activations.order(updated_at: :desc)
end
def set_strikes

View file

@ -45,6 +45,6 @@ class MediaController < ApplicationController
end
def allow_iframing
response.headers['X-Frame-Options'] = 'ALLOWALL'
response.headers.delete('X-Frame-Options')
end
end

View file

@ -9,6 +9,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :set_body_classes
before_action :set_cache_headers
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
skip_before_action :require_functional!
include Localized
@ -35,4 +37,14 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def set_cache_headers
response.cache_control.replace(private: true, no_store: true)
end
def set_last_used_at_by_app
@last_used_at_by_app = Doorkeeper::AccessToken
.select('DISTINCT ON (application_id) application_id, last_used_at')
.where(resource_owner_id: current_resource_owner.id)
.where.not(last_used_at: nil)
.order(application_id: :desc, last_used_at: :desc)
.pluck(:application_id, :last_used_at)
.to_h
end
end

View file

@ -45,7 +45,7 @@ class StatusesController < ApplicationController
return not_found if @status.hidden? || @status.reblog?
expires_in 180, public: true
response.headers['X-Frame-Options'] = 'ALLOWALL'
response.headers.delete('X-Frame-Options')
render layout: 'embedded'
end

View file

@ -52,7 +52,7 @@ module ApplicationHelper
if closed_registrations? || omniauth_only?
'https://joinmastodon.org/#getting-started'
else
new_user_registration_path
ENV.fetch('SSO_ACCOUNT_SIGN_UP', new_user_registration_path)
end
end

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { importFetchedAccount, importFetchedAccounts } from './importer';
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { normalizeAnnouncement } from './importer/normalizer';
export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
import { openModal } from './modal';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';

View file

@ -1,9 +1,12 @@
import { defineMessages } from 'react-intl';
import axios from 'axios';
import { throttle } from 'lodash';
import { defineMessages } from 'react-intl';
import api from 'mastodon/api';
import { search as emojiSearch } from 'mastodon/features/emoji/emoji_mart_search_light';
import { tagHistory } from 'mastodon/settings';
import { showAlert, showAlertForError } from './alerts';
import { useEmoji } from './emojis';
import { importFetchedAccounts, importFetchedStatus } from './importer';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import {
importFetchedAccounts,
importFetchedStatuses,

View file

@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
export const DIRECTORY_FETCH_REQUEST = 'DIRECTORY_FETCH_REQUEST';
export const DIRECTORY_FETCH_SUCCESS = 'DIRECTORY_FETCH_SUCCESS';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { openModal } from './modal';
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
export const HISTORY_FETCH_REQUEST = 'HISTORY_FETCH_REQUEST';

View file

@ -1,7 +1,8 @@
import escapeTextContentForBrowser from 'escape-html';
import emojify from '../../features/emoji/emoji';
import { unescapeHTML } from '../../utils/html';
import { expandSpoilers, me } from '../../initial_state';
import { unescapeHTML } from '../../utils/html';
const domParser = new DOMParser();
@ -91,7 +92,6 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.spoiler_text = '';
}
console.dir(normalStatus.emojis);
if (normalStatus.emojis && normalStatus.emojis.some((emoji) => emoji.is_sensitive) && !normalStatus.spoiler_text) {
normalStatus.spoiler_text = '[Contains sensitive custom emoji(s)]';
}

View file

@ -1,4 +1,5 @@
import api from '../api';
import { importFetchedAccounts, importFetchedStatus } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';

View file

@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
import { showAlertForError } from './alerts';
import { importFetchedAccounts } from './importer';
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';

View file

@ -1,8 +1,10 @@
import api from '../api';
import { debounce } from 'lodash';
import { compareId } from '../compare_id';
import { List as ImmutableList } from 'immutable';
import { debounce } from 'lodash';
import api from '../api';
import { compareId } from '../compare_id';
export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
export const MARKERS_FETCH_FAIL = 'MARKERS_FETCH_FAIL';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
import { openModal } from './modal';

View file

@ -1,5 +1,15 @@
import api, { getLinks } from '../api';
import IntlMessageFormat from 'intl-messageformat';
import { defineMessages } from 'react-intl';
import { List as ImmutableList } from 'immutable';
import { compareId } from 'mastodon/compare_id';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
import api, { getLinks } from '../api';
import { unescapeHTML } from '../utils/html';
import { requestNotificationPermission } from '../utils/notifications';
import { fetchFollowRequests, fetchRelationships } from './accounts';
import {
importFetchedAccount,
@ -9,12 +19,6 @@ import {
} from './importer';
import { submitMarkers } from './markers';
import { saveSettings } from './settings';
import { defineMessages } from 'react-intl';
import { List as ImmutableList } from 'immutable';
import { unescapeHTML } from '../utils/html';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
import { compareId } from 'mastodon/compare_id';
import { requestNotificationPermission } from '../utils/notifications';
import { STATUS_EMOJI_REACTION_UPDATE } from './statuses';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';

View file

@ -1,8 +1,8 @@
import api from '../api';
import { importFetchedStatuses } from './importer';
import { me } from '../initial_state';
import { importFetchedStatuses } from './importer';
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { importFetchedPoll } from './importer';
export const POLL_VOTE_REQUEST = 'POLL_VOTE_REQUEST';

View file

@ -1,5 +1,5 @@
import { setAlerts } from './setter';
import { saveSettings } from './registerer';
import { setAlerts } from './setter';
export function changeAlerts(path, value) {
return dispatch => {

View file

@ -1,8 +1,9 @@
import api from '../../api';
import { decode as decodeBase64 } from '../../utils/base64';
import { pushNotificationsSetting } from '../../settings';
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
import { me } from '../../initial_state';
import { pushNotificationsSetting } from '../../settings';
import { decode as decodeBase64 } from '../../utils/base64';
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
// Taken from https://www.npmjs.com/package/web-push
const urlBase64ToUint8Array = (base64String) => {

View file

@ -0,0 +1,79 @@
import api from '../api';
export const REACTION_DECK_FETCH_REQUEST = 'REACTION_DECK_FETCH_REQUEST';
export const REACTION_DECK_FETCH_SUCCESS = 'REACTION_DECK_FETCH_SUCCESS';
export const REACTION_DECK_FETCH_FAIL = 'REACTION_DECK_FETCH_FAIL';
export const REACTION_DECK_UPDATE_REQUEST = 'REACTION_DECK_UPDATE_REQUEST';
export const REACTION_DECK_UPDATE_SUCCESS = 'REACTION_DECK_UPDATE_SUCCESS';
export const REACTION_DECK_UPDATE_FAIL = 'REACTION_DECK_UPDATE_FAIL';
export function fetchReactionDeck() {
return (dispatch, getState) => {
dispatch(fetchReactionDeckRequest());
api(getState).get('/api/v1/reaction_deck').then(response => {
dispatch(fetchReactionDeckSuccess(response.data));
}).catch(error => {
dispatch(fetchReactionDeckFail(error));
});
};
}
export function fetchReactionDeckRequest() {
return {
type: REACTION_DECK_FETCH_REQUEST,
skipLoading: true,
};
}
export function fetchReactionDeckSuccess(emojis) {
return {
type: REACTION_DECK_FETCH_SUCCESS,
emojis,
skipLoading: true,
};
}
export function fetchReactionDeckFail(error) {
return {
type: REACTION_DECK_FETCH_FAIL,
error,
skipLoading: true,
};
}
export function updateReactionDeck(id, emoji) {
return (dispatch, getState) => {
dispatch(updateReactionDeckRequest());
api(getState).post('/api/v1/reaction_deck', { emojis: [{ id, emoji: emoji.native || emoji.id }] }).then(response => {
dispatch(updateReactionDeckSuccess(response.data));
}).catch(error => {
dispatch(updateReactionDeckFail(error));
});
};
}
export function updateReactionDeckRequest() {
return {
type: REACTION_DECK_UPDATE_REQUEST,
skipLoading: true,
};
}
export function updateReactionDeckSuccess(emojis) {
return {
type: REACTION_DECK_UPDATE_SUCCESS,
emojis,
skipLoading: true,
};
}
export function updateReactionDeckFail(error) {
return {
type: REACTION_DECK_UPDATE_FAIL,
error,
skipLoading: true,
};
}

View file

@ -1,4 +1,5 @@
import api from '../api';
import { openModal } from './modal';
export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatuses } from './importer';

View file

@ -1,4 +1,5 @@
import api from '../api';
import { importFetchedAccount } from './importer';
export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';

View file

@ -1,5 +1,7 @@
import api from '../api';
import { debounce } from 'lodash';
import api from '../api';
import { showAlertForError } from './alerts';
export const SETTING_CHANGE = 'SETTING_CHANGE';

View file

@ -1,8 +1,8 @@
import api from '../api';
import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer';
import { ensureComposeIsVisible, setComposeToStatus } from './compose';
import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer';
import { deleteFromTimelines } from './timelines';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';

View file

@ -1,4 +1,5 @@
import { Iterable, fromJS } from 'immutable';
import { hydrateCompose } from './compose';
import { importFetchedAccounts } from './importer';

View file

@ -1,6 +1,17 @@
// @ts-check
import { getLocale } from '../locales';
import { connectStream } from '../stream';
import {
fetchAnnouncements,
updateAnnouncements,
updateReaction as updateAnnouncementsReaction,
deleteAnnouncement,
} from './announcements';
import { updateConversations } from './conversations';
import { updateNotifications, expandNotifications, updateEmojiReactions } from './notifications';
import { updateStatus } from './statuses';
import {
updateTimeline,
deleteFromTimelines,
@ -12,16 +23,6 @@ import {
fillCommunityTimelineGaps,
fillListTimelineGaps,
} from './timelines';
import { updateNotifications, expandNotifications, updateEmojiReactions } from './notifications';
import { updateConversations } from './conversations';
import { updateStatus } from './statuses';
import {
fetchAnnouncements,
updateAnnouncements,
updateReaction as updateAnnouncementsReaction,
deleteAnnouncement,
} from './announcements';
import { getLocale } from '../locales';
const { messages } = getLocale();

View file

@ -1,6 +1,7 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST';
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS';

View file

@ -1,10 +1,12 @@
import { importFetchedStatus, importFetchedStatuses } from './importer';
import { submitMarkers } from './markers';
import api, { getLinks } from 'mastodon/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import api, { getLinks } from 'mastodon/api';
import { compareId } from 'mastodon/compare_id';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
import { importFetchedStatus, importFetchedStatuses } from './importer';
import { submitMarkers } from './markers';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';

View file

@ -1,4 +1,5 @@
import api, { getLinks } from '../api';
import { importFetchedStatuses } from './importer';
export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';

View file

@ -2,6 +2,7 @@
import axios from 'axios';
import LinkHeader from 'http-link-header';
import ready from './ready';
/**

View file

@ -1,5 +1,5 @@
import React from 'react';
import renderer from 'react-test-renderer';
import AutosuggestEmoji from '../autosuggest_emoji';
describe('<AutosuggestEmoji />', () => {

View file

@ -1,6 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { fromJS } from 'immutable';
import renderer from 'react-test-renderer';
import { Avatar } from '../avatar';
describe('<Avatar />', () => {

View file

@ -1,6 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { fromJS } from 'immutable';
import renderer from 'react-test-renderer';
import { AvatarOverlay } from '../avatar_overlay';
describe('<AvatarOverlay', () => {

View file

@ -1,6 +1,6 @@
import { render, fireEvent, screen } from '@testing-library/react';
import React from 'react';
import renderer from 'react-test-renderer';
import Button from '../button';
describe('<Button />', () => {

View file

@ -1,6 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { fromJS } from 'immutable';
import renderer from 'react-test-renderer';
import { DisplayName } from '../display_name';
describe('<DisplayName />', () => {

View file

@ -1,24 +1,29 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { counterRenderer } from 'mastodon/components/common_counter';
import { EmptyAccount } from 'mastodon/components/empty_account';
import ShortNumber from 'mastodon/components/short_number';
import { VerifiedBadge } from 'mastodon/components/verified_badge';
import { me } from '../initial_state';
import { Avatar } from './avatar';
import { DisplayName } from './display_name';
import { IconButton } from './icon_button';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me } from '../initial_state';
import { RelativeTimestamp } from './relative_timestamp';
import { Link } from 'react-router-dom';
import { counterRenderer } from 'mastodon/components/common_counter';
import ShortNumber from 'mastodon/components/short_number';
import classNames from 'classnames';
import { VerifiedBadge } from 'mastodon/components/verified_badge';
import { EmptyAccount } from 'mastodon/components/empty_account';
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },

View file

@ -1,10 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import api from 'mastodon/api';
import { PureComponent } from 'react';
import { FormattedNumber } from 'react-intl';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import classNames from 'classnames';
import Skeleton from 'mastodon/components/skeleton';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import api from 'mastodon/api';
import { Skeleton } from 'mastodon/components/skeleton';
const percIncrease = (a, b) => {
let percent;
@ -24,7 +28,7 @@ const percIncrease = (a, b) => {
return percent;
};
export default class Counter extends React.PureComponent {
export default class Counter extends PureComponent {
static propTypes = {
measure: PropTypes.string.isRequired,
@ -62,25 +66,25 @@ export default class Counter extends React.PureComponent {
if (loading) {
content = (
<React.Fragment>
<>
<span className='sparkline__value__total'><Skeleton width={43} /></span>
<span className='sparkline__value__change'><Skeleton width={43} /></span>
</React.Fragment>
</>
);
} else {
const measure = data[0];
const percentChange = measure.previous_total && percIncrease(measure.previous_total * 1, measure.total * 1);
content = (
<React.Fragment>
<>
<span className='sparkline__value__total'>{measure.human_value || <FormattedNumber value={measure.total} />}</span>
{measure.previous_total && (<span className={classNames('sparkline__value__change', { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'}<FormattedNumber value={percentChange} style='percent' /></span>)}
</React.Fragment>
</>
);
}
const inner = (
<React.Fragment>
<>
<div className='sparkline__value'>
{content}
</div>
@ -96,7 +100,7 @@ export default class Counter extends React.PureComponent {
</Sparklines>
)}
</div>
</React.Fragment>
</>
);
if (href) {

View file

@ -1,11 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import api from 'mastodon/api';
import { FormattedNumber } from 'react-intl';
import { roundTo10 } from 'mastodon/utils/numbers';
import Skeleton from 'mastodon/components/skeleton';
import { PureComponent } from 'react';
export default class Dimension extends React.PureComponent {
import { FormattedNumber } from 'react-intl';
import api from 'mastodon/api';
import { Skeleton } from 'mastodon/components/skeleton';
import { roundTo10 } from 'mastodon/utils/numbers';
export default class Dimension extends PureComponent {
static propTypes = {
dimension: PropTypes.string.isRequired,

View file

@ -1,16 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import api from 'mastodon/api';
import { PureComponent } from 'react';
import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import api from 'mastodon/api';
const messages = defineMessages({
other: { id: 'report.categories.other', defaultMessage: 'Other' },
spam: { id: 'report.categories.spam', defaultMessage: 'Spam' },
violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
});
class Category extends React.PureComponent {
class Category extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
@ -52,7 +55,7 @@ class Category extends React.PureComponent {
}
class Rule extends React.PureComponent {
class Rule extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
@ -84,7 +87,7 @@ class Rule extends React.PureComponent {
}
class ReportReasonSelector extends React.PureComponent {
class ReportReasonSelector extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,

View file

@ -1,8 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import api from 'mastodon/api';
import { PureComponent } from 'react';
import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
import classNames from 'classnames';
import api from 'mastodon/api';
import { roundTo10 } from 'mastodon/utils/numbers';
const dateForCohort = cohort => {
@ -14,7 +17,7 @@ const dateForCohort = cohort => {
}
};
export default class Retention extends React.PureComponent {
export default class Retention extends PureComponent {
static propTypes = {
start_at: PropTypes.string,

View file

@ -1,11 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import api from 'mastodon/api';
import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import api from 'mastodon/api';
import Hashtag from 'mastodon/components/hashtag';
export default class Trends extends React.PureComponent {
export default class Trends extends PureComponent {
static propTypes = {
limit: PropTypes.number.isRequired,

View file

@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import { TransitionMotion, spring } from 'react-motion';

View file

@ -1,9 +1,12 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Icon } from 'mastodon/components/icon';
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];

View file

@ -1,9 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light';
import { PureComponent } from 'react';
import { assetHost } from 'mastodon/utils/config';
export default class AutosuggestEmoji extends React.PureComponent {
import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light';
export default class AutosuggestEmoji extends PureComponent {
static propTypes = {
emoji: PropTypes.object.isRequired,

View file

@ -1,9 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import ShortNumber from 'mastodon/components/short_number';
import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
export default class AutosuggestHashtag extends React.PureComponent {
import ShortNumber from 'mastodon/components/short_number';
export default class AutosuggestHashtag extends PureComponent {
static propTypes = {
tag: PropTypes.shape({

View file

@ -1,11 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import AutosuggestEmoji from './autosuggest_emoji';
import AutosuggestHashtag from './autosuggest_hashtag';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import classNames from 'classnames';
const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => {
let word;

View file

@ -1,12 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import AutosuggestEmoji from './autosuggest_emoji';
import AutosuggestHashtag from './autosuggest_hashtag';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
import classNames from 'classnames';
const textAtCursorMatchesToken = (str, caretPosition) => {
let word;

View file

@ -1,5 +1,3 @@
import * as React from 'react';
import classNames from 'classnames';
import { useHovering } from '../../hooks/useHovering';

View file

@ -1,10 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { autoPlayGif } from '../initial_state';
import { Avatar } from './avatar';
export default class AvatarComposite extends React.PureComponent {
export default class AvatarComposite extends PureComponent {
static propTypes = {
accounts: ImmutablePropTypes.list.isRequired,

View file

@ -1,5 +1,3 @@
import React from 'react';
import { useHovering } from '../../hooks/useHovering';
import type { Account } from '../../types/resources';
import { autoPlayGif } from '../initial_state';

View file

@ -1,4 +1,4 @@
import React, { useRef, useEffect } from 'react';
import { memo, useRef, useEffect } from 'react';
import { decode } from 'blurhash';
@ -43,6 +43,6 @@ const Blurhash: React.FC<Props> = ({
);
};
const MemoizedBlurhash = React.memo(Blurhash);
const MemoizedBlurhash = memo(Blurhash);
export { MemoizedBlurhash as Blurhash };

View file

@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import classNames from 'classnames';
export default class Button extends React.PureComponent {
export default class Button extends PureComponent {
static propTypes = {
text: PropTypes.node,

View file

@ -1,5 +1,3 @@
import React from 'react';
export const Check: React.FC = () => (
<svg
xmlns='http://www.w3.org/2000/svg'

View file

@ -1,9 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { supportsPassiveEvents } from 'detect-passive-events';
import { scrollTop } from '../scroll';
export default class Column extends React.PureComponent {
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
export default class Column extends PureComponent {
static propTypes = {
children: PropTypes.node,
@ -35,17 +39,17 @@ export default class Column extends React.PureComponent {
componentDidMount () {
if (this.props.bindToDocument) {
document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
document.addEventListener('wheel', this.handleWheel, listenerOptions);
} else {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
}
}
componentWillUnmount () {
if (this.props.bindToDocument) {
document.removeEventListener('wheel', this.handleWheel);
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
} else {
this.node.removeEventListener('wheel', this.handleWheel);
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
}
}

View file

@ -1,10 +1,12 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { Icon } from 'mastodon/components/icon';
import { PureComponent } from 'react';
import { createPortal } from 'react-dom';
export default class ColumnBackButton extends React.PureComponent {
import { FormattedMessage } from 'react-intl';
import { Icon } from 'mastodon/components/icon';
export default class ColumnBackButton extends PureComponent {
static contextTypes = {
router: PropTypes.object,

View file

@ -1,8 +1,9 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import ColumnBackButton from './column_back_button';
import { Icon } from 'mastodon/components/icon';
import ColumnBackButton from './column_back_button';
export default class ColumnBackButtonSlim extends ColumnBackButton {
render () {

View file

@ -1,8 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import { Icon } from 'mastodon/components/icon';
const messages = defineMessages({
@ -12,7 +15,7 @@ const messages = defineMessages({
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
});
class ColumnHeader extends React.PureComponent {
class ColumnHeader extends PureComponent {
static contextTypes = {
router: PropTypes.object,

View file

@ -1,5 +1,4 @@
// @ts-check
import React from 'react';
import { FormattedMessage } from 'react-intl';
/**

View file

@ -1,14 +1,17 @@
import React from 'react';
import { IconButton } from './icon_button';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { injectIntl, defineMessages } from 'react-intl';
import { bannerSettings } from 'mastodon/settings';
import { IconButton } from './icon_button';
const messages = defineMessages({
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
});
class DismissableBanner extends React.PureComponent {
class DismissableBanner extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,

View file

@ -5,7 +5,7 @@ import type { List } from 'immutable';
import type { Account } from '../../types/resources';
import { autoPlayGif } from '../initial_state';
import Skeleton from './skeleton';
import { Skeleton } from './skeleton';
interface Props {
account?: Account;

View file

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import { useCallback } from 'react';
import type { InjectedIntl } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';

View file

@ -1,16 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { IconButton } from './icon_button';
import Overlay from 'react-overlays/Overlay';
import { supportsPassiveEvents } from 'detect-passive-events';
import { PureComponent, cloneElement, Children } from 'react';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay';
import { CircularProgress } from 'mastodon/components/loading_indicator';
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
import { IconButton } from './icon_button';
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
let id = 0;
class DropdownMenu extends React.PureComponent {
class DropdownMenu extends PureComponent {
static contextTypes = {
router: PropTypes.object,
@ -35,12 +40,13 @@ class DropdownMenu extends React.PureComponent {
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose();
e.stopPropagation();
}
};
componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('keydown', this.handleKeyDown, false);
document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('keydown', this.handleKeyDown, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem && this.props.openedViaKeyboard) {
@ -49,8 +55,8 @@ class DropdownMenu extends React.PureComponent {
}
componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false);
document.removeEventListener('keydown', this.handleKeyDown, false);
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('keydown', this.handleKeyDown, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
}
@ -154,7 +160,7 @@ class DropdownMenu extends React.PureComponent {
}
export default class Dropdown extends React.PureComponent {
export default class Dropdown extends PureComponent {
static contextTypes = {
router: PropTypes.object,
@ -285,7 +291,7 @@ export default class Dropdown extends React.PureComponent {
const open = this.state.id === openDropdownId;
const button = children ? React.cloneElement(React.Children.only(children), {
const button = children ? cloneElement(Children.only(children), {
onClick: this.handleClick,
onMouseDown: this.handleMouseDown,
onKeyDown: this.handleButtonKeyDown,
@ -305,7 +311,7 @@ export default class Dropdown extends React.PureComponent {
);
return (
<React.Fragment>
<>
<span ref={this.setTargetRef}>
{button}
</span>
@ -328,7 +334,7 @@ export default class Dropdown extends React.PureComponent {
</div>
)}
</Overlay>
</React.Fragment>
</>
);
}

View file

@ -1,4 +1,5 @@
import { connect } from 'react-redux';
import { openDropdownMenu, closeDropdownMenu } from 'mastodon/actions/dropdown_menu';
import { fetchHistory } from 'mastodon/actions/history';
import DropdownMenu from 'mastodon/components/dropdown_menu';

View file

@ -1,12 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Icon } from 'mastodon/components/icon';
import DropdownMenu from './containers/dropdown_menu_container';
import { connect } from 'react-redux';
import { openModal } from 'mastodon/actions/modal';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { Icon } from 'mastodon/components/icon';
import InlineAccount from 'mastodon/components/inline_account';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import DropdownMenu from './containers/dropdown_menu_container';
const mapDispatchToProps = (dispatch, { statusId }) => ({
@ -16,7 +20,7 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({
});
class EditedTimestamp extends React.PureComponent {
class EditedTimestamp extends PureComponent {
static propTypes = {
statusId: PropTypes.string.isRequired,

View file

@ -1,11 +1,8 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { injectIntl } from 'react-intl';
import emojify from '../features/emoji/emoji';
import classNames from 'classnames';
export default class EmojiView extends React.PureComponent {
export default class EmojiView extends PureComponent {
static propTypes = {
name: PropTypes.string,

View file

@ -3,7 +3,7 @@ import React from 'react';
import classNames from 'classnames';
import { DisplayName } from 'mastodon/components/display_name';
import Skeleton from 'mastodon/components/skeleton';
import { Skeleton } from 'mastodon/components/skeleton';
interface Props {
size?: number;

View file

@ -1,11 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
import { version, source_url } from 'mastodon/initial_state';
import StackTrace from 'stacktrace-js';
import { Helmet } from 'react-helmet';
export default class ErrorBoundary extends React.PureComponent {
import StackTrace from 'stacktrace-js';
import { version, source_url } from 'mastodon/initial_state';
export default class ErrorBoundary extends PureComponent {
static propTypes = {
children: PropTypes.node,

View file

@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
interface Props {
src: string;

View file

@ -1,15 +1,20 @@
// @ts-check
import React from 'react';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Link } from 'react-router-dom';
import ShortNumber from 'mastodon/components/short_number';
import Skeleton from 'mastodon/components/skeleton';
import classNames from 'classnames';
import { Component } from 'react';
class SilentErrorBoundary extends React.Component {
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import ShortNumber from 'mastodon/components/short_number';
import { Skeleton } from 'mastodon/components/skeleton';
class SilentErrorBoundary extends Component {
static propTypes = {
children: PropTypes.node,
@ -69,7 +74,7 @@ const Hashtag = ({ name, to, people, uses, history, className, description, with
<div className={classNames('trends__item', className)}>
<div className='trends__item__name'>
<Link to={to}>
{name ? <React.Fragment>#<span>{name}</span></React.Fragment> : <Skeleton width={50} />}
{name ? <>#<span>{name}</span></> : <Skeleton width={50} />}
</Link>
{description ? (

View file

@ -1,5 +1,3 @@
import React from 'react';
import classNames from 'classnames';
interface Props extends React.HTMLAttributes<HTMLImageElement> {

View file

@ -1,4 +1,4 @@
import React from 'react';
import { PureComponent } from 'react';
import classNames from 'classnames';
@ -32,7 +32,7 @@ interface States {
activate: boolean;
deactivate: boolean;
}
export class IconButton extends React.PureComponent<Props, States> {
export class IconButton extends PureComponent<Props, States> {
static defaultProps = {
size: 18,
active: false,
@ -127,14 +127,14 @@ export class IconButton extends React.PureComponent<Props, States> {
}
let contents = (
<React.Fragment>
<>
<Icon id={icon} fixedWidth aria-hidden='true' />{' '}
{typeof counter !== 'undefined' && (
<span className='icon-button__counter'>
<AnimatedNumber value={counter} obfuscate={obfuscateCount} />
</span>
)}
</React.Fragment>
</>
);
if (href != null) {

View file

@ -1,5 +1,3 @@
import React from 'react';
import { Icon } from './icon';
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);

View file

@ -1,8 +1,10 @@
import React from 'react';
import { PureComponent } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { makeGetAccount } from 'mastodon/selectors';
import { Avatar } from 'mastodon/components/avatar';
import { makeGetAccount } from 'mastodon/selectors';
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
@ -14,7 +16,7 @@ const makeMapStateToProps = () => {
return mapStateToProps;
};
class InlineAccount extends React.PureComponent {
class InlineAccount extends PureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,

View file

@ -1,12 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import scheduleIdleTask from '../features/ui/util/schedule_idle_task';
import { cloneElement, Component } from 'react';
import getRectFromEntry from '../features/ui/util/get_rect_from_entry';
import scheduleIdleTask from '../features/ui/util/schedule_idle_task';
// Diff these props in the "unrendered" state
const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight'];
export default class IntersectionObserverArticle extends React.Component {
export default class IntersectionObserverArticle extends Component {
static propTypes = {
intersectionObserverWrapper: PropTypes.object.isRequired,
@ -115,14 +116,14 @@ export default class IntersectionObserverArticle extends React.Component {
data-id={id}
tabIndex={0}
>
{children && React.cloneElement(children, { hidden: true })}
{children && cloneElement(children, { hidden: true })}
</article>
);
}
return (
<article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={0}>
{children && React.cloneElement(children, { hidden: false })}
{children && cloneElement(children, { hidden: false })}
</article>
);
}

View file

@ -1,35 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, defineMessages } from 'react-intl';
import { Icon } from 'mastodon/components/icon';
const messages = defineMessages({
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
});
class LoadGap extends React.PureComponent {
static propTypes = {
disabled: PropTypes.bool,
maxId: PropTypes.string,
onClick: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onClick(this.props.maxId);
};
render () {
const { disabled, intl } = this.props;
return (
<button className='load-more load-gap' disabled={disabled} onClick={this.handleClick} aria-label={intl.formatMessage(messages.load_more)}>
<Icon id='ellipsis-h' />
</button>
);
}
}
export default injectIntl(LoadGap);

View file

@ -0,0 +1,36 @@
import { useCallback } from 'react';
import type { InjectedIntl } from 'react-intl';
import { injectIntl, defineMessages } from 'react-intl';
import { Icon } from 'mastodon/components/icon';
const messages = defineMessages({
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
});
interface Props {
disabled: boolean;
maxId: string;
onClick: (maxId: string) => void;
intl: InjectedIntl;
}
export const LoadGap = injectIntl<Props>(
({ disabled, maxId, onClick, intl }) => {
const handleClick = useCallback(() => {
onClick(maxId);
}, [maxId, onClick]);
return (
<button
className='load-more load-gap'
disabled={disabled}
onClick={handleClick}
aria-label={intl.formatMessage(messages.load_more)}
>
<Icon id='ellipsis-h' />
</button>
);
}
);

View file

@ -1,8 +1,9 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
export default class LoadMore extends React.PureComponent {
import { FormattedMessage } from 'react-intl';
export default class LoadMore extends PureComponent {
static propTypes = {
onClick: PropTypes.func,

View file

@ -1,8 +1,9 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
export default class LoadPending extends React.PureComponent {
import { FormattedMessage } from 'react-intl';
export default class LoadPending extends PureComponent {
static propTypes = {
onClick: PropTypes.func,

View file

@ -1,4 +1,3 @@
import React from 'react';
import PropTypes from 'prop-types';
export const CircularProgress = ({ size, strokeWidth }) => {

View file

@ -1,5 +1,3 @@
import React from 'react';
import logo from 'mastodon/../images/logo.svg';
export const WordmarkLogo: React.FC = () => (

View file

@ -1,11 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { MediaGallery, Video, Audio } from 'mastodon/features/ui/util/async-components';
import Bundle from 'mastodon/features/ui/components/bundle';
import noop from 'lodash/noop';
import Bundle from 'mastodon/features/ui/components/bundle';
import { MediaGallery, Video, Audio } from 'mastodon/features/ui/util/async-components';
export default class MediaAttachments extends ImmutablePureComponent {
static propTypes = {

View file

@ -1,19 +1,26 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { is } from 'immutable';
import { IconButton } from './icon_button';
import { PureComponent } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { autoPlayGif, cropImages, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state';
import { is } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash';
import { Blurhash } from 'mastodon/components/blurhash';
import { autoPlayGif, cropImages, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state';
import { IconButton } from './icon_button';
const messages = defineMessages({
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: '{number, plural, one {Hide image} other {Hide images}}' },
});
class Item extends React.PureComponent {
class Item extends PureComponent {
static propTypes = {
attachment: ImmutablePropTypes.map.isRequired,
@ -212,7 +219,7 @@ class Item extends React.PureComponent {
}
class MediaGallery extends React.PureComponent {
class MediaGallery extends PureComponent {
static propTypes = {
sensitive: PropTypes.bool,

View file

@ -1,10 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import 'wicg-inert';
import { createBrowserHistory } from 'history';
import { multiply } from 'color-blend';
import { PureComponent } from 'react';
export default class ModalRoot extends React.PureComponent {
import 'wicg-inert';
import { multiply } from 'color-blend';
import { createBrowserHistory } from 'history';
export default class ModalRoot extends PureComponent {
static contextTypes = {
router: PropTypes.object,

View file

@ -1,21 +1,21 @@
import React from 'react';
import { PureComponent } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import { showTrends } from 'mastodon/initial_state';
import Trends from 'mastodon/features/getting_started/containers/trends_container';
import AccountNavigation from 'mastodon/features/account/navigation';
import Trends from 'mastodon/features/getting_started/containers/trends_container';
import { showTrends } from 'mastodon/initial_state';
const DefaultNavigation = () => (
<>
{showTrends && (
<>
<div className='flex-spacer' />
<Trends />
</>
)}
</>
showTrends ? (
<>
<div className='flex-spacer' />
<Trends />
</>
) : null
);
class NavigationPortal extends React.PureComponent {
class NavigationPortal extends PureComponent {
render () {
return (

Some files were not shown because too many files have changed in this diff Show more