Merge pull request #2449 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 9a3d047f3e
This commit is contained in:
commit
59893a4eab
425 changed files with 4668 additions and 5836 deletions
|
@ -236,7 +236,7 @@ module.exports = {
|
|||
},
|
||||
// Common React utilities
|
||||
{
|
||||
pattern: '{classnames,react-helmet,react-router-dom}',
|
||||
pattern: '{classnames,react-helmet,react-router,react-router-dom}',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
|
|
19
.github/actions/setup-javascript/action.yml
vendored
Normal file
19
.github/actions/setup-javascript/action.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: 'Setup Javascript'
|
||||
description: 'Setup a Javascript environment ready to run the Mastodon code'
|
||||
inputs:
|
||||
onlyProduction:
|
||||
description: Only install production dependencies
|
||||
default: 'false'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
shell: bash
|
||||
run: yarn --frozen-lockfile ${{ inputs.onlyProduction != 'false' && '--production' || '' }}
|
23
.github/actions/setup-ruby/action.yml
vendored
Normal file
23
.github/actions/setup-ruby/action.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: 'Setup RUby'
|
||||
description: 'Setup a Ruby environment ready to run the Mastodon code'
|
||||
inputs:
|
||||
ruby-version:
|
||||
description: The Ruby version to install
|
||||
default: '.ruby-version'
|
||||
additional-system-dependencies:
|
||||
description: 'Additional packages to install'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install system dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev ${{ inputs.additional-system-dependencies }}
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
bundler-cache: true
|
1
.github/renovate.json5
vendored
1
.github/renovate.json5
vendored
|
@ -3,7 +3,6 @@
|
|||
extends: [
|
||||
'config:recommended',
|
||||
':labels(dependencies)',
|
||||
':maintainLockFilesMonthly', // update non-direct dependencies monthly
|
||||
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
||||
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
||||
],
|
||||
|
|
10
.github/workflows/bundler-audit.yml
vendored
10
.github/workflows/bundler-audit.yml
vendored
|
@ -27,14 +27,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Run bundler-audit
|
||||
run: bundle exec bundler-audit
|
||||
|
|
22
.github/workflows/check-i18n.yml
vendored
22
.github/workflows/check-i18n.yml
vendored
|
@ -19,25 +19,11 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Check for missing strings in English JSON
|
||||
run: |
|
||||
|
|
10
.github/workflows/crowdin-download.yml
vendored
10
.github/workflows/crowdin-download.yml
vendored
|
@ -45,14 +45,8 @@ jobs:
|
|||
run: sudo chown -R runner:docker .
|
||||
|
||||
# This is needed to run the normalize step
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Run i18n normalize task
|
||||
run: bundle exec i18n-tasks normalize
|
||||
|
|
10
.github/workflows/lint-css.yml
vendored
10
.github/workflows/lint-css.yml
vendored
|
@ -35,14 +35,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- uses: xt0rted/stylelint-problem-matcher@v1
|
||||
|
||||
|
|
12
.github/workflows/lint-haml.yml
vendored
12
.github/workflows/lint-haml.yml
vendored
|
@ -30,16 +30,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Run haml-lint
|
||||
run: |
|
||||
|
|
10
.github/workflows/lint-js.yml
vendored
10
.github/workflows/lint-js.yml
vendored
|
@ -39,14 +39,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: ESLint
|
||||
run: yarn lint:js --max-warnings 0
|
||||
|
|
10
.github/workflows/lint-json.yml
vendored
10
.github/workflows/lint-json.yml
vendored
|
@ -31,14 +31,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Prettier
|
||||
run: yarn lint:json
|
||||
|
|
10
.github/workflows/lint-md.yml
vendored
10
.github/workflows/lint-md.yml
vendored
|
@ -31,14 +31,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Prettier
|
||||
run: yarn lint:md
|
||||
|
|
10
.github/workflows/lint-ruby.yml
vendored
10
.github/workflows/lint-ruby.yml
vendored
|
@ -31,14 +31,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Set-up RuboCop Problem Matcher
|
||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||
|
|
10
.github/workflows/lint-yml.yml
vendored
10
.github/workflows/lint-yml.yml
vendored
|
@ -33,14 +33,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Prettier
|
||||
run: yarn lint:yml
|
||||
|
|
10
.github/workflows/test-js.yml
vendored
10
.github/workflows/test-js.yml
vendored
|
@ -35,14 +35,8 @@ jobs:
|
|||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install all yarn packages
|
||||
run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Jest testing
|
||||
run: yarn jest --reporters github-actions summary
|
||||
|
|
12
.github/workflows/test-migrations-one-step.yml
vendored
12
.github/workflows/test-migrations-one-step.yml
vendored
|
@ -72,16 +72,8 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
|
12
.github/workflows/test-migrations-two-step.yml
vendored
12
.github/workflows/test-migrations-two-step.yml
vendored
|
@ -71,16 +71,8 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Create database
|
||||
run: './bin/rails db:create'
|
||||
|
|
85
.github/workflows/test-ruby.yml
vendored
85
.github/workflows/test-ruby.yml
vendored
|
@ -34,24 +34,14 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
onlyProduction: 'true'
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: .ruby-version
|
||||
bundler-cache: true
|
||||
|
||||
- run: yarn --frozen-lockfile --production
|
||||
- name: Precompile assets
|
||||
# Previously had set this, but it's not supported
|
||||
# export NODE_OPTIONS=--openssl-legacy-provider
|
||||
|
@ -135,20 +125,11 @@ jobs:
|
|||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
||||
- name: Update package index
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Install additional system dependencies
|
||||
run: sudo apt-get install -y ffmpeg imagemagick libpam-dev
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
bundler-cache: true
|
||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||
|
||||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
@ -210,28 +191,14 @@ jobs:
|
|||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
||||
- name: Update package index
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Install additional system dependencies
|
||||
run: sudo apt-get install -y ffmpeg imagemagick
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
bundler-cache: true
|
||||
additional-system-dependencies: ffmpeg imagemagick
|
||||
|
||||
- run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
@ -328,28 +295,14 @@ jobs:
|
|||
path: './public'
|
||||
name: ${{ github.sha }}
|
||||
|
||||
- name: Update package index
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: yarn
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Install native Ruby dependencies
|
||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
||||
|
||||
- name: Install additional system dependencies
|
||||
run: sudo apt-get install -y ffmpeg imagemagick
|
||||
|
||||
- name: Set up bundler cache
|
||||
uses: ruby/setup-ruby@v1
|
||||
- name: Set up Ruby environment
|
||||
uses: ./.github/actions/setup-ruby
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version}}
|
||||
bundler-cache: true
|
||||
additional-system-dependencies: ffmpeg imagemagick
|
||||
|
||||
- run: yarn --frozen-lockfile
|
||||
- name: Set up Javascript environment
|
||||
uses: ./.github/actions/setup-javascript
|
||||
|
||||
- name: Load database schema
|
||||
run: './bin/rails db:create db:schema:load db:seed'
|
||||
|
|
|
@ -1,33 +1,21 @@
|
|||
# This configuration was generated by
|
||||
# `haml-lint --auto-gen-config`
|
||||
# on 2023-10-11 11:31:24 -0400 using Haml-Lint version 0.51.0.
|
||||
# on 2023-10-25 08:29:48 -0400 using Haml-Lint version 0.51.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the lints are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of Haml-Lint, may require this file to be generated again.
|
||||
|
||||
linters:
|
||||
# Offense count: 946
|
||||
# Offense count: 945
|
||||
LineLength:
|
||||
enabled: false
|
||||
|
||||
# Offense count: 22
|
||||
UnnecessaryStringOutput:
|
||||
enabled: false
|
||||
|
||||
# Offense count: 44
|
||||
# Offense count: 10
|
||||
RuboCop:
|
||||
enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
ViewLength:
|
||||
exclude:
|
||||
- 'app/views/admin/accounts/show.html.haml'
|
||||
- 'app/views/admin/reports/show.html.haml'
|
||||
- 'app/views/disputes/strikes/show.html.haml'
|
||||
|
||||
# Offense count: 2
|
||||
IdNames:
|
||||
exclude:
|
||||
- 'app/views/oauth/authorizations/error.html.haml'
|
||||
- 'app/views/shared/_error_messages.html.haml'
|
||||
- 'app/views/admin/accounts/_buttons.html.haml'
|
||||
- 'app/views/admin/accounts/_local_account.html.haml'
|
||||
- 'app/views/admin/accounts/index.html.haml'
|
||||
- 'app/views/admin/roles/_form.html.haml'
|
||||
- 'app/views/layouts/application.html.haml'
|
||||
|
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
20.8
|
||||
20.9
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.56.1.
|
||||
# using RuboCop version 1.57.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
|
@ -80,7 +80,6 @@ RSpec/AnyInstance:
|
|||
- 'spec/controllers/admin/accounts_controller_spec.rb'
|
||||
- 'spec/controllers/admin/resets_controller_spec.rb'
|
||||
- 'spec/controllers/admin/settings/branding_controller_spec.rb'
|
||||
- 'spec/controllers/api/v1/media_controller_spec.rb'
|
||||
- 'spec/controllers/auth/sessions_controller_spec.rb'
|
||||
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
|
||||
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
|
||||
|
@ -180,7 +179,6 @@ RSpec/LetSetup:
|
|||
|
||||
RSpec/MessageChain:
|
||||
Exclude:
|
||||
- 'spec/controllers/api/v1/media_controller_spec.rb'
|
||||
- 'spec/models/concerns/remotable_spec.rb'
|
||||
- 'spec/models/session_activation_spec.rb'
|
||||
- 'spec/models/setting_spec.rb'
|
||||
|
@ -219,19 +217,6 @@ Rails/ApplicationController:
|
|||
Exclude:
|
||||
- 'app/controllers/health_controller.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: db/**/*.rb
|
||||
Rails/CreateTableWithTimestamps:
|
||||
Exclude:
|
||||
- 'db/migrate/20170508230434_create_conversation_mutes.rb'
|
||||
- 'db/migrate/20170823162448_create_status_pins.rb'
|
||||
- 'db/migrate/20171116161857_create_list_accounts.rb'
|
||||
- 'db/migrate/20180929222014_create_account_conversations.rb'
|
||||
- 'db/migrate/20181007025445_create_pghero_space_stats.rb'
|
||||
- 'db/migrate/20190103124649_create_scheduled_statuses.rb'
|
||||
- 'db/migrate/20220824233535_create_status_trends.rb'
|
||||
- 'db/migrate/20221006061337_create_preview_card_trends.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: Severity.
|
||||
Rails/DuplicateAssociation:
|
||||
|
@ -273,7 +258,6 @@ Rails/LexicallyScopedActionFilter:
|
|||
Exclude:
|
||||
- 'app/controllers/auth/passwords_controller.rb'
|
||||
- 'app/controllers/auth/registrations_controller.rb'
|
||||
- 'app/controllers/auth/sessions_controller.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/NegateInclude:
|
||||
|
@ -289,7 +273,6 @@ Rails/NegateInclude:
|
|||
- 'app/models/custom_filter.rb'
|
||||
- 'app/services/activitypub/process_status_update_service.rb'
|
||||
- 'app/services/fetch_link_card_service.rb'
|
||||
- 'app/services/search_service.rb'
|
||||
- 'app/workers/web/push_notification_worker.rb'
|
||||
- 'lib/paperclip/color_extractor.rb'
|
||||
|
||||
|
@ -309,24 +292,6 @@ Rails/RakeEnvironment:
|
|||
- 'lib/tasks/repo.rake'
|
||||
- 'lib/tasks/statistics.rake'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: db/**/*.rb
|
||||
Rails/ReversibleMigration:
|
||||
Exclude:
|
||||
- 'db/migrate/20160223164502_make_uris_nullable_in_statuses.rb'
|
||||
- 'db/migrate/20161122163057_remove_unneeded_indexes.rb'
|
||||
- 'db/migrate/20170205175257_remove_devices.rb'
|
||||
- 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
|
||||
- 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
|
||||
- 'db/migrate/20170609145826_remove_default_language_from_statuses.rb'
|
||||
- 'db/migrate/20170711225116_fix_null_booleans.rb'
|
||||
- 'db/migrate/20171129172043_add_index_on_stream_entries.rb'
|
||||
- 'db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb'
|
||||
- 'db/migrate/20171226094803_more_faster_index_on_notifications.rb'
|
||||
- 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
|
||||
- 'db/migrate/20180617162849_remove_unused_indexes.rb'
|
||||
- 'db/migrate/20220827195229_change_canonical_email_blocks_nullable.rb'
|
||||
|
||||
# Configuration parameters: ForbiddenMethods, AllowedMethods.
|
||||
# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
|
||||
Rails/SkipsModelValidations:
|
||||
|
@ -379,31 +344,6 @@ Rails/SkipsModelValidations:
|
|||
- 'spec/services/follow_service_spec.rb'
|
||||
- 'spec/services/update_account_service_spec.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: db/**/*.rb
|
||||
Rails/ThreeStateBooleanColumn:
|
||||
Exclude:
|
||||
- 'db/migrate/20160325130944_add_admin_to_users.rb'
|
||||
- 'db/migrate/20161123093447_add_sensitive_to_statuses.rb'
|
||||
- 'db/migrate/20170123203248_add_reject_media_to_domain_blocks.rb'
|
||||
- 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb'
|
||||
- 'db/migrate/20170209184350_add_reply_to_statuses.rb'
|
||||
- 'db/migrate/20170330163835_create_imports.rb'
|
||||
- 'db/migrate/20170905165803_add_local_to_statuses.rb'
|
||||
- 'db/migrate/20171210213213_add_local_only_flag_to_statuses.rb'
|
||||
- 'db/migrate/20181203021853_add_discoverable_to_accounts.rb'
|
||||
- 'db/migrate/20190509164208_add_by_moderator_to_tombstone.rb'
|
||||
- 'db/migrate/20190805123746_add_capabilities_to_tags.rb'
|
||||
- 'db/migrate/20191212163405_add_hide_collections_to_accounts.rb'
|
||||
- 'db/migrate/20200309150742_add_forwarded_to_reports.rb'
|
||||
- 'db/migrate/20210609202149_create_login_activities.rb'
|
||||
- 'db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb'
|
||||
- 'db/migrate/20211031031021_create_preview_card_providers.rb'
|
||||
- 'db/migrate/20211115032527_add_trendable_to_preview_cards.rb'
|
||||
- 'db/migrate/20220202200743_add_trendable_to_accounts.rb'
|
||||
- 'db/migrate/20220202200926_add_trendable_to_statuses.rb'
|
||||
- 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/UniqueValidationWithoutIndex:
|
||||
|
@ -467,7 +407,7 @@ Style/CaseEquality:
|
|||
Exclude:
|
||||
- 'config/initializers/trusted_proxies.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
# AllowedMethods: ==, equal?, eql?
|
||||
Style/ClassEqualityComparison:
|
||||
|
@ -675,7 +615,6 @@ Style/RedundantReturn:
|
|||
Style/SafeNavigation:
|
||||
Exclude:
|
||||
- 'app/models/concerns/account_finder_concern.rb'
|
||||
- 'app/models/status.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
|
|
2329
CHANGELOG.md
2329
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -28,7 +28,7 @@ RUN apt-get update && \
|
|||
libgdbm-dev \
|
||||
libgmp-dev \
|
||||
libssl-dev \
|
||||
libyaml-0-2 \
|
||||
libyaml-dev \
|
||||
ca-certificates \
|
||||
libreadline8 \
|
||||
python3 \
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -4,7 +4,7 @@ source 'https://rubygems.org'
|
|||
ruby '>= 3.0.0'
|
||||
|
||||
gem 'puma', '~> 6.3'
|
||||
gem 'rails', '~> 7.0'
|
||||
gem 'rails', '~> 7.1.1'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'thor', '~> 1.2'
|
||||
gem 'rack', '~> 2.2.7'
|
||||
|
|
173
Gemfile.lock
173
Gemfile.lock
|
@ -39,75 +39,83 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actioncable (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailer (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actiontext (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
active_model_serializers (0.10.14)
|
||||
actionpack (>= 4.1)
|
||||
activemodel (>= 4.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
activejob (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activejob (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activestorage (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activemodel (7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
activerecord (7.1.1)
|
||||
activemodel (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.1.1)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.5)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
|
@ -158,6 +166,7 @@ GEM
|
|||
erubi (~> 1.4)
|
||||
parser (>= 2.4)
|
||||
smart_properties
|
||||
bigdecimal (3.1.4)
|
||||
bindata (2.4.15)
|
||||
binding_of_caller (1.0.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
|
@ -237,6 +246,8 @@ GEM
|
|||
dotenv-rails (2.8.1)
|
||||
dotenv (= 2.8.1)
|
||||
railties (>= 3.2)
|
||||
drb (2.1.1)
|
||||
ruby2_keywords
|
||||
ed25519 (1.3.0)
|
||||
elasticsearch (7.13.3)
|
||||
elasticsearch-api (= 7.13.3)
|
||||
|
@ -305,8 +316,8 @@ GEM
|
|||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
globalid (1.2.1)
|
||||
activesupport (>= 6.1)
|
||||
haml (6.2.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
|
@ -357,7 +368,11 @@ GEM
|
|||
rainbow (>= 2.2.2, < 4.0)
|
||||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.6.0)
|
||||
ipaddress (0.8.3)
|
||||
irb (1.8.1)
|
||||
rdoc
|
||||
reline (>= 0.3.8)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
json-canonicalization (0.3.2)
|
||||
|
@ -434,7 +449,6 @@ GEM
|
|||
azure-storage-blob (~> 2.0.1)
|
||||
hashie (~> 5.0)
|
||||
memory_profiler (1.0.1)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.5.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2023.0808)
|
||||
|
@ -444,11 +458,12 @@ GEM
|
|||
msgpack (1.7.1)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.3.0)
|
||||
mutex_m (0.1.2)
|
||||
net-http (0.3.2)
|
||||
uri
|
||||
net-http-persistent (4.0.2)
|
||||
connection_pool (~> 2.2)
|
||||
net-imap (0.3.7)
|
||||
net-imap (0.4.1)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.18.0)
|
||||
|
@ -456,7 +471,7 @@ GEM
|
|||
net-protocol
|
||||
net-protocol (0.2.1)
|
||||
timeout
|
||||
net-smtp (0.3.3)
|
||||
net-smtp (0.4.0)
|
||||
net-protocol
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.4)
|
||||
|
@ -512,6 +527,8 @@ GEM
|
|||
net-smtp
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
private_address_check (0.5.0)
|
||||
psych (5.1.1)
|
||||
stringio
|
||||
public_suffix (5.0.3)
|
||||
puma (6.4.0)
|
||||
nio4r (~> 2.0)
|
||||
|
@ -534,22 +551,27 @@ GEM
|
|||
rack
|
||||
rack-proxy (0.7.6)
|
||||
rack
|
||||
rack-session (1.0.1)
|
||||
rack (< 3)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rails (7.0.8)
|
||||
actioncable (= 7.0.8)
|
||||
actionmailbox (= 7.0.8)
|
||||
actionmailer (= 7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actiontext (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rackup (1.0.0)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.1)
|
||||
actioncable (= 7.1.1)
|
||||
actionmailbox (= 7.1.1)
|
||||
actionmailer (= 7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
actiontext (= 7.1.1)
|
||||
actionview (= 7.1.1)
|
||||
activejob (= 7.1.1)
|
||||
activemodel (= 7.1.1)
|
||||
activerecord (= 7.1.1)
|
||||
activestorage (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.8)
|
||||
railties (= 7.1.1)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -564,19 +586,22 @@ GEM
|
|||
rails-i18n (7.0.8)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
method_source
|
||||
railties (7.1.1)
|
||||
actionpack (= 7.1.1)
|
||||
activesupport (= 7.1.1)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
thor (~> 1.0, >= 1.2.2)
|
||||
zeitwerk (~> 2.6)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rdf (3.2.11)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.6.1)
|
||||
rdf (~> 3.2)
|
||||
rdoc (6.5.0)
|
||||
psych (>= 4.0.0)
|
||||
redcarpet (3.6.0)
|
||||
redis (4.8.1)
|
||||
redis-namespace (1.11.0)
|
||||
|
@ -584,13 +609,15 @@ GEM
|
|||
redlock (1.3.2)
|
||||
redis (>= 3.0.0, < 6.0)
|
||||
regexp_parser (2.8.2)
|
||||
reline (0.3.9)
|
||||
io-console (~> 0.5)
|
||||
request_store (1.5.1)
|
||||
rack (>= 1.4)
|
||||
responders (3.1.1)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.6)
|
||||
rotp (6.2.2)
|
||||
rotp (6.3.0)
|
||||
rouge (4.1.2)
|
||||
rpam2 (4.0.2)
|
||||
rqrcode (2.2.0)
|
||||
|
@ -712,6 +739,7 @@ GEM
|
|||
statsd-ruby (1.5.0)
|
||||
stoplight (3.0.2)
|
||||
redlock (~> 1.0)
|
||||
stringio (3.0.8)
|
||||
strong_migrations (0.8.0)
|
||||
activerecord (>= 5.2)
|
||||
swd (1.3.0)
|
||||
|
@ -783,6 +811,7 @@ GEM
|
|||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webrick (1.8.1)
|
||||
websocket (1.2.10)
|
||||
websocket-driver (0.7.6)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
|
@ -878,7 +907,7 @@ DEPENDENCIES
|
|||
rack-attack (~> 6.6)
|
||||
rack-cors (~> 2.0)
|
||||
rack-test (~> 2.1)
|
||||
rails (~> 7.0)
|
||||
rails (~> 7.1.1)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 7.0)
|
||||
rails-settings-cached (~> 0.6)!
|
||||
|
@ -929,4 +958,4 @@ RUBY VERSION
|
|||
ruby 3.2.2p53
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.13
|
||||
2.4.20
|
||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
|||
private
|
||||
|
||||
def set_instance
|
||||
@instance = Instance.find(TagManager.instance.normalize_domain(params[:id]&.strip))
|
||||
@instance = Instance.find_or_initialize_by(domain: TagManager.instance.normalize_domain(params[:id]&.strip))
|
||||
end
|
||||
|
||||
def set_instances
|
||||
|
|
|
@ -13,6 +13,7 @@ class ApplicationController < ActionController::Base
|
|||
include ThemingConcern
|
||||
include DatabaseHelper
|
||||
include AuthorizedFetchHelper
|
||||
include SelfDestructHelper
|
||||
|
||||
helper_method :current_account
|
||||
helper_method :current_session
|
||||
|
@ -41,6 +42,8 @@ class ApplicationController < ActionController::Base
|
|||
service_unavailable
|
||||
end
|
||||
|
||||
before_action :check_self_destruct!
|
||||
|
||||
before_action :store_referrer, except: :raise_not_found, if: :devise_controller?
|
||||
before_action :require_functional!, if: :user_signed_in?
|
||||
|
||||
|
@ -169,6 +172,15 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
def check_self_destruct!
|
||||
return unless self_destruct?
|
||||
|
||||
respond_to do |format|
|
||||
format.any { render 'errors/self_destruct', layout: 'auth', status: 410, formats: [:html] }
|
||||
format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[410] }, status: code }
|
||||
end
|
||||
end
|
||||
|
||||
def set_cache_control_defaults
|
||||
response.cache_control.replace(private: true, no_store: true)
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ class Auth::ChallengesController < ApplicationController
|
|||
before_action :set_pack
|
||||
before_action :authenticate_user!
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def create
|
||||
|
|
|
@ -13,6 +13,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
||||
before_action :require_captcha_if_needed!, only: [:show]
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
def self.provides_callback_for(provider)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Auth::PasswordsController < Devise::PasswordsController
|
||||
skip_before_action :check_self_destruct!
|
||||
before_action :check_validity_of_reset_password_token, only: :edit
|
||||
before_action :set_pack
|
||||
before_action :set_body_classes
|
||||
|
|
|
@ -18,6 +18,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
before_action :require_rules_acceptance!, only: :new
|
||||
before_action :set_registration_form_time, only: :new
|
||||
|
||||
skip_before_action :check_self_destruct!, only: [:edit, :update]
|
||||
skip_before_action :require_functional!, only: [:edit, :update]
|
||||
|
||||
def new
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Auth::SessionsController < Devise::SessionsController
|
||||
layout 'auth'
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_no_authentication, only: [:create]
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :update_user_sign_in
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class BackupsController < ApplicationController
|
||||
include RoutingHelper
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
|
|
@ -7,6 +7,7 @@ module ExportControllerConcern
|
|||
before_action :authenticate_user!
|
||||
before_action :load_export
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class Settings::ExportsController < Settings::BaseController
|
|||
include Redisable
|
||||
include Lockable
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::LoginActivitiesController < Settings::BaseController
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def index
|
||||
@login_activities = LoginActivity.where(user: current_user).order(id: :desc).page(params[:page])
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module Settings
|
||||
module TwoFactorAuthentication
|
||||
class WebauthnCredentialsController < BaseController
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_otp_enabled
|
||||
|
|
|
@ -4,6 +4,7 @@ module Settings
|
|||
class TwoFactorAuthenticationMethodsController < BaseController
|
||||
include ChallengableConcern
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_challenge!, only: :disable
|
||||
|
|
|
@ -9,6 +9,10 @@ module FormattingHelper
|
|||
TextFormatter.new(text, options).to_s
|
||||
end
|
||||
|
||||
def url_for_preview_card(preview_card)
|
||||
preview_card.url
|
||||
end
|
||||
|
||||
def extract_status_plain_text(status)
|
||||
PlainTextFormatter.new(status.text, status.local?).to_s
|
||||
end
|
||||
|
|
14
app/helpers/self_destruct_helper.rb
Normal file
14
app/helpers/self_destruct_helper.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module SelfDestructHelper
|
||||
def self.self_destruct?
|
||||
value = ENV.fetch('SELF_DESTRUCT', nil)
|
||||
value.present? && Rails.application.message_verifier('self-destruct').verify(value) == ENV['LOCAL_DOMAIN']
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
false
|
||||
end
|
||||
|
||||
def self_destruct?
|
||||
SelfDestructHelper.self_destruct?
|
||||
end
|
||||
end
|
3
app/javascript/__mocks__/svg.js
Normal file
3
app/javascript/__mocks__/svg.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default 'SvgrURL';
|
||||
export const ReactComponent = 'div';
|
|
@ -1,7 +1,7 @@
|
|||
// This file will be loaded on admin pages, regardless of theme.
|
||||
|
||||
import 'packs/public-path';
|
||||
import { delegate } from '@rails/ujs';
|
||||
import Rails from '@rails/ujs';
|
||||
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
|
@ -19,7 +19,7 @@ const setAnnouncementEndsAttributes = (target) => {
|
|||
}
|
||||
};
|
||||
|
||||
delegate(document, 'input[type="datetime-local"]#announcement_starts_at', 'change', ({ target }) => {
|
||||
Rails.delegate(document, 'input[type="datetime-local"]#announcement_starts_at', 'change', ({ target }) => {
|
||||
setAnnouncementEndsAttributes(target);
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,7 @@ const hideSelectAll = () => {
|
|||
hiddenField.value = '0';
|
||||
};
|
||||
|
||||
delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
||||
Rails.delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
|
||||
[].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
|
||||
|
@ -58,7 +58,7 @@ delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
|||
}
|
||||
});
|
||||
|
||||
delegate(document, '.batch-table__select-all button', 'click', () => {
|
||||
Rails.delegate(document, '.batch-table__select-all button', 'click', () => {
|
||||
const hiddenField = document.querySelector('#select_all_matching');
|
||||
const active = hiddenField.value === '1';
|
||||
const selectedMsg = document.querySelector('.batch-table__select-all .selected');
|
||||
|
@ -75,7 +75,7 @@ delegate(document, '.batch-table__select-all button', 'click', () => {
|
|||
}
|
||||
});
|
||||
|
||||
delegate(document, batchCheckboxClassName, 'change', () => {
|
||||
Rails.delegate(document, batchCheckboxClassName, 'change', () => {
|
||||
const checkAllElement = document.querySelector('#batch_checkbox_all');
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
|
||||
|
@ -93,19 +93,19 @@ delegate(document, batchCheckboxClassName, 'change', () => {
|
|||
}
|
||||
});
|
||||
|
||||
delegate(document, '.media-spoiler-show-button', 'click', () => {
|
||||
Rails.delegate(document, '.media-spoiler-show-button', 'click', () => {
|
||||
[].forEach.call(document.querySelectorAll('button.media-spoiler'), (element) => {
|
||||
element.click();
|
||||
});
|
||||
});
|
||||
|
||||
delegate(document, '.media-spoiler-hide-button', 'click', () => {
|
||||
Rails.delegate(document, '.media-spoiler-hide-button', 'click', () => {
|
||||
[].forEach.call(document.querySelectorAll('.spoiler-button.spoiler-button--visible button'), (element) => {
|
||||
element.click();
|
||||
});
|
||||
});
|
||||
|
||||
delegate(document, '.filter-subset--with-select select', 'change', ({ target }) => {
|
||||
Rails.delegate(document, '.filter-subset--with-select select', 'change', ({ target }) => {
|
||||
target.form.submit();
|
||||
});
|
||||
|
||||
|
@ -122,7 +122,7 @@ const onDomainBlockSeverityChange = (target) => {
|
|||
}
|
||||
};
|
||||
|
||||
delegate(document, '#domain_block_severity', 'change', ({ target }) => onDomainBlockSeverityChange(target));
|
||||
Rails.delegate(document, '#domain_block_severity', 'change', ({ target }) => onDomainBlockSeverityChange(target));
|
||||
|
||||
const onEnableBootstrapTimelineAccountsChange = (target) => {
|
||||
const bootstrapTimelineAccountsField = document.querySelector('#form_admin_settings_bootstrap_timeline_accounts');
|
||||
|
@ -139,7 +139,7 @@ const onEnableBootstrapTimelineAccountsChange = (target) => {
|
|||
}
|
||||
};
|
||||
|
||||
delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'change', ({ target }) => onEnableBootstrapTimelineAccountsChange(target));
|
||||
Rails.delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'change', ({ target }) => onEnableBootstrapTimelineAccountsChange(target));
|
||||
|
||||
const onChangeRegistrationMode = (target) => {
|
||||
const enabled = target.value === 'approved';
|
||||
|
@ -176,7 +176,7 @@ const convertLocalDatetimeToUTC = (value) => {
|
|||
return fullISO8601.slice(0, fullISO8601.indexOf('T') + 6);
|
||||
};
|
||||
|
||||
delegate(document, '#form_admin_settings_registrations_mode', 'change', ({ target }) => onChangeRegistrationMode(target));
|
||||
Rails.delegate(document, '#form_admin_settings_registrations_mode', 'change', ({ target }) => onChangeRegistrationMode(target));
|
||||
|
||||
ready(() => {
|
||||
const domainBlockSeverityInput = document.getElementById('domain_block_severity');
|
||||
|
@ -213,7 +213,7 @@ ready(() => {
|
|||
}
|
||||
});
|
||||
|
||||
delegate(document, 'form', 'submit', ({ target }) => {
|
||||
Rails.delegate(document, 'form', 'submit', ({ target }) => {
|
||||
[].forEach.call(target.querySelectorAll('input[type="datetime-local"]'), element => {
|
||||
if (element.value && element.validity.valid) {
|
||||
element.value = convertLocalDatetimeToUTC(element.value);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// This file will be loaded on settings pages, regardless of theme.
|
||||
|
||||
import 'packs/public-path';
|
||||
import { delegate } from '@rails/ujs';
|
||||
import Rails from '@rails/ujs';
|
||||
|
||||
delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => {
|
||||
Rails.delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => {
|
||||
const avatar = document.getElementById(target.id + '-preview');
|
||||
const [file] = target.files || [];
|
||||
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
|
||||
|
@ -11,13 +11,13 @@ delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => {
|
|||
avatar.src = url;
|
||||
});
|
||||
|
||||
delegate(document, '.input-copy input', 'click', ({ target }) => {
|
||||
Rails.delegate(document, '.input-copy input', 'click', ({ target }) => {
|
||||
target.focus();
|
||||
target.select();
|
||||
target.setSelectionRange(0, target.value.length);
|
||||
});
|
||||
|
||||
delegate(document, '.input-copy button', 'click', ({ target }) => {
|
||||
Rails.delegate(document, '.input-copy button', 'click', ({ target }) => {
|
||||
const input = target.parentNode.querySelector('.input-copy__wrapper input');
|
||||
|
||||
const oldReadOnly = input.readonly;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
import { unicodeMapping } from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
import { assetHost } from 'flavours/glitch/utils/config';
|
||||
|
||||
export default class AutosuggestEmoji extends PureComponent {
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class Button extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
block: PropTypes.bool,
|
||||
secondary: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
if (!this.props.disabled) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.node.focus();
|
||||
}
|
||||
|
||||
render () {
|
||||
let attrs = {
|
||||
className: classNames('button', this.props.className, {
|
||||
'button-secondary': this.props.secondary,
|
||||
'button--block': this.props.block,
|
||||
}),
|
||||
disabled: this.props.disabled,
|
||||
onClick: this.handleClick,
|
||||
ref: this.setRef,
|
||||
};
|
||||
|
||||
if (this.props.title) attrs.title = this.props.title;
|
||||
|
||||
return (
|
||||
<button {...attrs}>
|
||||
{this.props.text || this.props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
58
app/javascript/flavours/glitch/components/button.tsx
Normal file
58
app/javascript/flavours/glitch/components/button.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface BaseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
block?: boolean;
|
||||
secondary?: boolean;
|
||||
text?: JSX.Element;
|
||||
}
|
||||
|
||||
interface PropsWithChildren extends BaseProps {
|
||||
text?: never;
|
||||
}
|
||||
|
||||
interface PropsWithText extends BaseProps {
|
||||
text: JSX.Element;
|
||||
children: never;
|
||||
}
|
||||
|
||||
type Props = PropsWithText | PropsWithChildren;
|
||||
|
||||
export const Button: React.FC<Props> = ({
|
||||
text,
|
||||
type = 'button',
|
||||
onClick,
|
||||
disabled,
|
||||
block,
|
||||
secondary,
|
||||
className,
|
||||
title,
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const handleClick = useCallback<React.MouseEventHandler<HTMLButtonElement>>(
|
||||
(e) => {
|
||||
if (!disabled && onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
},
|
||||
[disabled, onClick],
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames('button', className, {
|
||||
'button-secondary': secondary,
|
||||
'button--block': block,
|
||||
})}
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
title={title}
|
||||
type={type}
|
||||
{...props}
|
||||
>
|
||||
{text ?? children}
|
||||
</button>
|
||||
);
|
||||
};
|
|
@ -24,12 +24,6 @@ export default class Column extends PureComponent {
|
|||
scrollable = document.scrollingElement;
|
||||
} else {
|
||||
scrollable = this.node.querySelector('.scrollable');
|
||||
|
||||
// Some columns have nested `.scrollable` containers, with the outer one
|
||||
// being a wrapper while the actual scrollable content is deeper.
|
||||
if (scrollable.classList.contains('scrollable--flex')) {
|
||||
scrollable = scrollable?.querySelector('.scrollable') || scrollable;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scrollable) {
|
||||
|
|
|
@ -4,26 +4,25 @@ import { createPortal } from 'react-dom';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
export default class ColumnBackButton extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
export class ColumnBackButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
const { router } = this.context;
|
||||
const { history } = this.props;
|
||||
|
||||
if (router.history.location?.state?.fromMastodon) {
|
||||
router.history.goBack();
|
||||
if (history.location?.state?.fromMastodon) {
|
||||
history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,3 +56,5 @@ export default class ColumnBackButton extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(ColumnBackButton);
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
export default class ColumnBackButtonSlim extends PureComponent {
|
||||
class ColumnBackButtonSlim extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
static propTypes = {
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
const { router } = this.context;
|
||||
const { location, history } = this.props;
|
||||
|
||||
// Check if there is a previous page in the app to go back to per https://stackoverflow.com/a/70532858/9703201
|
||||
// When upgrading to V6, check `location.key !== 'default'` instead per https://github.com/remix-run/history/blob/main/docs/api-reference.md#location
|
||||
if (router.route.location.key) {
|
||||
router.history.goBack();
|
||||
if (location.key) {
|
||||
history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,5 +35,6 @@ export default class ColumnBackButtonSlim extends PureComponent {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(ColumnBackButtonSlim);
|
||||
|
|
|
@ -5,8 +5,10 @@ import { createPortal } from 'react-dom';
|
|||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
|
@ -18,7 +20,6 @@ const messages = defineMessages({
|
|||
class ColumnHeader extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
@ -38,6 +39,7 @@ class ColumnHeader extends PureComponent {
|
|||
onClick: PropTypes.func,
|
||||
appendContent: PropTypes.node,
|
||||
collapseIssues: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -63,12 +65,12 @@ class ColumnHeader extends PureComponent {
|
|||
};
|
||||
|
||||
handleBackClick = () => {
|
||||
const { router } = this.context;
|
||||
const { history } = this.props;
|
||||
|
||||
if (router.history.location?.state?.fromMastodon) {
|
||||
router.history.goBack();
|
||||
if (history.location?.state?.fromMastodon) {
|
||||
history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -78,15 +80,14 @@ class ColumnHeader extends PureComponent {
|
|||
|
||||
handlePin = () => {
|
||||
if (!this.props.pinned) {
|
||||
this.context.router.history.replace('/');
|
||||
this.props.history.replace('/');
|
||||
}
|
||||
|
||||
this.props.onPin();
|
||||
};
|
||||
|
||||
render () {
|
||||
const { router } = this.context;
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props;
|
||||
const { collapsed, animating } = this.state;
|
||||
|
||||
const wrapperClassName = classNames('column-header__wrapper', {
|
||||
|
@ -129,7 +130,7 @@ class ColumnHeader extends PureComponent {
|
|||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
||||
}
|
||||
|
||||
if (!pinned && ((multiColumn && router.history.location?.state?.fromMastodon) || showBackButton)) {
|
||||
if (!pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) {
|
||||
backButton = (
|
||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||
|
@ -215,4 +216,4 @@ class ColumnHeader extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(ColumnHeader);
|
||||
export default injectIntl(withRouter(ColumnHeader));
|
||||
|
|
|
@ -2,13 +2,16 @@ import PropTypes from 'prop-types';
|
|||
import { PureComponent, cloneElement, Children } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
import { CircularProgress } from "./circular_progress";
|
||||
import { CircularProgress } from 'flavours/glitch/components/circular_progress';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
|
@ -16,10 +19,6 @@ let id = 0;
|
|||
|
||||
class DropdownMenu extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
|
||||
loading: PropTypes.bool,
|
||||
|
@ -159,11 +158,7 @@ class DropdownMenu extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default class Dropdown extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
class Dropdown extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
|
@ -183,6 +178,7 @@ export default class Dropdown extends PureComponent {
|
|||
renderItem: PropTypes.func,
|
||||
renderHeader: PropTypes.func,
|
||||
onItemClick: PropTypes.func,
|
||||
...WithRouterPropTypes
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -250,7 +246,7 @@ export default class Dropdown extends PureComponent {
|
|||
item.action();
|
||||
} else if (item && item.to) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(item.to);
|
||||
this.props.history.push(item.to);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -338,3 +334,5 @@ export default class Dropdown extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(Dropdown);
|
||||
|
|
|
@ -2,14 +2,13 @@ import PropTypes from 'prop-types';
|
|||
import { PureComponent } from 'react';
|
||||
|
||||
import 'wicg-inert';
|
||||
|
||||
import { multiply } from 'color-blend';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
export default class ModalRoot extends PureComponent {
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
class ModalRoot extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
|
@ -21,6 +20,7 @@ export default class ModalRoot extends PureComponent {
|
|||
}),
|
||||
noEsc: PropTypes.bool,
|
||||
ignoreFocus: PropTypes.bool,
|
||||
...WithOptionalRouterPropTypes,
|
||||
};
|
||||
|
||||
activeElement = this.props.children ? document.activeElement : null;
|
||||
|
@ -56,7 +56,7 @@ export default class ModalRoot extends PureComponent {
|
|||
componentDidMount () {
|
||||
window.addEventListener('keyup', this.handleKeyUp, false);
|
||||
window.addEventListener('keydown', this.handleKeyDown, false);
|
||||
this.history = this.context.router ? this.context.router.history : createBrowserHistory();
|
||||
this.history = this.props.history || createBrowserHistory();
|
||||
|
||||
if (this.props.children) {
|
||||
this._handleModalOpen();
|
||||
|
@ -160,3 +160,5 @@ export default class ModalRoot extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withOptionalRouter(ModalRoot);
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import { PureComponent } from 'react';
|
||||
|
||||
import { Switch, Route, withRouter } from 'react-router-dom';
|
||||
|
||||
import AccountNavigation from 'flavours/glitch/features/account/navigation';
|
||||
import Trends from 'flavours/glitch/features/getting_started/containers/trends_container';
|
||||
import { showTrends } from 'flavours/glitch/initial_state';
|
||||
|
||||
const DefaultNavigation = () => (
|
||||
showTrends ? (
|
||||
<>
|
||||
<div className='flex-spacer' />
|
||||
<Trends />
|
||||
</>
|
||||
) : null
|
||||
);
|
||||
|
||||
class NavigationPortal extends PureComponent {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path='/@:acct' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/tagged/:tagged?' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/with_replies' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/followers' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/following' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/media' exact component={AccountNavigation} />
|
||||
<Route component={DefaultNavigation} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(NavigationPortal);
|
|
@ -0,0 +1,25 @@
|
|||
import { Switch, Route } from 'react-router-dom';
|
||||
|
||||
import AccountNavigation from 'flavours/glitch/features/account/navigation';
|
||||
import Trends from 'flavours/glitch/features/getting_started/containers/trends_container';
|
||||
import { showTrends } from 'flavours/glitch/initial_state';
|
||||
|
||||
const DefaultNavigation: React.FC = () =>
|
||||
showTrends ? (
|
||||
<>
|
||||
<div className='flex-spacer' />
|
||||
<Trends />
|
||||
</>
|
||||
) : null;
|
||||
|
||||
export const NavigationPortal: React.FC = () => (
|
||||
<Switch>
|
||||
<Route path='/@:acct' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/tagged/:tagged?' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/with_replies' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/followers' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/following' exact component={AccountNavigation} />
|
||||
<Route path='/@:acct/media' exact component={AccountNavigation} />
|
||||
<Route component={DefaultNavigation} />
|
||||
</Switch>
|
||||
);
|
|
@ -1,11 +1,9 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
export default class Permalink extends PureComponent {
|
||||
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
class Permalink extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
|
@ -13,6 +11,7 @@ export default class Permalink extends PureComponent {
|
|||
to: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
onInterceptClick: PropTypes.func,
|
||||
...WithOptionalRouterPropTypes,
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
|
@ -22,9 +21,9 @@ export default class Permalink extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.context.router) {
|
||||
if (this.props.history) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(this.props.to);
|
||||
this.props.history.push(this.props.to);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -47,3 +46,5 @@ export default class Permalink extends PureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withOptionalRouter(Permalink);
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { Router as OriginalRouter } from 'react-router';
|
||||
|
||||
import type {
|
||||
LocationDescriptor,
|
||||
LocationDescriptorObject,
|
||||
Path,
|
||||
} from 'history';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
import { layoutFromWindow } from 'flavours/glitch/is_mobile';
|
||||
|
||||
interface MastodonLocationState {
|
||||
fromMastodon?: boolean;
|
||||
mastodonModalKey?: string;
|
||||
}
|
||||
type HistoryPath = Path | LocationDescriptor<MastodonLocationState>;
|
||||
|
||||
const browserHistory = createBrowserHistory<
|
||||
MastodonLocationState | undefined
|
||||
|
@ -17,28 +24,55 @@ const browserHistory = createBrowserHistory<
|
|||
const originalPush = browserHistory.push.bind(browserHistory);
|
||||
const originalReplace = browserHistory.replace.bind(browserHistory);
|
||||
|
||||
browserHistory.push = (path: string, state?: MastodonLocationState) => {
|
||||
state = state ?? {};
|
||||
state.fromMastodon = true;
|
||||
function normalizePath(
|
||||
path: HistoryPath,
|
||||
state?: MastodonLocationState,
|
||||
): LocationDescriptorObject<MastodonLocationState> {
|
||||
const location = typeof path === 'string' ? { pathname: path } : { ...path };
|
||||
|
||||
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
|
||||
originalPush(`/deck${path}`, state);
|
||||
} else {
|
||||
originalPush(path, state);
|
||||
if (location.state === undefined && state !== undefined) {
|
||||
location.state = state;
|
||||
} else if (
|
||||
location.state !== undefined &&
|
||||
state !== undefined &&
|
||||
process.env.NODE_ENV === 'development'
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'You should avoid providing a 2nd state argument to push when the 1st argument is a location-like object that already has state; it is ignored',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
layoutFromWindow() === 'multi-column' &&
|
||||
!location.pathname?.startsWith('/deck')
|
||||
) {
|
||||
location.pathname = `/deck${location.pathname}`;
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
browserHistory.push = (path: HistoryPath, state?: MastodonLocationState) => {
|
||||
const location = normalizePath(path, state);
|
||||
|
||||
location.state = location.state ?? {};
|
||||
location.state.fromMastodon = true;
|
||||
|
||||
originalPush(location);
|
||||
};
|
||||
|
||||
browserHistory.replace = (path: string, state?: MastodonLocationState) => {
|
||||
browserHistory.replace = (path: HistoryPath, state?: MastodonLocationState) => {
|
||||
const location = normalizePath(path, state);
|
||||
|
||||
if (!location.pathname) return;
|
||||
|
||||
if (browserHistory.location.state?.fromMastodon) {
|
||||
state = state ?? {};
|
||||
state.fromMastodon = true;
|
||||
location.state = location.state ?? {};
|
||||
location.state.fromMastodon = true;
|
||||
}
|
||||
|
||||
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
|
||||
originalReplace(`/deck${path}`, state);
|
||||
} else {
|
||||
originalReplace(path, state);
|
||||
}
|
||||
originalReplace(location);
|
||||
};
|
||||
|
||||
export const Router: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
|||
import { Children, cloneElement, PureComponent } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -34,11 +35,32 @@ const mapStateToProps = (state, { scrollKey }) => {
|
|||
};
|
||||
};
|
||||
|
||||
class ScrollableList extends PureComponent {
|
||||
// This component only exists to be able to call useLocation()
|
||||
const IOArticleContainerWrapper = ({id, index, listLength, intersectionObserverWrapper, trackScroll, scrollKey, children}) => {
|
||||
const location = useLocation();
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
return (<IntersectionObserverArticleContainer
|
||||
id={id}
|
||||
index={index}
|
||||
listLength={listLength}
|
||||
intersectionObserverWrapper={intersectionObserverWrapper}
|
||||
saveHeightKey={trackScroll ? `${location.key}:${scrollKey}` : null}
|
||||
>
|
||||
{children}
|
||||
</IntersectionObserverArticleContainer>);
|
||||
};
|
||||
|
||||
IOArticleContainerWrapper.propTypes = {
|
||||
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
listLength: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
intersectionObserverWrapper: PropTypes.object.isRequired,
|
||||
trackScroll: PropTypes.bool.isRequired,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
class ScrollableList extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
scrollKey: PropTypes.string.isRequired,
|
||||
|
@ -331,13 +353,14 @@ class ScrollableList extends PureComponent {
|
|||
{loadPending}
|
||||
|
||||
{Children.map(this.props.children, (child, index) => (
|
||||
<IntersectionObserverArticleContainer
|
||||
<IOArticleContainerWrapper
|
||||
key={child.key}
|
||||
id={child.key}
|
||||
index={index}
|
||||
listLength={childrenCount}
|
||||
intersectionObserverWrapper={this.intersectionObserverWrapper}
|
||||
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
|
||||
trackScroll={trackScroll}
|
||||
scrollKey={scrollKey}
|
||||
>
|
||||
{cloneElement(child, {
|
||||
getScrollPosition: this.getScrollPosition,
|
||||
|
@ -345,7 +368,7 @@ class ScrollableList extends PureComponent {
|
|||
cachedMediaWidth: this.state.cachedMediaWidth,
|
||||
cacheMediaWidth: this.cacheMediaWidth,
|
||||
})}
|
||||
</IntersectionObserverArticleContainer>
|
||||
</IOArticleContainerWrapper>
|
||||
))}
|
||||
|
||||
{loadMore}
|
||||
|
|
|
@ -14,6 +14,7 @@ import PollContainer from 'flavours/glitch/containers/poll_container';
|
|||
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
|
||||
import { displayMedia } from 'flavours/glitch/initial_state';
|
||||
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
|
||||
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import Card from '../features/status/components/card';
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
|
@ -67,10 +68,6 @@ export const defaultMediaVisibility = (status, settings) => {
|
|||
|
||||
class Status extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
containerId: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
|
@ -117,6 +114,7 @@ class Status extends ImmutablePureComponent {
|
|||
inUse: PropTypes.bool,
|
||||
available: PropTypes.bool,
|
||||
}),
|
||||
...WithOptionalRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -356,10 +354,9 @@ class Status extends ImmutablePureComponent {
|
|||
// Otherwise, we open the url handed to us in `destination`, if
|
||||
// applicable.
|
||||
parseClick = (e, destination) => {
|
||||
const { router } = this.context;
|
||||
const { status } = this.props;
|
||||
const { status, history } = this.props;
|
||||
const { isCollapsed } = this.state;
|
||||
if (!router) return;
|
||||
if (!history) return;
|
||||
|
||||
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
|
||||
if (isCollapsed) this.setCollapsed(false);
|
||||
|
@ -377,7 +374,7 @@ class Status extends ImmutablePureComponent {
|
|||
status.getIn(['reblog', 'id'], status.get('id'))
|
||||
}`;
|
||||
}
|
||||
router.history.push(destination);
|
||||
history.push(destination);
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
@ -431,7 +428,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
this.props.onReply(this.props.status, this.props.history);
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = (e) => {
|
||||
|
@ -448,16 +445,16 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onMention(this.props.status.get('account'), this.props.history);
|
||||
};
|
||||
|
||||
handleHotkeyOpen = () => {
|
||||
const status = this.props.status;
|
||||
this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
this.props.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||
this.props.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
|
@ -514,7 +511,6 @@ class Status extends ImmutablePureComponent {
|
|||
parseClick,
|
||||
setCollapsed,
|
||||
} = this;
|
||||
const { router } = this.context;
|
||||
const {
|
||||
intl,
|
||||
status,
|
||||
|
@ -533,6 +529,7 @@ class Status extends ImmutablePureComponent {
|
|||
previousId,
|
||||
nextInReplyToId,
|
||||
rootId,
|
||||
history,
|
||||
...other
|
||||
} = this.props;
|
||||
const { isCollapsed } = this.state;
|
||||
|
@ -828,7 +825,7 @@ class Status extends ImmutablePureComponent {
|
|||
onExpandedToggle={this.handleExpandedToggle}
|
||||
onTranslate={this.handleTranslate}
|
||||
parseClick={parseClick}
|
||||
disabled={!router}
|
||||
disabled={!history}
|
||||
tagLinks={settings.get('tag_misleading_links')}
|
||||
rewriteMentions={settings.get('rewrite_mentions')}
|
||||
/>
|
||||
|
@ -854,4 +851,4 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Status);
|
||||
export default withOptionalRouter(injectIntl(Status));
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
@ -11,6 +12,7 @@ import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_cont
|
|||
import { me } from 'flavours/glitch/initial_state';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
|
@ -53,7 +55,6 @@ const messages = defineMessages({
|
|||
class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
@ -80,6 +81,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
showReplyCount: PropTypes.bool,
|
||||
scrollKey: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
|
@ -95,7 +97,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
const { signedIn } = this.context.identity;
|
||||
|
||||
if (signedIn) {
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
this.props.onReply(this.props.status, this.props.history);
|
||||
} else {
|
||||
this.props.onInteractionModal('reply', this.props.status);
|
||||
}
|
||||
|
@ -132,15 +134,15 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleDeleteClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history);
|
||||
this.props.onDelete(this.props.status, this.props.history);
|
||||
};
|
||||
|
||||
handleRedraftClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history, true);
|
||||
this.props.onDelete(this.props.status, this.props.history, true);
|
||||
};
|
||||
|
||||
handleEditClick = () => {
|
||||
this.props.onEdit(this.props.status, this.context.router.history);
|
||||
this.props.onEdit(this.props.status, this.props.history);
|
||||
};
|
||||
|
||||
handlePinClick = () => {
|
||||
|
@ -148,11 +150,11 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleMentionClick = () => {
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onMention(this.props.status.get('account'), this.props.history);
|
||||
};
|
||||
|
||||
handleDirectClick = () => {
|
||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onDirect(this.props.status.get('account'), this.props.history);
|
||||
};
|
||||
|
||||
handleMuteClick = () => {
|
||||
|
@ -164,12 +166,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpen = () => {
|
||||
let state = { ...this.context.router.history.location.state };
|
||||
if (state.mastodonModalKey) {
|
||||
this.context.router.history.replace(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
} else {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
}
|
||||
this.props.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
};
|
||||
|
||||
handleEmbed = () => {
|
||||
|
@ -340,4 +337,4 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(StatusActionBar);
|
||||
export default withRouter(injectIntl(StatusActionBar));
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PureComponent } from 'react';
|
|||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -131,6 +132,10 @@ class StatusContent extends PureComponent {
|
|||
rewriteMentions: PropTypes.string,
|
||||
languages: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object,
|
||||
// from react-router
|
||||
match: PropTypes.object.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -472,4 +477,4 @@ class StatusContent extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(StatusContent));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent)));
|
||||
|
|
|
@ -14,10 +14,6 @@ const messages = defineMessages({
|
|||
|
||||
class FeaturedTags extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
featuredTags: ImmutablePropTypes.list,
|
||||
|
|
|
@ -4,18 +4,20 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||
import { autoPlayGif, me, domain } from 'flavours/glitch/initial_state';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||
import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import AccountNoteContainer from '../containers/account_note_container';
|
||||
import FollowRequestNoteContainer from '../containers/follow_request_note_container';
|
||||
|
@ -81,10 +83,6 @@ const dateFormatOptions = {
|
|||
|
||||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
|
@ -107,6 +105,11 @@ class Header extends ImmutablePureComponent {
|
|||
intl: PropTypes.object.isRequired,
|
||||
domain: PropTypes.string.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
openEditProfile = () => {
|
||||
|
@ -406,4 +409,4 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Header);
|
||||
export default withRouter(injectIntl(Header));
|
||||
|
|
|
@ -2,18 +2,19 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import ActionBar from 'flavours/glitch/features/account/components/action_bar';
|
||||
import InnerHeader from 'flavours/glitch/features/account/components/header';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import MemorialNote from './memorial_note';
|
||||
import MovedNote from './moved_note';
|
||||
|
||||
export default class Header extends ImmutablePureComponent {
|
||||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
|
@ -34,10 +35,7 @@ export default class Header extends ImmutablePureComponent {
|
|||
hideTabs: PropTypes.bool,
|
||||
domain: PropTypes.string.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleFollow = () => {
|
||||
|
@ -49,11 +47,11 @@ export default class Header extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleMention = () => {
|
||||
this.props.onMention(this.props.account, this.context.router.history);
|
||||
this.props.onMention(this.props.account, this.props.history);
|
||||
};
|
||||
|
||||
handleDirect = () => {
|
||||
this.props.onDirect(this.props.account, this.context.router.history);
|
||||
this.props.onDirect(this.props.account, this.props.history);
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
|
@ -162,3 +160,5 @@ export default class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(Header);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { revealAccount } from 'flavours/glitch/actions/accounts';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
|
||||
const mapDispatchToProps = (dispatch, { accountId }) => ({
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import AvatarOverlay from '../../../components/avatar_overlay';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
|
||||
export default class MovedNote extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
class MovedNote extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
from: ImmutablePropTypes.map.isRequired,
|
||||
to: ImmutablePropTypes.map.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleAccountClick = e => {
|
||||
if (e.button === 0) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/@${this.props.to.get('acct')}`);
|
||||
this.props.history.push(`/@${this.props.to.get('acct')}`);
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
|
@ -50,3 +48,5 @@ export default class MovedNote extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(MovedNote);
|
||||
|
|
|
@ -44,7 +44,6 @@ class CommunityTimeline extends PureComponent {
|
|||
};
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { length } from 'stringz';
|
|||
|
||||
import { maxChars } from 'flavours/glitch/initial_state';
|
||||
import { isMobile } from 'flavours/glitch/is_mobile';
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import AutosuggestInput from '../../../components/autosuggest_input';
|
||||
import AutosuggestTextarea from '../../../components/autosuggest_textarea';
|
||||
|
@ -38,11 +39,6 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
class ComposeForm extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
text: PropTypes.string,
|
||||
|
@ -70,7 +66,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
isInReply: PropTypes.bool,
|
||||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
|
||||
advancedOptions: ImmutablePropTypes.map,
|
||||
layout: PropTypes.string,
|
||||
media: ImmutablePropTypes.list,
|
||||
|
@ -82,6 +77,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onChangeSpoilerness: PropTypes.func,
|
||||
onChangeVisibility: PropTypes.func,
|
||||
onMediaDescriptionConfirm: PropTypes.func,
|
||||
...WithOptionalRouterPropTypes
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -129,12 +125,12 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
// Submit unless there are media with missing descriptions
|
||||
if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) {
|
||||
const firstWithoutDescription = media.find(item => !item.get('description'));
|
||||
onMediaDescriptionConfirm(this.context.router ? this.context.router.history : null, firstWithoutDescription.get('id'), overriddenVisibility);
|
||||
onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility);
|
||||
} else if (onSubmit) {
|
||||
if (onChangeVisibility && overriddenVisibility) {
|
||||
onChangeVisibility(overriddenVisibility);
|
||||
}
|
||||
onSubmit(this.context.router ? this.context.router.history : null);
|
||||
onSubmit(this.props.history || null);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -390,4 +386,4 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(ComposeForm);
|
||||
export default withOptionalRouter(injectIntl(ComposeForm));
|
||||
|
|
|
@ -8,7 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
|
||||
import { length } from 'stringz';
|
||||
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { maxChars } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
@ -6,18 +5,13 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
// Components.
|
||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import AccountContainer from 'flavours/glitch/containers/account_container';
|
||||
// Messages.
|
||||
const messages = defineMessages({
|
||||
cancel: {
|
||||
defaultMessage: 'Cancel',
|
||||
id: 'reply_indicator.cancel',
|
||||
},
|
||||
});
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
|
||||
});
|
||||
|
||||
class ReplyIndicator extends ImmutablePureComponent {
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import { PureComponent } from 'react';
|
|||
import { defineMessages, injectIntl, FormattedMessage, FormattedList } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { domain, searchEnabled } from 'flavours/glitch/initial_state';
|
||||
import { focusRoot } from 'flavours/glitch/utils/dom_helpers';
|
||||
import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
|
||||
|
@ -32,7 +32,6 @@ const labelForRecentSearch = search => {
|
|||
class Search extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
identity: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
@ -50,6 +49,7 @@ class Search extends PureComponent {
|
|||
openInRoute: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
singleColumn: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -122,8 +122,7 @@ class Search extends PureComponent {
|
|||
switch(e.key) {
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
|
||||
focusRoot();
|
||||
this._unfocus();
|
||||
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
|
@ -170,32 +169,29 @@ class Search extends PureComponent {
|
|||
};
|
||||
|
||||
handleHashtagClick = () => {
|
||||
const { router } = this.context;
|
||||
const { value, onClickSearchResult } = this.props;
|
||||
const { value, onClickSearchResult, history } = this.props;
|
||||
|
||||
const query = value.trim().replace(/^#/, '');
|
||||
|
||||
router.history.push(`/tags/${query}`);
|
||||
history.push(`/tags/${query}`);
|
||||
onClickSearchResult(query, 'hashtag');
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleAccountClick = () => {
|
||||
const { router } = this.context;
|
||||
const { value, onClickSearchResult } = this.props;
|
||||
const { value, onClickSearchResult, history } = this.props;
|
||||
|
||||
const query = value.trim().replace(/^@/, '');
|
||||
|
||||
router.history.push(`/@${query}`);
|
||||
history.push(`/@${query}`);
|
||||
onClickSearchResult(query, 'account');
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
handleURLClick = () => {
|
||||
const { router } = this.context;
|
||||
const { onOpenURL } = this.props;
|
||||
const { onOpenURL, history } = this.props;
|
||||
|
||||
onOpenURL(router.history);
|
||||
onOpenURL(history);
|
||||
this._unfocus();
|
||||
};
|
||||
|
||||
|
@ -208,13 +204,12 @@ class Search extends PureComponent {
|
|||
};
|
||||
|
||||
handleRecentSearchClick = search => {
|
||||
const { onChange } = this.props;
|
||||
const { router } = this.context;
|
||||
const { onChange, history } = this.props;
|
||||
|
||||
if (search.get('type') === 'account') {
|
||||
router.history.push(`/@${search.get('q')}`);
|
||||
history.push(`/@${search.get('q')}`);
|
||||
} else if (search.get('type') === 'hashtag') {
|
||||
router.history.push(`/tags/${search.get('q')}`);
|
||||
history.push(`/tags/${search.get('q')}`);
|
||||
} else {
|
||||
onChange(search.get('q'));
|
||||
this._submit(search.get('type'));
|
||||
|
@ -246,8 +241,7 @@ class Search extends PureComponent {
|
|||
}
|
||||
|
||||
_submit (type) {
|
||||
const { onSubmit, openInRoute, value, onClickSearchResult } = this.props;
|
||||
const { router } = this.context;
|
||||
const { onSubmit, openInRoute, value, onClickSearchResult, history } = this.props;
|
||||
|
||||
onSubmit(type);
|
||||
|
||||
|
@ -256,7 +250,7 @@ class Search extends PureComponent {
|
|||
}
|
||||
|
||||
if (openInRoute) {
|
||||
router.history.push('/search');
|
||||
history.push('/search');
|
||||
}
|
||||
|
||||
this._unfocus();
|
||||
|
@ -403,4 +397,4 @@ class Search extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Search);
|
||||
export default withRouter(injectIntl(Search));
|
||||
|
|
|
@ -13,10 +13,6 @@ import Motion from '../../ui/util/optional_motion';
|
|||
|
||||
export default class Upload extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
media: ImmutablePropTypes.map.isRequired,
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
@ -17,6 +18,7 @@ import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp
|
|||
import StatusContent from 'flavours/glitch/components/status_content';
|
||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
|
@ -30,10 +32,6 @@ const messages = defineMessages({
|
|||
|
||||
class Conversation extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
conversationId: PropTypes.string.isRequired,
|
||||
accounts: ImmutablePropTypes.list.isRequired,
|
||||
|
@ -45,6 +43,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
markRead: PropTypes.func.isRequired,
|
||||
delete: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -52,9 +51,8 @@ class Conversation extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
parseClick = (e, destination) => {
|
||||
const { router } = this.context;
|
||||
const { lastStatus, unread, markRead } = this.props;
|
||||
if (!router) return;
|
||||
const { history, lastStatus, unread, markRead } = this.props;
|
||||
if (!history) return;
|
||||
|
||||
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
|
||||
if (destination === undefined) {
|
||||
|
@ -63,7 +61,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
}
|
||||
destination = `/statuses/${lastStatus.get('id')}`;
|
||||
}
|
||||
router.history.push(destination);
|
||||
history.push(destination);
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
@ -95,7 +93,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.context.router) {
|
||||
if (!this.props.history) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,7 +103,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
markRead();
|
||||
}
|
||||
|
||||
this.context.router.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
|
||||
this.props.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
|
||||
};
|
||||
|
||||
handleMarkAsRead = () => {
|
||||
|
@ -113,7 +111,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleReply = () => {
|
||||
this.props.reply(this.props.lastStatus, this.context.router.history);
|
||||
this.props.reply(this.props.lastStatus, this.props.history);
|
||||
};
|
||||
|
||||
handleDelete = () => {
|
||||
|
@ -232,4 +230,4 @@ class Conversation extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(Conversation);
|
||||
export default withRouter(injectIntl(Conversation));
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from 'flavours/glitch/actions/accounts';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { DisplayName } from 'flavours/glitch/components/display_name';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
|
|
|
@ -36,10 +36,6 @@ const mapStateToProps = state => ({
|
|||
|
||||
class Directory extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
isLoading: PropTypes.bool,
|
||||
accountIds: ImmutablePropTypes.list.isRequired,
|
||||
|
|
|
@ -3,7 +3,7 @@ import Trie from 'substring-trie';
|
|||
import { autoPlayGif, useSystemEmojiFont } from 'flavours/glitch/initial_state';
|
||||
import { assetHost } from 'flavours/glitch/utils/config';
|
||||
|
||||
import unicodeMapping from './emoji_unicode_mapping_light';
|
||||
import { unicodeMapping } from './emoji_unicode_mapping_light';
|
||||
|
||||
const trie = new Trie(Object.keys(unicodeMapping));
|
||||
|
||||
|
|
|
@ -13,15 +13,20 @@ export type Search = string;
|
|||
* This could be a potential area of refactoring or error handling.
|
||||
* The non-existence of 'skins' property is evident at [this location]{@link app/javascript/flavours/glitch/features/emoji/emoji_compressed.js:121}.
|
||||
*/
|
||||
export type Skins = null;
|
||||
type Skins = null;
|
||||
|
||||
export type FilenameData = string[] | string[][];
|
||||
type Filename = string;
|
||||
type UnicodeFilename = string;
|
||||
export type FilenameData = [
|
||||
filename: Filename,
|
||||
unicodeFilename?: UnicodeFilename,
|
||||
][];
|
||||
export type ShortCodesToEmojiDataKey =
|
||||
| EmojiData['id']
|
||||
| BaseEmoji['native']
|
||||
| keyof NimbleEmojiIndex['emojis'];
|
||||
|
||||
export type SearchData = [
|
||||
type SearchData = [
|
||||
BaseEmoji['native'],
|
||||
Emoji['short_names'],
|
||||
Search,
|
||||
|
@ -32,9 +37,9 @@ export type ShortCodesToEmojiData = Record<
|
|||
ShortCodesToEmojiDataKey,
|
||||
[FilenameData, SearchData]
|
||||
>;
|
||||
export type EmojisWithoutShortCodes = FilenameData[];
|
||||
type EmojisWithoutShortCodes = FilenameData;
|
||||
|
||||
export type EmojiCompressed = [
|
||||
type EmojiCompressed = [
|
||||
ShortCodesToEmojiData,
|
||||
Skins,
|
||||
Category[],
|
||||
|
|
|
@ -30,22 +30,13 @@ const emojis: Emojis = {};
|
|||
// decompress
|
||||
Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
|
||||
const [_filenameData, searchData] = shortCodesToEmojiData[shortCode];
|
||||
const native = searchData[0];
|
||||
let short_names = searchData[1];
|
||||
const search = searchData[2];
|
||||
let unified = searchData[3];
|
||||
const [native, short_names, search, unified] = searchData;
|
||||
|
||||
if (!unified) {
|
||||
// unified name can be derived from unicodeToUnifiedName
|
||||
unified = unicodeToUnifiedName(native);
|
||||
}
|
||||
|
||||
if (short_names) short_names = [shortCode].concat(short_names);
|
||||
emojis[shortCode] = {
|
||||
native,
|
||||
search,
|
||||
short_names,
|
||||
unified,
|
||||
short_names: short_names ? [shortCode].concat(short_names) : undefined,
|
||||
unified: unified ?? unicodeToUnifiedName(native),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// A mapping of unicode strings to an object containing the filename
|
||||
// (i.e. the svg filename) and a shortCode intended to be shown
|
||||
// as a "title" attribute in an HTML element (aka tooltip).
|
||||
|
||||
import emojiCompressed from './emoji_compressed';
|
||||
import { unicodeToFilename } from './unicode_to_filename';
|
||||
|
||||
const [
|
||||
shortCodesToEmojiData,
|
||||
_skins,
|
||||
_categories,
|
||||
_short_names,
|
||||
emojisWithoutShortCodes,
|
||||
] = emojiCompressed;
|
||||
|
||||
// decompress
|
||||
const unicodeMapping = {};
|
||||
|
||||
function processEmojiMapData(emojiMapData, shortCode) {
|
||||
let [ native, filename ] = emojiMapData;
|
||||
if (!filename) {
|
||||
// filename name can be derived from unicodeToFilename
|
||||
filename = unicodeToFilename(native);
|
||||
}
|
||||
unicodeMapping[native] = {
|
||||
shortCode: shortCode,
|
||||
filename: filename,
|
||||
};
|
||||
}
|
||||
|
||||
Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
|
||||
let [ filenameData ] = shortCodesToEmojiData[shortCode];
|
||||
filenameData.forEach(emojiMapData => processEmojiMapData(emojiMapData, shortCode));
|
||||
});
|
||||
emojisWithoutShortCodes.forEach(emojiMapData => processEmojiMapData(emojiMapData));
|
||||
|
||||
export default unicodeMapping;
|
|
@ -0,0 +1,60 @@
|
|||
// A mapping of unicode strings to an object containing the filename
|
||||
// (i.e. the svg filename) and a shortCode intended to be shown
|
||||
// as a "title" attribute in an HTML element (aka tooltip).
|
||||
|
||||
import type {
|
||||
FilenameData,
|
||||
ShortCodesToEmojiDataKey,
|
||||
} from './emoji_compressed';
|
||||
import emojiCompressed from './emoji_compressed';
|
||||
import { unicodeToFilename } from './unicode_to_filename';
|
||||
|
||||
type UnicodeMapping = {
|
||||
[key in FilenameData[number][0]]: {
|
||||
shortCode: ShortCodesToEmojiDataKey;
|
||||
filename: FilenameData[number][number];
|
||||
};
|
||||
};
|
||||
|
||||
const [
|
||||
shortCodesToEmojiData,
|
||||
_skins,
|
||||
_categories,
|
||||
_short_names,
|
||||
emojisWithoutShortCodes,
|
||||
] = emojiCompressed;
|
||||
|
||||
// decompress
|
||||
const unicodeMapping: UnicodeMapping = {};
|
||||
|
||||
function processEmojiMapData(
|
||||
emojiMapData: FilenameData[number],
|
||||
shortCode?: ShortCodesToEmojiDataKey,
|
||||
) {
|
||||
const [native, _filename] = emojiMapData;
|
||||
let filename = emojiMapData[1];
|
||||
if (!filename) {
|
||||
// filename name can be derived from unicodeToFilename
|
||||
filename = unicodeToFilename(native);
|
||||
}
|
||||
unicodeMapping[native] = {
|
||||
shortCode,
|
||||
filename,
|
||||
};
|
||||
}
|
||||
|
||||
Object.keys(shortCodesToEmojiData).forEach(
|
||||
(shortCode: ShortCodesToEmojiDataKey) => {
|
||||
if (shortCode === undefined) return;
|
||||
const [filenameData, _searchData] = shortCodesToEmojiData[shortCode];
|
||||
filenameData.forEach((emojiMapData) => {
|
||||
processEmojiMapData(emojiMapData, shortCode);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
emojisWithoutShortCodes.forEach((emojiMapData) => {
|
||||
processEmojiMapData(emojiMapData);
|
||||
});
|
||||
|
||||
export { unicodeMapping };
|
|
@ -34,7 +34,6 @@ const mapStateToProps = state => ({
|
|||
class Explore extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
@ -69,47 +68,45 @@ class Explore extends PureComponent {
|
|||
<Search />
|
||||
</div>
|
||||
|
||||
<div className='scrollable scrollable--flex' data-nosnippet>
|
||||
{isSearching ? (
|
||||
<SearchResults />
|
||||
) : (
|
||||
<>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/explore'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_statuses' defaultMessage='Posts' />
|
||||
{isSearching ? (
|
||||
<SearchResults />
|
||||
) : (
|
||||
<>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/explore'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_statuses' defaultMessage='Posts' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/explore/tags'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_tags' defaultMessage='Hashtags' />
|
||||
</NavLink>
|
||||
|
||||
{signedIn && (
|
||||
<NavLink exact to='/explore/suggestions'>
|
||||
<FormattedMessage tagName='div' id='explore.suggested_follows' defaultMessage='People' />
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
<NavLink exact to='/explore/tags'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_tags' defaultMessage='Hashtags' />
|
||||
</NavLink>
|
||||
<NavLink exact to='/explore/links'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_links' defaultMessage='News' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
{signedIn && (
|
||||
<NavLink exact to='/explore/suggestions'>
|
||||
<FormattedMessage tagName='div' id='explore.suggested_follows' defaultMessage='People' />
|
||||
</NavLink>
|
||||
)}
|
||||
<Switch>
|
||||
<Route path='/explore/tags' component={Tags} />
|
||||
<Route path='/explore/links' component={Links} />
|
||||
<Route path='/explore/suggestions' component={Suggestions} />
|
||||
<Route exact path={['/explore', '/explore/posts', '/search']}>
|
||||
<Statuses multiColumn={multiColumn} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<NavLink exact to='/explore/links'>
|
||||
<FormattedMessage tagName='div' id='explore.trending_links' defaultMessage='News' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Route path='/explore/tags' component={Tags} />
|
||||
<Route path='/explore/links' component={Links} />
|
||||
<Route path='/explore/suggestions' component={Suggestions} />
|
||||
<Route exact path={['/explore', '/explore/posts', '/search']}>
|
||||
<Statuses multiColumn={multiColumn} />
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content={isSearching ? 'noindex' : 'all'} />
|
||||
</Helmet>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content={isSearching ? 'noindex' : 'all'} />
|
||||
</Helmet>
|
||||
</>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,15 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { fetchTrendingLinks } from 'flavours/glitch/actions/trends';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import Story from './components/story';
|
||||
|
||||
|
@ -23,10 +26,17 @@ class Links extends PureComponent {
|
|||
links: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { dispatch, links, history } = this.props;
|
||||
|
||||
// If we're navigating back to the screen, do not trigger a reload
|
||||
if (history.action === 'POP' && links.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchTrendingLinks());
|
||||
}
|
||||
|
||||
|
@ -52,7 +62,7 @@ class Links extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
<div className='explore__links scrollable' data-nosnippet>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : links.map((link, i) => (
|
||||
|
@ -77,4 +87,4 @@ class Links extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Links);
|
||||
export default connect(mapStateToProps)(withRouter(Links));
|
||||
|
|
|
@ -204,7 +204,7 @@ class Results extends PureComponent {
|
|||
<button onClick={this.handleSelectStatuses} className={type === 'statuses' ? 'active' : undefined}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
|
||||
</div>
|
||||
|
||||
<div className='explore__search-results'>
|
||||
<div className='explore__search-results' data-nosnippet>
|
||||
<ScrollableList
|
||||
scrollKey='search-results'
|
||||
isLoading={isLoading}
|
||||
|
|
|
@ -3,15 +3,19 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
|
||||
import { fetchTrendingStatuses, expandTrendingStatuses } from 'flavours/glitch/actions/trends';
|
||||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import StatusList from 'flavours/glitch/components/status_list';
|
||||
import { getStatusList } from 'flavours/glitch/selectors';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
statusIds: getStatusList(state, 'trending'),
|
||||
|
@ -27,10 +31,17 @@ class Statuses extends PureComponent {
|
|||
hasMore: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { dispatch, statusIds, history } = this.props;
|
||||
|
||||
// If we're navigating back to the screen, do not trigger a reload
|
||||
if (history.action === 'POP' && statusIds.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchTrendingStatuses());
|
||||
}
|
||||
|
||||
|
@ -45,27 +56,23 @@ class Statuses extends PureComponent {
|
|||
const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DismissableBanner id='explore/statuses'>
|
||||
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' />
|
||||
</DismissableBanner>
|
||||
|
||||
<StatusList
|
||||
trackScroll
|
||||
timelineId='explore'
|
||||
statusIds={statusIds}
|
||||
scrollKey='explore-statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
withCounters
|
||||
/>
|
||||
</>
|
||||
<StatusList
|
||||
trackScroll
|
||||
prepend={<DismissableBanner id='explore/statuses'><FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' /></DismissableBanner>}
|
||||
alwaysPrepend
|
||||
timelineId='explore'
|
||||
statusIds={statusIds}
|
||||
scrollKey='explore-statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
withCounters
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Statuses);
|
||||
export default connect(mapStateToProps)(withRouter(Statuses));
|
||||
|
|
|
@ -3,12 +3,15 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { fetchSuggestions, dismissSuggestion } from 'flavours/glitch/actions/suggestions';
|
||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||
import AccountCard from 'flavours/glitch/features/directory/components/account_card';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['suggestions', 'items']),
|
||||
|
@ -21,10 +24,17 @@ class Suggestions extends PureComponent {
|
|||
isLoading: PropTypes.bool,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { dispatch, suggestions, history } = this.props;
|
||||
|
||||
// If we're navigating back to the screen, do not trigger a reload
|
||||
if (history.action === 'POP' && suggestions.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchSuggestions(true));
|
||||
}
|
||||
|
||||
|
@ -47,7 +57,7 @@ class Suggestions extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__suggestions'>
|
||||
<div className='explore__suggestions scrollable' data-nosnippet>
|
||||
{isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => (
|
||||
<AccountCard key={suggestion.get('account')} id={suggestion.get('account')} onDismiss={suggestion.get('source') === 'past_interactions' ? this.handleDismiss : null} />
|
||||
))}
|
||||
|
@ -57,4 +67,4 @@ class Suggestions extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Suggestions);
|
||||
export default connect(mapStateToProps)(withRouter(Suggestions));
|
||||
|
|
|
@ -3,6 +3,8 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
@ -10,6 +12,7 @@ import { fetchTrendingHashtags } from 'flavours/glitch/actions/trends';
|
|||
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
|
||||
import { ImmutableHashtag as Hashtag } from 'flavours/glitch/components/hashtag';
|
||||
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
|
||||
|
||||
|
@ -24,10 +27,17 @@ class Tags extends PureComponent {
|
|||
hashtags: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { dispatch, history, hashtags } = this.props;
|
||||
|
||||
// If we're navigating back to the screen, do not trigger a reload
|
||||
if (history.action === 'POP' && hashtags.size > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchTrendingHashtags());
|
||||
}
|
||||
|
||||
|
@ -53,7 +63,7 @@ class Tags extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='explore__links'>
|
||||
<div className='scrollable explore__links' data-nosnippet>
|
||||
{banner}
|
||||
|
||||
{isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => (
|
||||
|
@ -65,4 +75,4 @@ class Tags extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Tags);
|
||||
export default connect(mapStateToProps)(withRouter(Tags));
|
||||
|
|
|
@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
||||
|
||||
const mapStateToProps = (state, { filterId }) => ({
|
||||
|
|
|
@ -188,33 +188,31 @@ const Firehose = ({ feedType, multiColumn }) => {
|
|||
<ColumnSettings />
|
||||
</ColumnHeader>
|
||||
|
||||
<div className='scrollable scrollable--flex'>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/public/local'>
|
||||
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
|
||||
</NavLink>
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to='/public/local'>
|
||||
<FormattedMessage tagName='div' id='firehose.local' defaultMessage='This server' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/public/remote'>
|
||||
<FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />
|
||||
</NavLink>
|
||||
<NavLink exact to='/public/remote'>
|
||||
<FormattedMessage tagName='div' id='firehose.remote' defaultMessage='Other servers' />
|
||||
</NavLink>
|
||||
|
||||
<NavLink exact to='/public'>
|
||||
<FormattedMessage tagName='div' id='firehose.all' defaultMessage='All' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<StatusListContainer
|
||||
prepend={prependBanner}
|
||||
timelineId={`${feedType}${feedType === 'public' && allowLocalOnly ? ':allow_local_only' : ''}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
trackScroll
|
||||
scrollKey='firehose'
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
regex={regex}
|
||||
/>
|
||||
<NavLink exact to='/public'>
|
||||
<FormattedMessage tagName='div' id='firehose.all' defaultMessage='All' />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<StatusListContainer
|
||||
prepend={prependBanner}
|
||||
timelineId={`${feedType}${feedType === 'public' && allowLocalOnly ? ':allow_local_only' : ''}${onlyMedia ? ':media' : ''}`}
|
||||
onLoadMore={handleLoadMore}
|
||||
trackScroll
|
||||
scrollKey='firehose'
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
regex={regex}
|
||||
/>
|
||||
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages.title)}</title>
|
||||
<meta name='robots' content='noindex' />
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
@ -12,8 +13,9 @@ import { requestBrowserPermission } from 'flavours/glitch/actions/notifications'
|
|||
import { changeSetting, saveSettings } from 'flavours/glitch/actions/settings';
|
||||
import { fetchSuggestions } from 'flavours/glitch/actions/suggestions';
|
||||
import { markAsPartial } from 'flavours/glitch/actions/timelines';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import Column from 'flavours/glitch/features/ui/components/column';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
import imageGreeting from 'mastodon/../images/elephant_ui_greeting.svg';
|
||||
|
||||
import Account from './components/account';
|
||||
|
@ -25,14 +27,11 @@ const mapStateToProps = state => ({
|
|||
|
||||
class FollowRecommendations extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
|
@ -56,8 +55,7 @@ class FollowRecommendations extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
handleDone = () => {
|
||||
const { dispatch } = this.props;
|
||||
const { router } = this.context;
|
||||
const { history, dispatch } = this.props;
|
||||
|
||||
dispatch(requestBrowserPermission((permission) => {
|
||||
if (permission === 'granted') {
|
||||
|
@ -71,7 +69,7 @@ class FollowRecommendations extends ImmutablePureComponent {
|
|||
}
|
||||
}));
|
||||
|
||||
router.history.push('/home');
|
||||
history.push('/home');
|
||||
};
|
||||
|
||||
render () {
|
||||
|
@ -118,4 +116,4 @@ class FollowRecommendations extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(FollowRecommendations);
|
||||
export default withRouter(connect(mapStateToProps)(FollowRecommendations));
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PureComponent } from 'react';
|
|||
import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
@ -16,27 +17,22 @@ import { AnimatedNumber } from 'flavours/glitch/components/animated_number';
|
|||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import EmojiPickerDropdown from 'flavours/glitch/features/compose/containers/emoji_picker_dropdown_container';
|
||||
import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
import { unicodeMapping } from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'flavours/glitch/initial_state';
|
||||
import { assetHost } from 'flavours/glitch/utils/config';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
|
||||
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
||||
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
||||
});
|
||||
|
||||
class Content extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
class ContentWithRouter extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
announcement: ImmutablePropTypes.map.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
|
@ -91,25 +87,25 @@ class Content extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
onMentionClick = (mention, e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/@${mention.get('acct')}`);
|
||||
this.props.history.push(`/@${mention.get('acct')}`);
|
||||
}
|
||||
};
|
||||
|
||||
onHashtagClick = (hashtag, e) => {
|
||||
hashtag = hashtag.replace(/^#/, '');
|
||||
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
if (this.props.history&& e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/tags/${hashtag}`);
|
||||
this.props.history.push(`/tags/${hashtag}`);
|
||||
}
|
||||
};
|
||||
|
||||
onStatusClick = (status, e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
if (this.props.history && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
this.props.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -155,6 +151,8 @@ class Content extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
const Content = withRouter(ContentWithRouter);
|
||||
|
||||
class Emoji extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
|
|
@ -88,7 +88,6 @@ const badgeDisplay = (number, limit) => {
|
|||
class GettingStarted extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ const messages = defineMessages({
|
|||
class GettingStartedMisc extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -11,7 +11,7 @@ import { throttle, escapeRegExp } from 'lodash';
|
|||
|
||||
import { openModal, closeModal } from 'flavours/glitch/actions/modal';
|
||||
import api from 'flavours/glitch/api';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { registrationsOpen, sso_redirect } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PureComponent } from 'react';
|
|||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -22,6 +23,7 @@ import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
|
|||
import { RadioButton } from 'flavours/glitch/components/radio_button';
|
||||
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
|
||||
import StatusListContainer from 'flavours/glitch/features/ui/containers/status_list_container';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
|
||||
|
@ -38,10 +40,6 @@ const mapStateToProps = (state, props) => ({
|
|||
|
||||
class ListTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -50,6 +48,7 @@ class ListTimeline extends PureComponent {
|
|||
multiColumn: PropTypes.bool,
|
||||
list: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
|
@ -59,7 +58,7 @@ class ListTimeline extends PureComponent {
|
|||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('LIST', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
this.props.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -137,7 +136,7 @@ class ListTimeline extends PureComponent {
|
|||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/lists');
|
||||
this.props.history.push('/lists');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -242,4 +241,4 @@ class ListTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(ListTimeline));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(ListTimeline)));
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
|
||||
// Our imports.
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import NotificationOverlayContainer from '../containers/overlay_container';
|
||||
|
||||
import Report from './report';
|
||||
|
||||
export default class AdminReport extends ImmutablePureComponent {
|
||||
class AdminReport extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
hidden: PropTypes.bool,
|
||||
|
@ -28,6 +27,7 @@ export default class AdminReport extends ImmutablePureComponent {
|
|||
notification: ImmutablePropTypes.map.isRequired,
|
||||
unread: PropTypes.bool,
|
||||
report: ImmutablePropTypes.map.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
|
@ -45,15 +45,15 @@ export default class AdminReport extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpenProfile = () => {
|
||||
const { notification } = this.props;
|
||||
this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
const { history, notification } = this.props;
|
||||
history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleMention = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
const { history, notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), history);
|
||||
};
|
||||
|
||||
getHandlers () {
|
||||
|
@ -111,3 +111,5 @@ export default class AdminReport extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(AdminReport);
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
|
||||
// Our imports.
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
import AccountContainer from 'flavours/glitch/containers/account_container';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import NotificationOverlayContainer from '../containers/overlay_container';
|
||||
|
||||
export default class NotificationFollow extends ImmutablePureComponent {
|
||||
class NotificationAdminSignup extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
hidden: PropTypes.bool,
|
||||
|
@ -26,6 +25,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
account: ImmutablePropTypes.map.isRequired,
|
||||
notification: ImmutablePropTypes.map.isRequired,
|
||||
unread: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
|
@ -43,15 +43,15 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpenProfile = () => {
|
||||
const { notification } = this.props;
|
||||
this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
const { history, notification } = this.props;
|
||||
history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleMention = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
const { history, notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), history);
|
||||
};
|
||||
|
||||
getHandlers () {
|
||||
|
@ -104,3 +104,5 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(NotificationAdminSignup);
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
|
||||
// Our imports.
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
import AccountContainer from 'flavours/glitch/containers/account_container';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import NotificationOverlayContainer from '../containers/overlay_container';
|
||||
|
||||
export default class NotificationFollow extends ImmutablePureComponent {
|
||||
class NotificationFollow extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
hidden: PropTypes.bool,
|
||||
|
@ -26,6 +25,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
account: ImmutablePropTypes.map.isRequired,
|
||||
notification: ImmutablePropTypes.map.isRequired,
|
||||
unread: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
|
@ -43,15 +43,15 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpenProfile = () => {
|
||||
const { notification } = this.props;
|
||||
this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
const { history, notification } = this.props;
|
||||
history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleMention = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
const { history, notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), history);
|
||||
};
|
||||
|
||||
getHandlers () {
|
||||
|
@ -104,3 +104,5 @@ export default class NotificationFollow extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export default withRouter(NotificationFollow);
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
@ -14,6 +15,7 @@ import { DisplayName } from 'flavours/glitch/components/display_name';
|
|||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import Permalink from 'flavours/glitch/components/permalink';
|
||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||
|
||||
import NotificationOverlayContainer from '../containers/overlay_container';
|
||||
|
||||
|
@ -31,6 +33,7 @@ class FollowRequest extends ImmutablePureComponent {
|
|||
intl: PropTypes.object.isRequired,
|
||||
notification: ImmutablePropTypes.map.isRequired,
|
||||
unread: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
|
@ -48,15 +51,15 @@ class FollowRequest extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpenProfile = () => {
|
||||
const { notification } = this.props;
|
||||
this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
const { history, notification } = this.props;
|
||||
history.push(`/@${notification.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleMention = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
const { history, notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), history);
|
||||
};
|
||||
|
||||
getHandlers () {
|
||||
|
@ -135,4 +138,4 @@ class FollowRequest extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default injectIntl(FollowRequest);
|
||||
export default withRouter(injectIntl(FollowRequest));
|
||||
|
|
|
@ -7,7 +7,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import { requestBrowserPermission } from 'flavours/glitch/actions/notifications';
|
||||
import { changeSetting } from 'flavours/glitch/actions/settings';
|
||||
import Button from 'flavours/glitch/components/button';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue