Merge remote-tracking branch 'parent/main' into kb-upstream-20231026
This commit is contained in:
commit
5448bcf276
313 changed files with 3717 additions and 4735 deletions
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/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
|
||||
|
@ -136,20 +126,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'
|
||||
|
@ -212,28 +193,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'
|
||||
|
@ -330,28 +297,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,39 +1,26 @@
|
|||
# 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
|
||||
exclude:
|
||||
- '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/instances/show.html.haml'
|
||||
- 'app/views/admin/roles/_form.html.haml'
|
||||
- 'app/views/layouts/application.html.haml'
|
||||
|
||||
# Offense count: 3
|
||||
ViewLength:
|
||||
exclude:
|
||||
- 'app/views/admin/accounts/show.html.haml'
|
||||
- 'app/views/admin/instances/show.html.haml'
|
||||
- 'app/views/admin/reports/show.html.haml'
|
||||
- 'app/views/disputes/strikes/show.html.haml'
|
||||
|
||||
# Offense count: 15
|
||||
InstanceVariables:
|
||||
exclude:
|
||||
- 'app/views/application/_sidebar.html.haml'
|
||||
|
||||
# Offense count: 2
|
||||
IdNames:
|
||||
exclude:
|
||||
- 'app/views/oauth/authorizations/error.html.haml'
|
||||
- 'app/views/shared/_error_messages.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
|
||||
|
@ -78,7 +78,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'
|
||||
|
@ -178,7 +177,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'
|
||||
|
@ -217,19 +215,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:
|
||||
|
@ -271,7 +256,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:
|
||||
|
@ -287,7 +271,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'
|
||||
|
||||
|
@ -307,24 +290,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:
|
||||
|
@ -377,36 +342,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/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'
|
||||
- 'db/migrate/20230314021909_add_group_message_following_only_to_accounts.rb'
|
||||
- 'db/migrate/20230314081013_add_group_allow_private_message_to_accounts.rb'
|
||||
- 'db/migrate/20230412005311_add_markdown_to_statuses.rb'
|
||||
- 'db/migrate/20230412073021_add_markdown_to_status_edits.rb'
|
||||
- 'db/migrate/20230428111230_add_emoji_reaction_streaming_to_accounts.rb'
|
||||
- 'db/migrate/20230510004621_remove_stop_emoji_reaction_streaming_from_accounts.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/UniqueValidationWithoutIndex:
|
||||
|
@ -470,7 +405,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:
|
||||
|
@ -678,7 +613,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)
|
||||
|
@ -714,6 +741,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)
|
||||
|
@ -785,6 +813,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)
|
||||
|
@ -880,7 +909,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)!
|
||||
|
@ -932,4 +961,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
|
||||
|
|
|
@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
|
|||
include DomainControlHelper
|
||||
include DatabaseHelper
|
||||
include AuthorizedFetchHelper
|
||||
include SelfDestructHelper
|
||||
|
||||
helper_method :current_account
|
||||
helper_method :current_session
|
||||
|
@ -39,6 +40,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?
|
||||
|
||||
|
@ -170,6 +173,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
|
||||
|
|
|
@ -7,6 +7,7 @@ class Auth::ChallengesController < ApplicationController
|
|||
|
||||
before_action :authenticate_user!
|
||||
|
||||
skip_before_action :check_self_destruct!
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def create
|
||||
|
|
|
@ -12,6 +12,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
|
||||
|
@ -38,6 +39,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
show
|
||||
end
|
||||
|
||||
def redirect_to_app?
|
||||
truthy_param?(:redirect_to_app)
|
||||
end
|
||||
|
||||
helper_method :redirect_to_app?
|
||||
|
||||
private
|
||||
|
||||
def require_captcha_if_needed!
|
||||
|
@ -81,7 +88,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
|||
end
|
||||
|
||||
def after_confirmation_path_for(_resource_name, user)
|
||||
if user.created_by_application && truthy_param?(:redirect_to_app)
|
||||
if user.created_by_application && redirect_to_app?
|
||||
user.created_by_application.confirmation_redirect_uri
|
||||
elsif user_signed_in?
|
||||
web_url('start')
|
||||
|
|
|
@ -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_body_classes
|
||||
|
||||
|
|
|
@ -17,6 +17,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
|
||||
|
|
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';
|
|
@ -18,7 +18,6 @@ import { Avatar } from './avatar';
|
|||
import { Button } from './button';
|
||||
import { FollowersCounter } from './counters';
|
||||
import { DisplayName } from './display_name';
|
||||
import { IconButton } from './icon_button';
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -46,10 +45,7 @@ class Account extends ImmutablePureComponent {
|
|||
hidden: PropTypes.bool,
|
||||
hideButtons: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
actionIcon: PropTypes.string,
|
||||
actionTitle: PropTypes.string,
|
||||
defaultAction: PropTypes.string,
|
||||
onActionClick: PropTypes.func,
|
||||
children: PropTypes.object,
|
||||
withBio: PropTypes.bool,
|
||||
};
|
||||
|
@ -78,12 +74,8 @@ class Account extends ImmutablePureComponent {
|
|||
this.props.onMuteNotifications(this.props.account, false);
|
||||
};
|
||||
|
||||
handleAction = () => {
|
||||
this.props.onActionClick(this.props.account);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl, hidden, hideButtons, withBio, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal, children } = this.props;
|
||||
const { account, intl, hidden, hideButtons, withBio, defaultAction, size, minimal, children } = this.props;
|
||||
|
||||
if (!account) {
|
||||
return <EmptyAccount size={size} minimal={minimal} />;
|
||||
|
@ -100,9 +92,7 @@ class Account extends ImmutablePureComponent {
|
|||
|
||||
let buttons;
|
||||
|
||||
if (actionIcon && onActionClick) {
|
||||
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
||||
} else if (!hideButtons && !actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||
if (!hideButtons && account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||
const following = account.getIn(['relationship', 'following']);
|
||||
const requested = account.getIn(['relationship', 'requested']);
|
||||
const blocking = account.getIn(['relationship', 'blocking']);
|
||||
|
|
|
@ -7,6 +7,8 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||
|
@ -25,7 +27,7 @@ export default class AttachmentList extends ImmutablePureComponent {
|
|||
<div className={classNames('attachment-list', { compact })}>
|
||||
{!compact && (
|
||||
<div className='attachment-list__icon'>
|
||||
<Icon id='link' />
|
||||
<Icon id='link' icon={LinkIcon} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -36,7 +38,7 @@ export default class AttachmentList extends ImmutablePureComponent {
|
|||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener noreferrer'>
|
||||
{compact && <Icon id='link' />}
|
||||
{compact && <Icon id='link' icon={LinkIcon} />}
|
||||
{compact && ' ' }
|
||||
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
|
||||
</a>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { assetHost } from 'mastodon/utils/config';
|
||||
|
||||
import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light';
|
||||
import { unicodeMapping } from '../features/emoji/emoji_unicode_mapping_light';
|
||||
|
||||
export default class AutosuggestEmoji extends PureComponent {
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as GroupsIcon } from '@material-design-icons/svg/outlined/group.svg';
|
||||
import { ReactComponent as PersonIcon } from '@material-design-icons/svg/outlined/person.svg';
|
||||
import { ReactComponent as SmartToyIcon } from '@material-design-icons/svg/outlined/smart_toy.svg';
|
||||
import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||
import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg';
|
||||
|
||||
|
||||
export const Badge = ({ icon, label, domain }) => (
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
export const Check: React.FC = () => (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 20 20'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z'
|
||||
clipRule='evenodd'
|
||||
/>
|
||||
</svg>
|
||||
);
|
|
@ -6,10 +6,12 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
class ColumnBackButton extends PureComponent {
|
||||
export class ColumnBackButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
|
@ -34,7 +36,7 @@ class ColumnBackButton extends PureComponent {
|
|||
|
||||
const component = (
|
||||
<button onClick={this.handleClick} className='column-back-button'>
|
||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import ColumnBackButton from './column_back_button';
|
||||
import { ColumnBackButton } from './column_back_button';
|
||||
|
||||
export default class ColumnBackButtonSlim extends ColumnBackButton {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className='column-back-button--slim'>
|
||||
<div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
|
||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@ import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
|||
import classNames from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
|
||||
import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg';
|
||||
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -27,6 +34,7 @@ class ColumnHeader extends PureComponent {
|
|||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
iconComponent: PropTypes.func,
|
||||
active: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
extraButton: PropTypes.node,
|
||||
|
@ -87,7 +95,7 @@ class ColumnHeader extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props;
|
||||
const { title, icon, iconComponent, 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', {
|
||||
|
@ -118,22 +126,22 @@ class ColumnHeader extends PureComponent {
|
|||
}
|
||||
|
||||
if (multiColumn && pinned) {
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
|
||||
|
||||
moveButtons = (
|
||||
<div key='move-buttons' className='column-header__setting-arrows'>
|
||||
<button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' /></button>
|
||||
<button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button>
|
||||
<button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' icon={ChevronLeftIcon} /></button>
|
||||
<button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' icon={ChevronRightIcon} /></button>
|
||||
</div>
|
||||
);
|
||||
} else if (multiColumn && this.props.onPin) {
|
||||
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>;
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' icon={AddIcon} /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
||||
}
|
||||
|
||||
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 />
|
||||
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</button>
|
||||
);
|
||||
|
@ -157,21 +165,21 @@ class ColumnHeader extends PureComponent {
|
|||
onClick={this.handleToggleClick}
|
||||
>
|
||||
<i className='icon-with-badge'>
|
||||
<Icon id='sliders' />
|
||||
<Icon id='sliders' icon={TuneIcon} />
|
||||
{collapseIssues && <i className='icon-with-badge__issue-badge' />}
|
||||
</i>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
const hasTitle = icon && title;
|
||||
const hasTitle = (icon || iconComponent) && title;
|
||||
|
||||
const component = (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 className={buttonClassName}>
|
||||
{hasTitle && (
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
||||
<Icon id={icon} icon={iconComponent} className='column-header__icon' />
|
||||
{title}
|
||||
</button>
|
||||
)}
|
||||
|
|
|
@ -103,6 +103,7 @@ class CompactedStatus extends ImmutablePureComponent {
|
|||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
cachedMediaWidth: PropTypes.number,
|
||||
history: PropTypes.func,
|
||||
};
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
|
@ -240,14 +241,14 @@ class CompactedStatus extends ImmutablePureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
const { router } = this.context;
|
||||
const { history } = this.props;
|
||||
const status = this._properStatus();
|
||||
|
||||
if (!router) {
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
|
@ -255,14 +256,14 @@ class CompactedStatus extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
_openProfile = (proper = true) => {
|
||||
const { router } = this.context;
|
||||
const { history } = this.props;
|
||||
const status = proper ? this._properStatus() : this.props.status;
|
||||
|
||||
if (!router) {
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.history.push(`/@${status.getIn(['account', 'acct'])}`);
|
||||
history.push(`/@${status.getIn(['account', 'acct'])}`);
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
|
|
|
@ -3,6 +3,8 @@ import { useCallback, useState } from 'react';
|
|||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { bannerSettings } from 'mastodon/settings';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
@ -36,6 +38,7 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
|||
<div className='dismissable-banner__action'>
|
||||
<IconButton
|
||||
icon='times'
|
||||
iconComponent={CloseIcon}
|
||||
title={intl.formatMessage(messages.dismiss)}
|
||||
onClick={handleDismiss}
|
||||
/>
|
||||
|
|
|
@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
|||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -34,6 +36,7 @@ export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => {
|
|||
<IconButton
|
||||
active
|
||||
icon='unlock'
|
||||
iconComponent={LockOpenIcon}
|
||||
title={intl.formatMessage(messages.unblockDomain, { domain })}
|
||||
onClick={handleDomainUnblock}
|
||||
/>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
|
@ -163,6 +164,7 @@ class Dropdown extends PureComponent {
|
|||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
iconComponent: PropTypes.func,
|
||||
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
|
||||
loading: PropTypes.bool,
|
||||
size: PropTypes.number,
|
||||
|
@ -255,7 +257,7 @@ class Dropdown extends PureComponent {
|
|||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
return this.target?.buttonRef?.current;
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
|
@ -271,6 +273,7 @@ class Dropdown extends PureComponent {
|
|||
render () {
|
||||
const {
|
||||
icon,
|
||||
iconComponent,
|
||||
items,
|
||||
size,
|
||||
title,
|
||||
|
@ -291,9 +294,11 @@ class Dropdown extends PureComponent {
|
|||
onMouseDown: this.handleMouseDown,
|
||||
onKeyDown: this.handleButtonKeyDown,
|
||||
onKeyPress: this.handleKeyPress,
|
||||
ref: this.setTargetRef,
|
||||
}) : (
|
||||
<IconButton
|
||||
icon={!open ? icon : 'close'}
|
||||
iconComponent={!open ? iconComponent : CloseIcon}
|
||||
title={title}
|
||||
active={open}
|
||||
disabled={disabled}
|
||||
|
@ -302,14 +307,14 @@ class Dropdown extends PureComponent {
|
|||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
ref={this.setTargetRef}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<span ref={this.setTargetRef}>
|
||||
{button}
|
||||
</span>
|
||||
{button}
|
||||
|
||||
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props, arrowProps, placement }) => (
|
||||
<div {...props}>
|
||||
|
|
|
@ -5,6 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ArrowDropDownIcon } from '@material-symbols/svg-600/outlined/arrow_drop_down.svg';
|
||||
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import InlineAccount from 'mastodon/components/inline_account';
|
||||
|
@ -66,7 +68,7 @@ class EditedTimestamp extends PureComponent {
|
|||
return (
|
||||
<DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}>
|
||||
<button className='dropdown-menu__text-button'>
|
||||
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' />
|
||||
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' icon={ArrowDropDownIcon} />
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
@ -1,20 +1,52 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
interface Props extends React.HTMLAttributes<HTMLImageElement> {
|
||||
id: string;
|
||||
className?: string;
|
||||
fixedWidth?: boolean;
|
||||
import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
|
||||
|
||||
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export type IconProp = React.FC<SVGPropsWithTitle>;
|
||||
|
||||
interface Props extends React.SVGProps<SVGSVGElement> {
|
||||
children?: never;
|
||||
id: string;
|
||||
icon: IconProp;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const Icon: React.FC<Props> = ({
|
||||
id,
|
||||
icon: IconComponent,
|
||||
className,
|
||||
fixedWidth,
|
||||
title: titleProp,
|
||||
...other
|
||||
}) => (
|
||||
<i
|
||||
className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!IconComponent) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
throw new Error(
|
||||
`<Icon id="${id}" className="${className}"> is missing an "icon" prop.`,
|
||||
);
|
||||
}
|
||||
|
||||
IconComponent = CheckBoxOutlineBlankIcon;
|
||||
}
|
||||
|
||||
const ariaHidden = titleProp ? undefined : true;
|
||||
const role = !ariaHidden ? 'img' : undefined;
|
||||
|
||||
// Set the title to an empty string to remove the built-in SVG one if any
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
const title = titleProp || '';
|
||||
|
||||
return (
|
||||
<IconComponent
|
||||
className={classNames('icon', `icon-${id}`, className)}
|
||||
title={title}
|
||||
aria-hidden={ariaHidden}
|
||||
role={role}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { PureComponent } from 'react';
|
||||
import { PureComponent, createRef } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { AnimatedNumber } from './animated_number';
|
||||
import type { IconProp } from './icon';
|
||||
import { Icon } from './icon';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
iconComponent: IconProp;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||
size: number;
|
||||
active: boolean;
|
||||
expanded?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
|
@ -33,8 +34,9 @@ interface States {
|
|||
deactivate: boolean;
|
||||
}
|
||||
export class IconButton extends PureComponent<Props, States> {
|
||||
buttonRef = createRef<HTMLButtonElement>();
|
||||
|
||||
static defaultProps = {
|
||||
size: 18,
|
||||
active: false,
|
||||
disabled: false,
|
||||
animate: false,
|
||||
|
@ -86,10 +88,6 @@ export class IconButton extends PureComponent<Props, States> {
|
|||
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: `${this.props.size}px`,
|
||||
width: `${this.props.size * 1.28571429}px`,
|
||||
height: `${this.props.size * 1.28571429}px`,
|
||||
lineHeight: `${this.props.size}px`,
|
||||
...this.props.style,
|
||||
...(this.props.active ? this.props.activeStyle : {}),
|
||||
};
|
||||
|
@ -100,6 +98,7 @@ export class IconButton extends PureComponent<Props, States> {
|
|||
disabled,
|
||||
expanded,
|
||||
icon,
|
||||
iconComponent,
|
||||
inverted,
|
||||
overlay,
|
||||
tabIndex,
|
||||
|
@ -122,13 +121,9 @@ export class IconButton extends PureComponent<Props, States> {
|
|||
'icon-button--with-counter': typeof counter !== 'undefined',
|
||||
});
|
||||
|
||||
if (typeof counter !== 'undefined') {
|
||||
style.width = 'auto';
|
||||
}
|
||||
|
||||
let contents = (
|
||||
<>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />{' '}
|
||||
<Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '}
|
||||
{typeof counter !== 'undefined' && (
|
||||
<span className='icon-button__counter'>
|
||||
<AnimatedNumber value={counter} />
|
||||
|
@ -161,6 +156,7 @@ export class IconButton extends PureComponent<Props, States> {
|
|||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
data-id={data_id}
|
||||
ref={this.buttonRef}
|
||||
>
|
||||
{contents}
|
||||
</button>
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import type { IconProp } from './icon';
|
||||
import { Icon } from './icon';
|
||||
|
||||
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
icon: IconProp;
|
||||
count: number;
|
||||
issueBadge: boolean;
|
||||
className: string;
|
||||
}
|
||||
export const IconWithBadge: React.FC<Props> = ({
|
||||
id,
|
||||
icon,
|
||||
count,
|
||||
issueBadge,
|
||||
className,
|
||||
}) => (
|
||||
<i className='icon-with-badge'>
|
||||
<Icon id={id} fixedWidth className={className} />
|
||||
<Icon id={id} icon={icon} className={className} />
|
||||
{count > 0 && (
|
||||
<i className='icon-with-badge__badge'>{formatNumber(count)}</i>
|
||||
)}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
|||
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -28,7 +30,7 @@ export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => {
|
|||
onClick={handleClick}
|
||||
aria-label={intl.formatMessage(messages.load_more)}
|
||||
>
|
||||
<Icon id='ellipsis-h' />
|
||||
<Icon id='ellipsis-h' icon={MoreHorizIcon} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import classNames from 'classnames';
|
|||
import { is } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
|
@ -342,7 +343,7 @@ class MediaGallery extends PureComponent {
|
|||
</button>
|
||||
);
|
||||
} else if (visible) {
|
||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />;
|
||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' iconComponent={VisibilityOffIcon} overlay onClick={this.handleOpen} ariaHidden />;
|
||||
} else {
|
||||
spoilerButton = (
|
||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||
|
|
|
@ -5,6 +5,8 @@ import { FormattedMessage } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CancelPresentationIcon } from '@material-symbols/svg-600/outlined/cancel_presentation.svg';
|
||||
|
||||
import { removePictureInPicture } from 'mastodon/actions/picture_in_picture';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
|
@ -25,7 +27,7 @@ class PictureInPicturePlaceholder extends PureComponent {
|
|||
|
||||
return (
|
||||
<div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}>
|
||||
<Icon id='window-restore' />
|
||||
<Icon id='window-restore' icon={CancelPresentationIcon} />
|
||||
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
|
||||
|
@ -192,7 +193,7 @@ class Poll extends ImmutablePureComponent {
|
|||
/>
|
||||
|
||||
{!!voted && <span className='poll__voted'>
|
||||
<Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} />
|
||||
<Icon id='check' icon={CheckIcon} className='poll__voted__mark' title={intl.formatMessage(messages.voted)} />
|
||||
</span>}
|
||||
</label>
|
||||
|
||||
|
|
|
@ -3,7 +3,11 @@ import React from 'react';
|
|||
|
||||
import { Router as OriginalRouter } from 'react-router';
|
||||
|
||||
import type { LocationDescriptor, Path } from 'history';
|
||||
import type {
|
||||
LocationDescriptor,
|
||||
LocationDescriptorObject,
|
||||
Path,
|
||||
} from 'history';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
import { layoutFromWindow } from 'mastodon/is_mobile';
|
||||
|
@ -20,39 +24,55 @@ const browserHistory = createBrowserHistory<
|
|||
const originalPush = browserHistory.push.bind(browserHistory);
|
||||
const originalReplace = browserHistory.replace.bind(browserHistory);
|
||||
|
||||
function extractRealPath(path: HistoryPath) {
|
||||
if (typeof path === 'string') return path;
|
||||
else return path.pathname;
|
||||
function normalizePath(
|
||||
path: HistoryPath,
|
||||
state?: MastodonLocationState,
|
||||
): LocationDescriptorObject<MastodonLocationState> {
|
||||
const location = typeof path === 'string' ? { pathname: path } : { ...path };
|
||||
|
||||
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) => {
|
||||
state = state ?? {};
|
||||
state.fromMastodon = true;
|
||||
const location = normalizePath(path, state);
|
||||
|
||||
const realPath = extractRealPath(path);
|
||||
if (!realPath) return;
|
||||
location.state = location.state ?? {};
|
||||
location.state.fromMastodon = true;
|
||||
|
||||
if (layoutFromWindow() === 'multi-column' && !realPath.startsWith('/deck')) {
|
||||
originalPush(`/deck${realPath}`, state);
|
||||
} else {
|
||||
originalPush(path, state);
|
||||
}
|
||||
originalPush(location);
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const realPath = extractRealPath(path);
|
||||
if (!realPath) return;
|
||||
|
||||
if (layoutFromWindow() === 'multi-column' && !realPath.startsWith('/deck')) {
|
||||
originalReplace(`/deck${realPath}`, state);
|
||||
} else {
|
||||
originalReplace(path, state);
|
||||
}
|
||||
originalReplace(location);
|
||||
};
|
||||
|
||||
export const Router: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
|
|
80
app/javascript/mastodon/components/searchability_icon.tsx
Normal file
80
app/javascript/mastodon/components/searchability_icon.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg';
|
||||
import { ReactComponent as PublicUnlistedIcon } from '@material-symbols/svg-600/outlined/cloud.svg';
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
type Searchability =
|
||||
| 'public'
|
||||
| 'public_unlisted'
|
||||
| 'private'
|
||||
| 'direct'
|
||||
| 'limited';
|
||||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'searchability.public.short', defaultMessage: 'Public' },
|
||||
public_unlisted_short: {
|
||||
id: 'searchability.public_unlisted.short',
|
||||
defaultMessage: 'Public unlisted',
|
||||
},
|
||||
private_short: {
|
||||
id: 'searchability.unlisted.short',
|
||||
defaultMessage: 'Followers',
|
||||
},
|
||||
direct_short: {
|
||||
id: 'searchability.private.short',
|
||||
defaultMessage: 'Reactionners',
|
||||
},
|
||||
limited_short: {
|
||||
id: 'searchability.direct.short',
|
||||
defaultMessage: 'Self only',
|
||||
},
|
||||
});
|
||||
|
||||
export const SearchabilityIcon: React.FC<{ searchability: Searchability }> = ({
|
||||
searchability,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const searchabilityIconInfo = {
|
||||
public: {
|
||||
icon: 'globe',
|
||||
iconComponent: PublicIcon,
|
||||
text: intl.formatMessage(messages.public_short),
|
||||
},
|
||||
public_unlisted: {
|
||||
icon: 'cloud',
|
||||
iconComponent: PublicUnlistedIcon,
|
||||
text: intl.formatMessage(messages.public_unlisted_short),
|
||||
},
|
||||
private: {
|
||||
icon: 'lock',
|
||||
iconComponent: LockOpenIcon,
|
||||
text: intl.formatMessage(messages.private_short),
|
||||
},
|
||||
limited: {
|
||||
icon: 'get-pocket',
|
||||
iconComponent: AlternateEmailIcon,
|
||||
text: intl.formatMessage(messages.limited_short),
|
||||
},
|
||||
direct: {
|
||||
icon: 'at',
|
||||
iconComponent: LockIcon,
|
||||
text: intl.formatMessage(messages.direct_short),
|
||||
},
|
||||
};
|
||||
|
||||
const searchabilityIcon = searchabilityIconInfo[searchability];
|
||||
|
||||
return (
|
||||
<Icon
|
||||
id={searchabilityIcon.icon}
|
||||
icon={searchabilityIcon.iconComponent}
|
||||
title={searchabilityIcon.text}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -7,6 +7,14 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg';
|
||||
import { ReactComponent as QuoteIcon } from '@material-symbols/svg-600/outlined/format_quote.svg';
|
||||
import { ReactComponent as ReferenceIcon } from '@material-symbols/svg-600/outlined/link.svg';
|
||||
import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg';
|
||||
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
|
||||
import { ReactComponent as LimitedIcon } from '@material-symbols/svg-600/outlined/shield.svg';
|
||||
import { ReactComponent as TimerIcon } from '@material-symbols/svg-600/outlined/timer.svg';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
|
||||
import AttachmentList from 'mastodon/components/attachment_list';
|
||||
|
@ -30,6 +38,7 @@ import { RelativeTimestamp } from './relative_timestamp';
|
|||
import StatusActionBar from './status_action_bar';
|
||||
import StatusContent from './status_content';
|
||||
import StatusEmojiReactionsBar from './status_emoji_reactions_bar';
|
||||
import { VisibilityIcon } from './visibility_icon';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
|
@ -399,25 +408,12 @@ class Status extends ImmutablePureComponent {
|
|||
const connectReply = nextInReplyToId && nextInReplyToId === status.get('id');
|
||||
const matchedFilters = status.get('matched_filters');
|
||||
|
||||
const visibilityIconInfo = {
|
||||
'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
|
||||
'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
|
||||
'public_unlisted': { icon: 'cloud', text: intl.formatMessage(messages.public_unlisted_short) },
|
||||
'login': { icon: 'key', text: intl.formatMessage(messages.login_short) },
|
||||
'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
|
||||
'limited': { icon: 'get-pocket', text: intl.formatMessage(messages.limited_short) },
|
||||
'mutual': { icon: 'exchange', text: intl.formatMessage(messages.mutual_short) },
|
||||
'circle': { icon: 'user-circle', text: intl.formatMessage(messages.circle_short) },
|
||||
'personal': { icon: 'sticky-note-o', text: intl.formatMessage(messages.personal_short) },
|
||||
'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) },
|
||||
};
|
||||
|
||||
let visibilityIcon = visibilityIconInfo[status.get('limited_scope') || status.get('visibility_ex')] || visibilityIconInfo[status.get('visibility')];
|
||||
let visibilityName = status.get('limited_scope') || status.get('visibility_ex') || status.get('visibility');
|
||||
|
||||
if (featured) {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' icon={PushPinIcon} className='status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
|
||||
</div>
|
||||
);
|
||||
|
@ -426,8 +422,8 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id={visibilityIcon.icon} className='status__prepend-icon' /></div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' icon={RepeatIcon} className='status__prepend-icon' /></div>
|
||||
<div className='status__prepend-icon-wrapper'><VisibilityIcon visibility={visibilityName} className='status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
|
||||
</div>
|
||||
);
|
||||
|
@ -439,7 +435,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else if (status.get('visibility') === 'direct') {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='at' icon={AlternateEmailIcon} className='status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
|
||||
</div>
|
||||
);
|
||||
|
@ -448,7 +444,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='reply' className='status__prepend-icon' fixedWidth /></div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='reply' icon={ReplyIcon} className='status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.replied_to' defaultMessage='Replied to {name}' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
|
||||
</div>
|
||||
);
|
||||
|
@ -473,7 +469,7 @@ class Status extends ImmutablePureComponent {
|
|||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div onClick={this.handleClick} className='status__info'>
|
||||
<a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
|
||||
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
|
||||
<span className='status__visibility-icon'><VisibilityIcon visibility={visibilityName} /></span>
|
||||
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
||||
</a>
|
||||
|
||||
|
@ -608,7 +604,7 @@ class Status extends ImmutablePureComponent {
|
|||
isCardMediaWithSensitive = status.get('spoiler_text').length > 0;
|
||||
}
|
||||
|
||||
visibilityIcon = visibilityIconInfo[status.get('limited_scope') || status.get('visibility_ex')] || visibilityIconInfo[status.get('visibility')];
|
||||
visibilityName = status.get('limited_scope') || status.get('visibility_ex') || status.get('visibility');
|
||||
|
||||
let emojiReactionsBar = null;
|
||||
if (!this.props.withoutEmojiReactions && status.get('emoji_reactions')) {
|
||||
|
@ -621,12 +617,12 @@ class Status extends ImmutablePureComponent {
|
|||
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
|
||||
const expanded = !status.get('hidden') || status.get('spoiler_text').length === 0;
|
||||
|
||||
const withLimited = status.get('visibility_ex') === 'limited' && status.get('limited_scope') ? <span className='status__visibility-icon'><Icon id='get-pocket' title='Limited' /></span> : null;
|
||||
const withQuote = status.get('quote_id') ? <span className='status__visibility-icon'><Icon id='quote-right' title='Quote' /></span> : null;
|
||||
const withReference = (!withQuote && status.get('status_references_count') > 0) ? <span className='status__visibility-icon'><Icon id='link' title='Reference' /></span> : null;
|
||||
const withExpiration = status.get('expires_at') ? <span className='status__visibility-icon'><Icon id='clock-o' title='Expiration' /></span> : null;
|
||||
const withLimited = status.get('visibility_ex') === 'limited' && status.get('limited_scope') ? <span className='status__visibility-icon'><Icon id='get-pocket' icon={LimitedIcon} title='Limited' /></span> : null;
|
||||
const withQuote = status.get('quote_id') ? <span className='status__visibility-icon'><Icon id='quote-right' icon={QuoteIcon} title='Quote' /></span> : null;
|
||||
const withReference = (!withQuote && status.get('status_references_count') > 0) ? <span className='status__visibility-icon'><Icon id='link' icon={ReferenceIcon} title='Reference' /></span> : null;
|
||||
const withExpiration = status.get('expires_at') ? <span className='status__visibility-icon'><Icon id='clock-o' icon={TimerIcon} title='Expiration' /></span> : null;
|
||||
|
||||
const quote = !muted && status.get('quote_id') && (['public', 'community'].includes(contextType) ? showQuoteInPublic : showQuoteInHome) && <CompactedStatusContainer id={status.get('quote_id')} />;
|
||||
const quote = !muted && status.get('quote_id') && (['public', 'community'].includes(contextType) ? showQuoteInPublic : showQuoteInHome) && <CompactedStatusContainer id={status.get('quote_id')} history={this.props.history} />;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
|
@ -643,7 +639,7 @@ class Status extends ImmutablePureComponent {
|
|||
{withReference}
|
||||
{withExpiration}
|
||||
{withLimited}
|
||||
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
|
||||
<span className='status__visibility-icon'><VisibilityIcon visibility={visibilityName} /></span>
|
||||
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
||||
</a>
|
||||
|
||||
|
|
|
@ -9,6 +9,17 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||
import { ReactComponent as BookmarkBorderIcon } from '@material-symbols/svg-600/outlined/bookmark.svg';
|
||||
import { ReactComponent as EmojiReactionIcon } from '@material-symbols/svg-600/outlined/mood.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
|
||||
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
|
||||
import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg';
|
||||
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
|
||||
import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg';
|
||||
import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg';
|
||||
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -253,7 +264,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleOpenMentions = () => {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}/mentioned_users`);
|
||||
this.props.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}/mentioned_users`);
|
||||
};
|
||||
|
||||
handleEmbed = () => {
|
||||
|
@ -413,12 +424,15 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
let replyIcon;
|
||||
let replyIconComponent;
|
||||
let replyTitle;
|
||||
if (status.get('in_reply_to_id', null) === null) {
|
||||
replyIcon = 'reply';
|
||||
replyIconComponent = ReplyIcon;
|
||||
replyTitle = intl.formatMessage(messages.reply);
|
||||
} else {
|
||||
replyIcon = 'reply-all';
|
||||
replyIconComponent = ReplyAllIcon;
|
||||
replyTitle = intl.formatMessage(messages.replyAll);
|
||||
}
|
||||
|
||||
|
@ -436,7 +450,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
const filterButton = this.props.onFilter && (
|
||||
<IconButton className='status__action-bar__button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} />
|
||||
<IconButton className='status__action-bar__button' title={intl.formatMessage(messages.hide)} icon='eye' iconComponent={VisibilityIcon} onClick={this.handleHideClick} />
|
||||
);
|
||||
|
||||
const emojiReactionPolicy = account.getIn(['other_settings', 'emoji_reaction_policy']) || 'allow';
|
||||
|
@ -446,33 +460,33 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
const outside = emojiReactionPolicy !== 'outside_only' || (relationship && (relationship.get('following') || relationship.get('followed_by')));
|
||||
const denyFromAll = emojiReactionPolicy !== 'block' && emojiReactionPolicy !== 'block';
|
||||
const emojiPickerButton = (
|
||||
<IconButton className='status__action-bar__button' title={intl.formatMessage(messages.emojiReaction)} icon='smile-o' onClick={this.handleEmojiPickInnerButton} />
|
||||
<IconButton className='status__action-bar__button' title={intl.formatMessage(messages.emojiReaction)} icon='smile-o' iconComponent={EmojiReactionIcon} onClick={this.handleEmojiPickInnerButton} />
|
||||
);
|
||||
const emojiPickerDropdown = enableEmojiReaction && denyFromAll && (writtenByMe || (following && followed && mutual && outside)) && (
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={emojiPickerButton} />
|
||||
);
|
||||
|
||||
const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
||||
|
||||
return (
|
||||
<div className='status__action-bar'>
|
||||
<IconButton className='status__action-bar__button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
||||
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
|
||||
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
|
||||
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} />
|
||||
{emojiPickerDropdown}
|
||||
|
||||
{filterButton}
|
||||
|
||||
<div className='status__action-bar__dropdown'>
|
||||
<DropdownMenuContainer
|
||||
scrollKey={scrollKey}
|
||||
status={status}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
size={18}
|
||||
direction='right'
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
</div>
|
||||
<DropdownMenuContainer
|
||||
scrollKey={scrollKey}
|
||||
status={status}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
iconComponent={MoreHorizIcon}
|
||||
direction='right'
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import { Link, withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import PollContainer from 'mastodon/containers/poll_container';
|
||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
@ -262,7 +264,7 @@ class StatusContent extends PureComponent {
|
|||
|
||||
const readMoreButton = renderReadMore && (
|
||||
<button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'>
|
||||
<FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' fixedWidth />
|
||||
<FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' icon={ChevronRightIcon} />
|
||||
</button>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
@ -21,7 +23,7 @@ interface Props {
|
|||
}
|
||||
export const VerifiedBadge: React.FC<Props> = ({ link }) => (
|
||||
<span className='verified-badge'>
|
||||
<Icon id='check' className='verified-badge__mark' />
|
||||
<Icon id='check' icon={CheckIcon} className='verified-badge__mark' />
|
||||
<span dangerouslySetInnerHTML={stripRelMe(link)} />
|
||||
</span>
|
||||
);
|
||||
|
|
129
app/javascript/mastodon/components/visibility_icon.tsx
Normal file
129
app/javascript/mastodon/components/visibility_icon.tsx
Normal file
|
@ -0,0 +1,129 @@
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as CircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg';
|
||||
import { ReactComponent as PublicUnlistedIcon } from '@material-symbols/svg-600/outlined/cloud.svg';
|
||||
import { ReactComponent as MutualIcon } from '@material-symbols/svg-600/outlined/compare_arrows.svg';
|
||||
import { ReactComponent as LoginIcon } from '@material-symbols/svg-600/outlined/key.svg';
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
import { ReactComponent as LimitedIcon } from '@material-symbols/svg-600/outlined/shield.svg';
|
||||
import { ReactComponent as PersonalIcon } from '@material-symbols/svg-600/outlined/sticky_note.svg';
|
||||
|
||||
import { Icon } from './icon';
|
||||
|
||||
type Visibility =
|
||||
| 'public'
|
||||
| 'unlisted'
|
||||
| 'private'
|
||||
| 'direct'
|
||||
| 'public_unlisted'
|
||||
| 'login'
|
||||
| 'mutual'
|
||||
| 'circle'
|
||||
| 'personal'
|
||||
| 'limited';
|
||||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
public_unlisted_short: {
|
||||
id: 'privacy.public_unlisted.short',
|
||||
defaultMessage: 'Public unlisted',
|
||||
},
|
||||
login_short: { id: 'privacy.login.short', defaultMessage: 'Login only' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
private_short: {
|
||||
id: 'privacy.private.short',
|
||||
defaultMessage: 'Followers only',
|
||||
},
|
||||
limited_short: {
|
||||
id: 'privacy.limited.short',
|
||||
defaultMessage: 'Limited menbers only',
|
||||
},
|
||||
mutual_short: {
|
||||
id: 'privacy.mutual.short',
|
||||
defaultMessage: 'Mutual followers only',
|
||||
},
|
||||
circle_short: {
|
||||
id: 'privacy.circle.short',
|
||||
defaultMessage: 'Circle members only',
|
||||
},
|
||||
personal_short: {
|
||||
id: 'privacy.personal.short',
|
||||
defaultMessage: 'Yourself only',
|
||||
},
|
||||
direct_short: {
|
||||
id: 'privacy.direct.short',
|
||||
defaultMessage: 'Mentioned people only',
|
||||
},
|
||||
});
|
||||
|
||||
export const VisibilityIcon: React.FC<{ visibility: Visibility }> = ({
|
||||
visibility,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const visibilityIconInfo = {
|
||||
public: {
|
||||
icon: 'globe',
|
||||
iconComponent: PublicIcon,
|
||||
text: intl.formatMessage(messages.public_short),
|
||||
},
|
||||
public_unlisted: {
|
||||
icon: 'cloud',
|
||||
iconComponent: PublicUnlistedIcon,
|
||||
text: intl.formatMessage(messages.public_unlisted_short),
|
||||
},
|
||||
login: {
|
||||
icon: 'key',
|
||||
iconComponent: LoginIcon,
|
||||
text: intl.formatMessage(messages.login_short),
|
||||
},
|
||||
unlisted: {
|
||||
icon: 'unlock',
|
||||
iconComponent: LockOpenIcon,
|
||||
text: intl.formatMessage(messages.unlisted_short),
|
||||
},
|
||||
private: {
|
||||
icon: 'lock',
|
||||
iconComponent: LockIcon,
|
||||
text: intl.formatMessage(messages.private_short),
|
||||
},
|
||||
limited: {
|
||||
icon: 'get-pocket',
|
||||
iconComponent: LimitedIcon,
|
||||
text: intl.formatMessage(messages.limited_short),
|
||||
},
|
||||
mutual: {
|
||||
icon: 'exchange',
|
||||
iconComponent: MutualIcon,
|
||||
text: intl.formatMessage(messages.mutual_short),
|
||||
},
|
||||
circle: {
|
||||
icon: 'user-circle',
|
||||
iconComponent: CircleIcon,
|
||||
text: intl.formatMessage(messages.circle_short),
|
||||
},
|
||||
personal: {
|
||||
icon: 'sticky-note-o',
|
||||
iconComponent: PersonalIcon,
|
||||
text: intl.formatMessage(messages.personal_short),
|
||||
},
|
||||
direct: {
|
||||
icon: 'at',
|
||||
iconComponent: AlternateEmailIcon,
|
||||
text: intl.formatMessage(messages.direct_short),
|
||||
},
|
||||
};
|
||||
|
||||
const visibilityIcon = visibilityIconInfo[visibility];
|
||||
|
||||
return (
|
||||
<Icon
|
||||
id={visibilityIcon.icon}
|
||||
icon={visibilityIcon.iconComponent}
|
||||
title={visibilityIcon.text}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -10,6 +10,9 @@ import { List as ImmutableList } from 'immutable';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
|
||||
import { ReactComponent as ExpandMoreIcon } from '@material-symbols/svg-600/outlined/expand_more.svg';
|
||||
|
||||
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
|
||||
import Column from 'mastodon/components/column';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
@ -85,7 +88,7 @@ class Section extends PureComponent {
|
|||
return (
|
||||
<div className={classNames('about__section', { active: !collapsed })}>
|
||||
<div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
|
||||
<Icon id={collapsed ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title}
|
||||
<Icon id={collapsed ? 'chevron-right' : 'chevron-down'} icon={collapsed ? ChevronRightIcon : ExpandMoreIcon} /> {title}
|
||||
</div>
|
||||
|
||||
{!collapsed && (
|
||||
|
|
|
@ -3,6 +3,9 @@ import { FormattedMessage } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||
|
@ -22,12 +25,12 @@ export default class FollowRequestNote extends ImmutablePureComponent {
|
|||
|
||||
<div className='follow-request-banner__action'>
|
||||
<button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
|
||||
<Icon id='check' fixedWidth />
|
||||
<Icon id='check' icon={CheckIcon} />
|
||||
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
|
||||
<Icon id='times' fixedWidth />
|
||||
<Icon id='times' icon={CloseIcon} />
|
||||
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,12 @@ import { NavLink, withRouter } from 'react-router-dom';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
|
||||
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg';
|
||||
import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active.svg';
|
||||
|
||||
import { Avatar } from 'mastodon/components/avatar';
|
||||
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
|
||||
import { Button } from 'mastodon/components/button';
|
||||
|
@ -264,7 +270,7 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
||||
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsIcon : NotificationsActiveIcon} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||
}
|
||||
|
||||
if (me !== account.get('id')) {
|
||||
|
@ -286,7 +292,7 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (account.get('locked')) {
|
||||
lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />;
|
||||
lockedIcon = <Icon id='lock' icon={LockIcon} title={intl.formatMessage(messages.account_locked)} />;
|
||||
}
|
||||
|
||||
if (signedIn && account.get('id') !== me) {
|
||||
|
@ -421,7 +427,7 @@ class Header extends ImmutablePureComponent {
|
|||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' size={24} direction='right' />
|
||||
<DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' iconComponent={MoreHorizIcon} size={24} direction='right' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -459,7 +465,7 @@ class Header extends ImmutablePureComponent {
|
|||
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
|
||||
|
||||
<dd className='translate' title={pair.get('value_plain')}>
|
||||
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
|
||||
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' icon={CheckIcon} className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
|
||||
</dd>
|
||||
</dl>
|
||||
))}
|
||||
|
|
|
@ -5,6 +5,10 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AudiotrackIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
|
||||
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg';
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
|
||||
import { Blurhash } from 'mastodon/components/blurhash';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
|
||||
|
@ -69,7 +73,7 @@ export default class MediaItem extends ImmutablePureComponent {
|
|||
if (!visible) {
|
||||
icon = (
|
||||
<span className='account-gallery__item__icons'>
|
||||
<Icon id='eye-slash' />
|
||||
<Icon id='eye-slash' icon={VisibilityOffIcon} />
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
|
@ -84,9 +88,9 @@ export default class MediaItem extends ImmutablePureComponent {
|
|||
);
|
||||
|
||||
if (attachment.get('type') === 'audio') {
|
||||
label = <Icon id='music' />;
|
||||
label = <Icon id='music' icon={AudiotrackIcon} />;
|
||||
} else {
|
||||
label = <Icon id='play' />;
|
||||
label = <Icon id='play' icon={PlayArrowIcon} />;
|
||||
}
|
||||
} else if (attachment.get('type') === 'image') {
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
|
||||
|
|
|
@ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outlined/wifi.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { removeFromAntennaAdder, addToAntennaAdder, removeExcludeFromAntennaAdder, addExcludeToAntennaAdder } from '../../../actions/antennas';
|
||||
|
@ -67,16 +71,16 @@ class Antenna extends ImmutablePureComponent {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={this.handleRemove} />;
|
||||
button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={this.handleRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={this.handleAdd} />;
|
||||
button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={this.handleAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='list'>
|
||||
<div className='list__wrapper'>
|
||||
<div className='list__display-name'>
|
||||
<Icon id='wifi' className='column-link__icon' fixedWidth />
|
||||
<Icon id='wifi' icon={AntennaIcon} className='column-link__icon' fixedWidth />
|
||||
{antenna.get('title')}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,10 +2,14 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { removeFromAntennaEditor, addToAntennaEditor, removeExcludeFromAntennaEditor, addExcludeToAntennaEditor } from '../../../actions/antennas';
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
|
@ -58,9 +62,9 @@ class Account extends ImmutablePureComponent {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={isExclude ? onExcludeRemove : onRemove} />;
|
||||
button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={isExclude ? onExcludeRemove : onRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={isExclude ? onExcludeAdd : onAdd} />;
|
||||
button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={isExclude ? onExcludeAdd : onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
|
||||
import { changeAntennaEditorTitle, submitAntennaEditor } from '../../../actions/antennas';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
|
@ -61,6 +63,7 @@ class AntennaForm extends PureComponent {
|
|||
<IconButton
|
||||
disabled={disabled}
|
||||
icon='check'
|
||||
iconComponent={CheckIcon}
|
||||
title={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
|
|
|
@ -7,6 +7,9 @@ import classNames from 'classnames';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { fetchAntennaSuggestions, clearAntennaSuggestions, changeAntennaSuggestions } from '../../../actions/antennas';
|
||||
|
@ -69,8 +72,8 @@ class Search extends PureComponent {
|
|||
</label>
|
||||
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={classNames({ active: !hasValue })} />
|
||||
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||
<Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} />
|
||||
<Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,12 +3,17 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outlined/wifi.svg';
|
||||
import Select, { NonceProvider } from 'react-select';
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
|
@ -41,6 +46,7 @@ import ColumnHeader from 'mastodon/components/column_header';
|
|||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
import RadioPanel from './components/radio_panel';
|
||||
import TextList from './components/text_list';
|
||||
|
@ -74,10 +80,6 @@ const mapStateToProps = (state, props) => ({
|
|||
|
||||
class AntennaSetting extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -89,6 +91,7 @@ class AntennaSetting extends PureComponent {
|
|||
keywords: ImmutablePropTypes.map,
|
||||
tags: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -109,7 +112,7 @@ class AntennaSetting extends PureComponent {
|
|||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('ANTENNA', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
this.props.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -184,7 +187,7 @@ class AntennaSetting extends PureComponent {
|
|||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/antennasw');
|
||||
this.props.history.push('/antennasw');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -192,7 +195,7 @@ class AntennaSetting extends PureComponent {
|
|||
};
|
||||
|
||||
handleTimelineClick = () => {
|
||||
this.context.router.history.push(`/antennast/${this.props.params.id}`);
|
||||
this.props.history.push(`/antennast/${this.props.params.id}`);
|
||||
};
|
||||
|
||||
onStlToggle = ({ target }) => {
|
||||
|
@ -376,6 +379,7 @@ class AntennaSetting extends PureComponent {
|
|||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
||||
<ColumnHeader
|
||||
icon='wifi'
|
||||
iconComponent={AntennaIcon}
|
||||
title={title}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
|
@ -385,15 +389,15 @@ class AntennaSetting extends PureComponent {
|
|||
>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditAntennaClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='antennas.edit_static' defaultMessage='Edit antenna' />
|
||||
<Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='antennas.edit_static' defaultMessage='Edit antenna' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
||||
<Icon id='trash' /> <FormattedMessage id='antennas.delete' defaultMessage='Delete antenna' />
|
||||
<Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='antennas.delete' defaultMessage='Delete antenna' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleTimelineClick}>
|
||||
<Icon id='wifi' /> <FormattedMessage id='antennas.go_timeline' defaultMessage='Go to antenna timeline' />
|
||||
<Icon id='wifi' icon={AntennaIcon} /> <FormattedMessage id='antennas.go_timeline' defaultMessage='Go to antenna timeline' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -555,4 +559,4 @@ class AntennaSetting extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(AntennaSetting));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(AntennaSetting)));
|
||||
|
|
|
@ -3,11 +3,17 @@ 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';
|
||||
|
||||
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outlined/wifi.svg';
|
||||
|
||||
import { fetchAntenna, deleteAntenna } from 'mastodon/actions/antennas';
|
||||
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
|
@ -19,6 +25,7 @@ import { Icon } from 'mastodon/components/icon';
|
|||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteMessage: { id: 'confirmations.delete_antenna.message', defaultMessage: 'Are you sure you want to permanently delete this antenna?' },
|
||||
|
@ -32,10 +39,6 @@ const mapStateToProps = (state, props) => ({
|
|||
|
||||
class AntennaTimeline extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -44,6 +47,7 @@ class AntennaTimeline extends PureComponent {
|
|||
multiColumn: PropTypes.bool,
|
||||
antenna: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
intl: PropTypes.object.isRequired,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
|
@ -53,7 +57,7 @@ class AntennaTimeline extends PureComponent {
|
|||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('ANTENNA_TIMELINE', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
this.props.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -110,7 +114,7 @@ class AntennaTimeline extends PureComponent {
|
|||
};
|
||||
|
||||
handleEditClick = () => {
|
||||
this.context.router.history.push(`/antennasw/${this.props.params.id}`);
|
||||
this.props.history.push(`/antennasw/${this.props.params.id}`);
|
||||
};
|
||||
|
||||
handleDeleteClick = () => {
|
||||
|
@ -128,7 +132,7 @@ class AntennaTimeline extends PureComponent {
|
|||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/antennasw');
|
||||
this.props.history.push('/antennasw');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -159,6 +163,7 @@ class AntennaTimeline extends PureComponent {
|
|||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
||||
<ColumnHeader
|
||||
icon='wifi'
|
||||
iconComponent={AntennaIcon}
|
||||
active={hasUnread}
|
||||
title={title}
|
||||
onPin={this.handlePin}
|
||||
|
@ -169,11 +174,11 @@ class AntennaTimeline extends PureComponent {
|
|||
>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='antennas.edit' defaultMessage='Edit antenna' />
|
||||
<Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='antennas.edit' defaultMessage='Edit antenna' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
||||
<Icon id='trash' /> <FormattedMessage id='antennas.delete' defaultMessage='Delete antenna' />
|
||||
<Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='antennas.delete' defaultMessage='Delete antenna' />
|
||||
</button>
|
||||
</div>
|
||||
</ColumnHeader>
|
||||
|
@ -197,4 +202,4 @@ class AntennaTimeline extends PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(AntennaTimeline));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(AntennaTimeline)));
|
||||
|
|
|
@ -9,6 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outlined/wifi.svg';
|
||||
|
||||
import { fetchAntennas } from 'mastodon/actions/antennas';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
|
@ -67,7 +69,7 @@ class Antennas extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='wifi' multiColumn={multiColumn} />
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='wifi' iconComponent={AntennaIcon} multiColumn={multiColumn} />
|
||||
|
||||
<NewAntennaForm />
|
||||
|
||||
|
@ -78,7 +80,7 @@ class Antennas extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{antennas.map(antenna => (
|
||||
<ColumnLink key={antenna.get('id')} to={`/antennast/${antenna.get('id')}`} icon='wifi' text={antenna.get('title')}
|
||||
<ColumnLink key={antenna.get('id')} to={`/antennast/${antenna.get('id')}`} icon='wifi' iconComponent={AntennaIcon} text={antenna.get('title')}
|
||||
badge={antenna.get('insert_feeds') ? intl.formatMessage(antenna.get('list') ? messages.insert_list : messages.insert_home) : undefined} />
|
||||
))}
|
||||
</ScrollableList>
|
||||
|
|
|
@ -7,6 +7,12 @@ import classNames from 'classnames';
|
|||
|
||||
import { is } from 'immutable';
|
||||
|
||||
import { ReactComponent as DownloadIcon } from '@material-symbols/svg-600/outlined/download.svg';
|
||||
import { ReactComponent as PauseIcon } from '@material-symbols/svg-600/outlined/pause.svg';
|
||||
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg';
|
||||
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||
import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg';
|
||||
import { ReactComponent as VolumeUpIcon } from '@material-symbols/svg-600/outlined/volume_up.svg';
|
||||
import { throttle, debounce } from 'lodash';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
@ -554,8 +560,8 @@ class Audio extends PureComponent {
|
|||
<div className='video-player__controls active'>
|
||||
<div className='video-player__buttons-bar'>
|
||||
<div className='video-player__buttons left'>
|
||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
|
||||
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} icon={paused ? PlayArrowIcon : PauseIcon} /></button>
|
||||
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} icon={muted ? VolumeOffIcon : VolumeUpIcon} /></button>
|
||||
|
||||
<div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}>
|
||||
<div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} />
|
||||
|
@ -575,9 +581,9 @@ class Audio extends PureComponent {
|
|||
</div>
|
||||
|
||||
<div className='video-player__buttons right'>
|
||||
{!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
|
||||
{!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' icon={VisibilityOffIcon} /></button>}
|
||||
<a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download>
|
||||
<Icon id={'download'} fixedWidth />
|
||||
<Icon id={'download'} icon={DownloadIcon} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
||||
|
@ -59,7 +60,7 @@ class Blocks extends ImmutablePureComponent {
|
|||
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}>
|
||||
<Column bindToDocument={!multiColumn} icon='ban' iconComponent={BlockIcon} heading={intl.formatMessage(messages.heading)}>
|
||||
<ColumnBackButtonSlim />
|
||||
<ScrollableList
|
||||
scrollKey='blocks'
|
||||
|
|
|
@ -9,6 +9,9 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark.svg';
|
||||
|
||||
import { fetchBookmarkCategories } from 'mastodon/actions/bookmark_categories';
|
||||
import Column from 'mastodon/components/column';
|
||||
import ColumnHeader from 'mastodon/components/column_header';
|
||||
|
@ -66,11 +69,11 @@ class BookmarkCategories extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} />
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='bookmark' iconComponent={BookmarksIcon} multiColumn={multiColumn} />
|
||||
|
||||
<NewListForm />
|
||||
|
||||
<ColumnLink to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.allBookmarks)} />
|
||||
<ColumnLink to='/bookmarks' icon='bookmark' iconComponent={BookmarkIcon} text={intl.formatMessage(messages.allBookmarks)} />
|
||||
<ScrollableList
|
||||
scrollKey='bookmark_categories'
|
||||
emptyMessage={emptyMessage}
|
||||
|
@ -78,7 +81,7 @@ class BookmarkCategories extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{categories.map(category =>
|
||||
<ColumnLink key={category.get('id')} to={`/bookmark_categories/${category.get('id')}`} icon='bookmark' text={category.get('title')} />,
|
||||
<ColumnLink key={category.get('id')} to={`/boozkmark_categories/${category.get('id')}`} icon='bookmark' iconComponent={BookmarkIcon} text={category.get('title')} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
|
||||
|
|
|
@ -2,10 +2,15 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { removeFromBookmarkCategoryAdder, addToBookmarkCategoryAdder } from '../../../actions/bookmark_categories';
|
||||
|
@ -46,16 +51,16 @@ class BookmarkCategory extends ImmutablePureComponent {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='list'>
|
||||
<div className='list__wrapper'>
|
||||
<div className='list__display-name'>
|
||||
<Icon id='bookmark' className='column-link__icon' fixedWidth />
|
||||
<Icon id='bookmark' icon={BookmarkIcon} className='column-link__icon' fixedWidth />
|
||||
{bookmarkCategory.get('title')}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
|
||||
import { changeBookmarkCategoryEditorTitle, submitBookmarkCategoryEditor } from '../../../actions/bookmark_categories';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
|
@ -61,6 +63,7 @@ class EditBookmarkCategoryForm extends PureComponent {
|
|||
<IconButton
|
||||
disabled={disabled}
|
||||
icon='check'
|
||||
iconComponent={CheckIcon}
|
||||
title={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
|
|
|
@ -2,12 +2,18 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl, 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';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
||||
import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg';
|
||||
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { deleteBookmarkCategory, expandBookmarkCategoryStatuses, fetchBookmarkCategory, fetchBookmarkCategoryStatuses , setupBookmarkCategoryEditor } from 'mastodon/actions/bookmark_categories';
|
||||
|
@ -20,6 +26,7 @@ import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
|||
import StatusList from 'mastodon/components/status_list';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import { getBookmarkCategoryStatusList } from 'mastodon/selectors';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
import EditBookmarkCategoryForm from './components/edit_bookmark_category_form';
|
||||
|
||||
|
@ -40,10 +47,6 @@ const mapStateToProps = (state, { params }) => ({
|
|||
|
||||
class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -55,6 +58,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
hasMore: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
isEditing: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount () {
|
||||
|
@ -69,7 +73,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('BOOKMARKS_EX', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
this.props.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -101,7 +105,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/bookmark_categories');
|
||||
this.props.history.push('/bookmark_categories');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -144,6 +148,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader
|
||||
icon='bookmark'
|
||||
iconComponent={BookmarkIcon}
|
||||
title={bookmarkCategory.get('title')}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
|
@ -153,11 +158,11 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='bookmark_categories.edit' defaultMessage='Edit category' />
|
||||
<Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='bookmark_categories.edit' defaultMessage='Edit category' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
||||
<Icon id='trash' /> <FormattedMessage id='bookmark_categories.delete' defaultMessage='Delete category' />
|
||||
<Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='bookmark_categories.delete' defaultMessage='Delete category' />
|
||||
</button>
|
||||
|
||||
{editor}
|
||||
|
@ -185,4 +190,4 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(BookmarkCategoryStatuses));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(BookmarkCategoryStatuses)));
|
||||
|
|
|
@ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks';
|
||||
|
@ -79,7 +80,8 @@ class Bookmarks extends ImmutablePureComponent {
|
|||
return (
|
||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader
|
||||
icon='bookmark'
|
||||
icon='bookmarks'
|
||||
iconComponent={BookmarksIcon}
|
||||
title={intl.formatMessage(messages.heading)}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
|
|
|
@ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { removeFromCircleAdder, addToCircleAdder } from '../../../actions/circles';
|
||||
|
@ -46,16 +50,16 @@ class Circle extends ImmutablePureComponent {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='list'>
|
||||
<div className='list__wrapper'>
|
||||
<div className='list__display-name'>
|
||||
<Icon id='user-circle' className='column-link__icon' fixedWidth />
|
||||
<Icon id='user-circle' icon={CircleIcon} className='column-link__icon' fixedWidth />
|
||||
{circle.get('title')}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,10 +2,14 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import { removeFromCircleEditor, addToCircleEditor } from '../../../actions/circles';
|
||||
import { Avatar } from '../../../components/avatar';
|
||||
import { DisplayName } from '../../../components/display_name';
|
||||
|
@ -53,9 +57,9 @@ class Account extends ImmutablePureComponent {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||
|
||||
import { changeCircleEditorTitle, submitCircleEditor } from '../../../actions/circles';
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
|
@ -61,6 +63,7 @@ class CircleForm extends PureComponent {
|
|||
<IconButton
|
||||
disabled={disabled}
|
||||
icon='check'
|
||||
iconComponent={CheckIcon}
|
||||
title={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
|
|
|
@ -7,6 +7,9 @@ import classNames from 'classnames';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
||||
import { fetchCircleSuggestions, clearCircleSuggestions, changeCircleSuggestions } from '../../../actions/circles';
|
||||
|
@ -69,8 +72,8 @@ class Search extends PureComponent {
|
|||
</label>
|
||||
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={classNames({ active: !hasValue })} />
|
||||
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||
<Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} />
|
||||
<Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,12 +2,18 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl, 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';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
||||
import { ReactComponent as CircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
|
||||
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { deleteCircle, expandCircleStatuses, fetchCircle, fetchCircleStatuses } from 'mastodon/actions/circles';
|
||||
|
@ -20,6 +26,7 @@ import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
|||
import StatusList from 'mastodon/components/status_list';
|
||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||
import { getCircleStatusList } from 'mastodon/selectors';
|
||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -38,10 +45,6 @@ const mapStateToProps = (state, { params }) => ({
|
|||
|
||||
class CircleStatuses extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
|
@ -52,6 +55,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
multiColumn: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
...WithRouterPropTypes,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount () {
|
||||
|
@ -66,7 +70,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
dispatch(addColumn('CIRCLE_STATUSES', { id: this.props.params.id }));
|
||||
this.context.router.history.push('/');
|
||||
this.props.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -101,7 +105,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
if (columnId) {
|
||||
dispatch(removeColumn(columnId));
|
||||
} else {
|
||||
this.context.router.history.push('/circles');
|
||||
this.props.history.push('/circles');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -140,6 +144,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader
|
||||
icon='user-circle'
|
||||
iconComponent={CircleIcon}
|
||||
title={circle.get('title')}
|
||||
onPin={this.handlePin}
|
||||
onMove={this.handleMove}
|
||||
|
@ -149,11 +154,11 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='circles.edit' defaultMessage='Edit circle' />
|
||||
<Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='circles.edit' defaultMessage='Edit circle' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
||||
<Icon id='trash' /> <FormattedMessage id='circles.delete' defaultMessage='Delete circle' />
|
||||
<Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='circles.delete' defaultMessage='Delete circle' />
|
||||
</button>
|
||||
</div>
|
||||
</ColumnHeader>
|
||||
|
@ -179,4 +184,4 @@ class CircleStatuses extends ImmutablePureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(CircleStatuses));
|
||||
export default withRouter(connect(mapStateToProps)(injectIntl(CircleStatuses)));
|
||||
|
|
|
@ -9,6 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { ReactComponent as CircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
|
||||
import { fetchCircles, deleteCircle } from 'mastodon/actions/circles';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import Column from 'mastodon/components/column';
|
||||
|
@ -94,7 +96,7 @@ class Circles extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='user-circle' multiColumn={multiColumn} />
|
||||
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='user-circle' iconComponent={CircleIcon} multiColumn={multiColumn} />
|
||||
|
||||
<NewCircleForm />
|
||||
|
||||
|
@ -105,7 +107,7 @@ class Circles extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{circles.map(circle =>
|
||||
<ColumnLink key={circle.get('id')} to={`/circles/${circle.get('id')}`} icon='user-circle' text={circle.get('title')} />,
|
||||
<ColumnLink key={circle.get('id')} to={`/circles/${circle.get('id')}`} icon='user-circle' iconComponent={CircleIcon} text={circle.get('title')} />,
|
||||
)}
|
||||
</ScrollableList>
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
|
|||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
|
||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||
import { domain } from 'mastodon/initial_state';
|
||||
|
||||
|
@ -128,6 +130,7 @@ class CommunityTimeline extends PureComponent {
|
|||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||
<ColumnHeader
|
||||
icon='users'
|
||||
iconComponent={PeopleIcon}
|
||||
active={hasUnread}
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onPin={this.handlePin}
|
||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
|
||||
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -21,7 +23,6 @@ const messages = defineMessages({
|
|||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
||||
antennas: { id: 'navigation_bar.antennas', defaultMessage: 'Antennas' },
|
||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
});
|
||||
|
@ -59,14 +60,13 @@ class ActionBar extends PureComponent {
|
|||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
|
||||
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
|
||||
menu.push({ text: intl.formatMessage(messages.antennas), href: '/antennas' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
|
||||
|
||||
return (
|
||||
<div className='compose__action-bar'>
|
||||
<div className='compose__action-bar-dropdown'>
|
||||
<DropdownMenuContainer items={menu} icon='bars' size={18} direction='right' />
|
||||
<DropdownMenuContainer items={menu} icon='bars' iconComponent={MenuIcon} size={24} direction='right' />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { length } from 'stringz';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
|
@ -242,7 +243,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
if (this.props.isEditing) {
|
||||
publishText = intl.formatMessage(messages.saveChanges);
|
||||
} else if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||
publishText = <span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span>;
|
||||
publishText = <><Icon id='lock' icon={LockIcon} /> {intl.formatMessage(messages.publish)}</>;
|
||||
} else {
|
||||
publishText = (this.props.privacy !== 'unlisted' && this.props.privacy !== 'public_unlisted' && this.props.privacy !== 'login') ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { injectIntl, defineMessages } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ReactComponent as TimerIcon } from '@material-symbols/svg-600/outlined/timer.svg';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
|
@ -237,6 +238,7 @@ class ExpirationDropdown extends PureComponent {
|
|||
<IconButton
|
||||
className='expiration-dropdown__value-icon'
|
||||
icon='clock-o'
|
||||
iconComponent={TimerIcon}
|
||||
title={intl.formatMessage(messages.add_expiration)}
|
||||
size={18}
|
||||
expanded={open}
|
||||
|
|
|
@ -3,8 +3,11 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
|
||||
|
||||
import { IconButton } from '../../../components/icon_button';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' },
|
||||
remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' },
|
||||
|
@ -40,6 +43,7 @@ class PollButton extends PureComponent {
|
|||
<div className='compose-form__poll-button'>
|
||||
<IconButton
|
||||
icon='tasks'
|
||||
iconComponent={InsertChartIcon}
|
||||
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
|
|
|
@ -8,6 +8,9 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { IconButton } from 'mastodon/components/icon_button';
|
||||
|
@ -108,7 +111,7 @@ class OptionIntl extends PureComponent {
|
|||
</label>
|
||||
|
||||
<div className='poll__cancel'>
|
||||
<IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} />
|
||||
<IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
@ -164,7 +167,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
<button type='button' disabled={options.size >= 8} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button>
|
||||
<button type='button' disabled={options.size >= 8} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button>
|
||||
|
||||
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
||||
|
|
|
@ -5,9 +5,19 @@ import { injectIntl, defineMessages } from 'react-intl';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
import { ReactComponent as CircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
|
||||
import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg';
|
||||
import { ReactComponent as PublicUnlistedIcon } from '@material-symbols/svg-600/outlined/cloud.svg';
|
||||
import { ReactComponent as MutualIcon } from '@material-symbols/svg-600/outlined/compare_arrows.svg';
|
||||
import { ReactComponent as LoginIcon } from '@material-symbols/svg-600/outlined/key.svg';
|
||||
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { enableLoginPrivacy, enableLocalPrivacy } from 'mastodon/initial_state';
|
||||
|
||||
|
@ -132,7 +142,7 @@ class PrivacyDropdownMenu extends PureComponent {
|
|||
{items.map(item => (
|
||||
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
||||
<div className='privacy-dropdown__option__icon'>
|
||||
<Icon id={item.icon} fixedWidth />
|
||||
<Icon id={item.icon} icon={item.iconComponent} />
|
||||
</div>
|
||||
|
||||
<div className='privacy-dropdown__option__content'>
|
||||
|
@ -231,14 +241,13 @@ class PrivacyDropdown extends PureComponent {
|
|||
const { intl: { formatMessage } } = this.props;
|
||||
|
||||
this.options = [
|
||||
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
||||
{ icon: 'cloud', value: 'public_unlisted', text: formatMessage(messages.public_unlisted_short), meta: formatMessage(messages.public_unlisted_long) },
|
||||
{ icon: 'key', value: 'login', text: formatMessage(messages.login_short), meta: formatMessage(messages.login_long) },
|
||||
{ icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
|
||||
{ icon: 'exchange', value: 'mutual', text: formatMessage(messages.mutual_short), meta: formatMessage(messages.mutual_long) },
|
||||
{ icon: 'user-circle', value: 'circle', text: formatMessage(messages.circle_short), meta: formatMessage(messages.circle_long) },
|
||||
{ icon: 'at', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
|
||||
{ icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
||||
{ icon: 'cloud', iconComponent: PublicUnlistedIcon, value: 'public_unlisted', text: formatMessage(messages.public_unlisted_short), meta: formatMessage(messages.public_unlisted_long) },
|
||||
{ icon: 'key', iconComponent: LoginIcon, value: 'login', text: formatMessage(messages.login_short), meta: formatMessage(messages.login_long) },
|
||||
{ icon: 'unlock', iconComponent: LockOpenIcon, value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
|
||||
{ icon: 'exchange', iconComponent: MutualIcon, value: 'mutual', text: formatMessage(messages.mutual_short), meta: formatMessage(messages.mutual_long) },
|
||||
{ icon: 'user-circle', iconComponent: CircleIcon, value: 'circle', text: formatMessage(messages.circle_short), meta: formatMessage(messages.circle_long) },
|
||||
];
|
||||
this.selectableOptions = [...this.options];
|
||||
|
||||
|
@ -250,8 +259,10 @@ class PrivacyDropdown extends PureComponent {
|
|||
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'public_unlisted');
|
||||
}
|
||||
|
||||
if (this.props.noDirect) {
|
||||
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'direct');
|
||||
if (!this.props.noDirect) {
|
||||
this.options.push(
|
||||
{ icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,25 +285,24 @@ class PrivacyDropdown extends PureComponent {
|
|||
const valueOption = this.options.find(item => item.value === value) || this.options[0];
|
||||
|
||||
return (
|
||||
<div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}>
|
||||
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })} ref={this.setTargetRef}>
|
||||
<IconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
icon={valueOption.icon}
|
||||
title={intl.formatMessage(messages.change_privacy)}
|
||||
size={18}
|
||||
expanded={open}
|
||||
active={open}
|
||||
inverted
|
||||
onClick={this.handleToggle}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
style={{ height: null, lineHeight: '27px' }}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}>
|
||||
<IconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
icon={valueOption.icon}
|
||||
iconComponent={valueOption.iconComponent}
|
||||
title={intl.formatMessage(messages.change_privacy)}
|
||||
size={18}
|
||||
expanded={open}
|
||||
active={open}
|
||||
inverted
|
||||
onClick={this.handleToggle}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
style={{ height: null, lineHeight: '27px' }}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
<Overlay show={open} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
|
||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
|
||||
import AttachmentList from 'mastodon/components/attachment_list';
|
||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
|
||||
|
||||
|
@ -48,7 +50,7 @@ class ReplyIndicator extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='reply-indicator'>
|
||||
<div className='reply-indicator__header'>
|
||||
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted /></div>
|
||||
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={this.handleClick} inverted /></div>
|
||||
|
||||
<a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
|
||||
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
|
||||
|
|
|
@ -8,6 +8,10 @@ import { withRouter } from 'react-router-dom';
|
|||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg';
|
||||
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
||||
|
@ -335,8 +339,8 @@ class Search extends PureComponent {
|
|||
/>
|
||||
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={hasValue ? '' : 'active'} />
|
||||
<Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
<Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} />
|
||||
<Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
</div>
|
||||
|
||||
<div className='search__popout'>
|
||||
|
@ -348,7 +352,7 @@ class Search extends PureComponent {
|
|||
{recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => (
|
||||
<button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
|
||||
<span>{label}</span>
|
||||
<button className='icon-button' onMouseDown={forget}><Icon id='times' /></button>
|
||||
<button className='icon-button' onMouseDown={forget}><Icon id='times' icon={CloseIcon} /></button>
|
||||
</button>
|
||||
)) : (
|
||||
<div className='search__popout__menu__message'>
|
||||
|
|
|
@ -5,6 +5,11 @@ import { FormattedMessage } from 'react-intl';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg';
|
||||
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
|
||||
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
|
||||
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { LoadMore } from 'mastodon/components/load_more';
|
||||
import { SearchSection } from 'mastodon/features/explore/components/search_section';
|
||||
|
@ -44,7 +49,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
|
||||
if (results.get('accounts') && results.get('accounts').size > 0) {
|
||||
accounts = (
|
||||
<SearchSection title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
|
||||
<SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
|
||||
{withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)}
|
||||
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
|
||||
</SearchSection>
|
||||
|
@ -53,7 +58,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
|
||||
if (results.get('hashtags') && results.get('hashtags').size > 0) {
|
||||
hashtags = (
|
||||
<SearchSection title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}>
|
||||
<SearchSection title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}>
|
||||
{withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
|
||||
{(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
|
||||
</SearchSection>
|
||||
|
@ -62,7 +67,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
|
||||
if (results.get('statuses') && results.get('statuses').size > 0) {
|
||||
statuses = (
|
||||
<SearchSection title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}>
|
||||
<SearchSection title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}>
|
||||
{withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)}
|
||||
{(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
|
||||
</SearchSection>
|
||||
|
@ -73,7 +78,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='search-results'>
|
||||
<div className='search-results__header'>
|
||||
<Icon id='search' fixedWidth />
|
||||
<Icon id='search' icon={SearchIcon} />
|
||||
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
|
||||
</div>
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue