Merge pull request #183 from kmycode/kb-upstream-20231026
Upstream 20231026
This commit is contained in:
commit
e32f00b275
324 changed files with 3755 additions and 4761 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: [
|
extends: [
|
||||||
'config:recommended',
|
'config:recommended',
|
||||||
':labels(dependencies)',
|
':labels(dependencies)',
|
||||||
':maintainLockFilesMonthly', // update non-direct dependencies monthly
|
|
||||||
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
|
||||||
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
|
':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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install native Ruby dependencies
|
- name: Set up Ruby environment
|
||||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- name: Set up Ruby
|
|
||||||
uses: ruby/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: .ruby-version
|
|
||||||
bundler-cache: true
|
|
||||||
|
|
||||||
- name: Run bundler-audit
|
- name: Run bundler-audit
|
||||||
run: bundle exec 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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install system dependencies
|
- name: Set up Ruby environment
|
||||||
run: |
|
uses: ./.github/actions/setup-ruby
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev
|
|
||||||
|
|
||||||
- name: Set up Ruby
|
- name: Set up Javascript environment
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ./.github/actions/setup-javascript
|
||||||
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: Check for missing strings in English JSON
|
- name: Check for missing strings in English JSON
|
||||||
run: |
|
run: |
|
||||||
|
|
10
.github/workflows/lint-css.yml
vendored
10
.github/workflows/lint-css.yml
vendored
|
@ -35,14 +35,8 @@ jobs:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- uses: xt0rted/stylelint-problem-matcher@v1
|
- 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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install native Ruby dependencies
|
- name: Set up Ruby environment
|
||||||
run: |
|
uses: ./.github/actions/setup-ruby
|
||||||
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: Run haml-lint
|
- name: Run haml-lint
|
||||||
run: |
|
run: |
|
||||||
|
|
10
.github/workflows/lint-js.yml
vendored
10
.github/workflows/lint-js.yml
vendored
|
@ -39,14 +39,8 @@ jobs:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- name: ESLint
|
- name: ESLint
|
||||||
run: yarn lint:js --max-warnings 0
|
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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Prettier
|
- name: Prettier
|
||||||
run: yarn lint:json
|
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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Prettier
|
- name: Prettier
|
||||||
run: yarn lint:md
|
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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install native Ruby dependencies
|
- name: Set up Ruby environment
|
||||||
run: sudo apt-get install -y libicu-dev libidn11-dev
|
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 RuboCop Problem Matcher
|
- name: Set-up RuboCop Problem Matcher
|
||||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Prettier
|
- name: Prettier
|
||||||
run: yarn lint:yml
|
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
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Javascript environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
|
||||||
cache: yarn
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Install all yarn packages
|
|
||||||
run: yarn --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Jest testing
|
- name: Jest testing
|
||||||
run: yarn jest --reporters github-actions summary
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install native Ruby dependencies
|
- name: Set up Ruby environment
|
||||||
run: |
|
uses: ./.github/actions/setup-ruby
|
||||||
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: Create database
|
- name: Create database
|
||||||
run: './bin/rails db:create'
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install native Ruby dependencies
|
- name: Set up Ruby environment
|
||||||
run: |
|
uses: ./.github/actions/setup-ruby
|
||||||
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: Create database
|
- name: Create database
|
||||||
run: './bin/rails db:create'
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Ruby environment
|
||||||
uses: actions/setup-node@v3
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
|
- name: Set up Javascript environment
|
||||||
|
uses: ./.github/actions/setup-javascript
|
||||||
with:
|
with:
|
||||||
cache: yarn
|
onlyProduction: 'true'
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- 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
|
- name: Precompile assets
|
||||||
# Previously had set this, but it's not supported
|
# Previously had set this, but it's not supported
|
||||||
# export NODE_OPTIONS=--openssl-legacy-provider
|
# export NODE_OPTIONS=--openssl-legacy-provider
|
||||||
|
@ -136,20 +126,11 @@ jobs:
|
||||||
path: './public'
|
path: './public'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Update package index
|
- name: Set up Ruby environment
|
||||||
run: sudo apt-get update
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- 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
|
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
bundler-cache: true
|
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
||||||
|
|
||||||
- name: Load database schema
|
- name: Load database schema
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
@ -212,28 +193,14 @@ jobs:
|
||||||
path: './public'
|
path: './public'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Update package index
|
- name: Set up Ruby environment
|
||||||
run: sudo apt-get update
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- 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
|
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
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
|
- name: Load database schema
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
@ -330,28 +297,14 @@ jobs:
|
||||||
path: './public'
|
path: './public'
|
||||||
name: ${{ github.sha }}
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
- name: Update package index
|
- name: Set up Ruby environment
|
||||||
run: sudo apt-get update
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- 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
|
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
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
|
- name: Load database schema
|
||||||
run: './bin/rails db:create db:schema:load db:seed'
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
|
|
@ -1,39 +1,30 @@
|
||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `haml-lint --auto-gen-config`
|
# `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
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the lints are removed from the code base.
|
# one by one as the lints are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of Haml-Lint, may require this file to be generated again.
|
# versions of Haml-Lint, may require this file to be generated again.
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
# Offense count: 946
|
# Offense count: 945
|
||||||
LineLength:
|
LineLength:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# Offense count: 22
|
# Offense count: 10
|
||||||
UnnecessaryStringOutput:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Offense count: 44
|
|
||||||
RuboCop:
|
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/roles/_form.html.haml'
|
||||||
|
- 'app/views/home/index.html.haml'
|
||||||
|
- 'app/views/layouts/application.html.haml'
|
||||||
|
|
||||||
# Offense count: 3
|
|
||||||
ViewLength:
|
ViewLength:
|
||||||
exclude:
|
exclude:
|
||||||
- 'app/views/admin/accounts/show.html.haml'
|
|
||||||
- 'app/views/admin/instances/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:
|
InstanceVariables:
|
||||||
exclude:
|
exclude:
|
||||||
- 'app/views/application/_sidebar.html.haml'
|
- '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
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
# `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
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# 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/accounts_controller_spec.rb'
|
||||||
- 'spec/controllers/admin/resets_controller_spec.rb'
|
- 'spec/controllers/admin/resets_controller_spec.rb'
|
||||||
- 'spec/controllers/admin/settings/branding_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/auth/sessions_controller_spec.rb'
|
||||||
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
|
- 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
|
||||||
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
|
- 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
|
||||||
|
@ -178,7 +177,6 @@ RSpec/LetSetup:
|
||||||
|
|
||||||
RSpec/MessageChain:
|
RSpec/MessageChain:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/controllers/api/v1/media_controller_spec.rb'
|
|
||||||
- 'spec/models/concerns/remotable_spec.rb'
|
- 'spec/models/concerns/remotable_spec.rb'
|
||||||
- 'spec/models/session_activation_spec.rb'
|
- 'spec/models/session_activation_spec.rb'
|
||||||
- 'spec/models/setting_spec.rb'
|
- 'spec/models/setting_spec.rb'
|
||||||
|
@ -217,19 +215,6 @@ Rails/ApplicationController:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/health_controller.rb'
|
- '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).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: Severity.
|
# Configuration parameters: Severity.
|
||||||
Rails/DuplicateAssociation:
|
Rails/DuplicateAssociation:
|
||||||
|
@ -271,7 +256,6 @@ Rails/LexicallyScopedActionFilter:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/auth/passwords_controller.rb'
|
- 'app/controllers/auth/passwords_controller.rb'
|
||||||
- 'app/controllers/auth/registrations_controller.rb'
|
- 'app/controllers/auth/registrations_controller.rb'
|
||||||
- 'app/controllers/auth/sessions_controller.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
Rails/NegateInclude:
|
Rails/NegateInclude:
|
||||||
|
@ -287,7 +271,6 @@ Rails/NegateInclude:
|
||||||
- 'app/models/custom_filter.rb'
|
- 'app/models/custom_filter.rb'
|
||||||
- 'app/services/activitypub/process_status_update_service.rb'
|
- 'app/services/activitypub/process_status_update_service.rb'
|
||||||
- 'app/services/fetch_link_card_service.rb'
|
- 'app/services/fetch_link_card_service.rb'
|
||||||
- 'app/services/search_service.rb'
|
|
||||||
- 'app/workers/web/push_notification_worker.rb'
|
- 'app/workers/web/push_notification_worker.rb'
|
||||||
- 'lib/paperclip/color_extractor.rb'
|
- 'lib/paperclip/color_extractor.rb'
|
||||||
|
|
||||||
|
@ -307,24 +290,6 @@ Rails/RakeEnvironment:
|
||||||
- 'lib/tasks/repo.rake'
|
- 'lib/tasks/repo.rake'
|
||||||
- 'lib/tasks/statistics.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.
|
# 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
|
# 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:
|
Rails/SkipsModelValidations:
|
||||||
|
@ -377,36 +342,6 @@ Rails/SkipsModelValidations:
|
||||||
- 'spec/services/follow_service_spec.rb'
|
- 'spec/services/follow_service_spec.rb'
|
||||||
- 'spec/services/update_account_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.
|
# Configuration parameters: Include.
|
||||||
# Include: app/models/**/*.rb
|
# Include: app/models/**/*.rb
|
||||||
Rails/UniqueValidationWithoutIndex:
|
Rails/UniqueValidationWithoutIndex:
|
||||||
|
@ -470,7 +405,7 @@ Style/CaseEquality:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'config/initializers/trusted_proxies.rb'
|
- 'config/initializers/trusted_proxies.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
# AllowedMethods: ==, equal?, eql?
|
# AllowedMethods: ==, equal?, eql?
|
||||||
Style/ClassEqualityComparison:
|
Style/ClassEqualityComparison:
|
||||||
|
@ -678,7 +613,6 @@ Style/RedundantReturn:
|
||||||
Style/SafeNavigation:
|
Style/SafeNavigation:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/models/concerns/account_finder_concern.rb'
|
- 'app/models/concerns/account_finder_concern.rb'
|
||||||
- 'app/models/status.rb'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyle.
|
# 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 \
|
libgdbm-dev \
|
||||||
libgmp-dev \
|
libgmp-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libyaml-0-2 \
|
libyaml-dev \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
libreadline8 \
|
libreadline8 \
|
||||||
python3 \
|
python3 \
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -4,7 +4,7 @@ source 'https://rubygems.org'
|
||||||
ruby '>= 3.0.0'
|
ruby '>= 3.0.0'
|
||||||
|
|
||||||
gem 'puma', '~> 6.3'
|
gem 'puma', '~> 6.3'
|
||||||
gem 'rails', '~> 7.0'
|
gem 'rails', '~> 7.1.1'
|
||||||
gem 'sprockets', '~> 3.7.2'
|
gem 'sprockets', '~> 3.7.2'
|
||||||
gem 'thor', '~> 1.2'
|
gem 'thor', '~> 1.2'
|
||||||
gem 'rack', '~> 2.2.7'
|
gem 'rack', '~> 2.2.7'
|
||||||
|
|
173
Gemfile.lock
173
Gemfile.lock
|
@ -39,75 +39,83 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.0.8)
|
actioncable (7.1.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailbox (7.0.8)
|
zeitwerk (~> 2.6)
|
||||||
actionpack (= 7.0.8)
|
actionmailbox (7.1.1)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activerecord (= 7.0.8)
|
activejob (= 7.1.1)
|
||||||
activestorage (= 7.0.8)
|
activerecord (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activestorage (= 7.1.1)
|
||||||
|
activesupport (= 7.1.1)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.0.8)
|
actionmailer (7.1.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.1.1)
|
||||||
activejob (= 7.0.8)
|
activejob (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (7.0.8)
|
actionpack (7.1.1)
|
||||||
actionview (= 7.0.8)
|
actionview (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
rack (~> 2.0, >= 2.2.4)
|
nokogiri (>= 1.8.5)
|
||||||
|
rack (>= 2.2.4)
|
||||||
|
rack-session (>= 1.0.1)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.6)
|
||||||
actiontext (7.0.8)
|
actiontext (7.1.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activerecord (= 7.0.8)
|
activerecord (= 7.1.1)
|
||||||
activestorage (= 7.0.8)
|
activestorage (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.0.8)
|
actionview (7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
rails-html-sanitizer (~> 1.6)
|
||||||
active_model_serializers (0.10.14)
|
active_model_serializers (0.10.14)
|
||||||
actionpack (>= 4.1)
|
actionpack (>= 4.1)
|
||||||
activemodel (>= 4.1)
|
activemodel (>= 4.1)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (7.0.8)
|
activejob (7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.0.8)
|
activemodel (7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
activerecord (7.0.8)
|
activerecord (7.1.1)
|
||||||
activemodel (= 7.0.8)
|
activemodel (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
activestorage (7.0.8)
|
timeout (>= 0.4.0)
|
||||||
actionpack (= 7.0.8)
|
activestorage (7.1.1)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activerecord (= 7.0.8)
|
activejob (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activerecord (= 7.1.1)
|
||||||
|
activesupport (= 7.1.1)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
mini_mime (>= 1.1.0)
|
activesupport (7.1.1)
|
||||||
activesupport (7.0.8)
|
base64
|
||||||
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
connection_pool (>= 2.2.5)
|
||||||
|
drb
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
|
mutex_m
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
addressable (2.8.5)
|
addressable (2.8.5)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
|
@ -158,6 +166,7 @@ GEM
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
parser (>= 2.4)
|
parser (>= 2.4)
|
||||||
smart_properties
|
smart_properties
|
||||||
|
bigdecimal (3.1.4)
|
||||||
bindata (2.4.15)
|
bindata (2.4.15)
|
||||||
binding_of_caller (1.0.0)
|
binding_of_caller (1.0.0)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
|
@ -237,6 +246,8 @@ GEM
|
||||||
dotenv-rails (2.8.1)
|
dotenv-rails (2.8.1)
|
||||||
dotenv (= 2.8.1)
|
dotenv (= 2.8.1)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
|
drb (2.1.1)
|
||||||
|
ruby2_keywords
|
||||||
ed25519 (1.3.0)
|
ed25519 (1.3.0)
|
||||||
elasticsearch (7.13.3)
|
elasticsearch (7.13.3)
|
||||||
elasticsearch-api (= 7.13.3)
|
elasticsearch-api (= 7.13.3)
|
||||||
|
@ -305,8 +316,8 @@ GEM
|
||||||
fuubar (2.5.1)
|
fuubar (2.5.1)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
ruby-progressbar (~> 1.4)
|
ruby-progressbar (~> 1.4)
|
||||||
globalid (1.1.0)
|
globalid (1.2.1)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 6.1)
|
||||||
haml (6.2.0)
|
haml (6.2.0)
|
||||||
temple (>= 0.8.2)
|
temple (>= 0.8.2)
|
||||||
thor
|
thor
|
||||||
|
@ -357,7 +368,11 @@ GEM
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
terminal-table (>= 1.5.1)
|
terminal-table (>= 1.5.1)
|
||||||
idn-ruby (0.1.5)
|
idn-ruby (0.1.5)
|
||||||
|
io-console (0.6.0)
|
||||||
ipaddress (0.8.3)
|
ipaddress (0.8.3)
|
||||||
|
irb (1.8.1)
|
||||||
|
rdoc
|
||||||
|
reline (>= 0.3.8)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.6.3)
|
json (2.6.3)
|
||||||
json-canonicalization (0.3.2)
|
json-canonicalization (0.3.2)
|
||||||
|
@ -434,7 +449,6 @@ GEM
|
||||||
azure-storage-blob (~> 2.0.1)
|
azure-storage-blob (~> 2.0.1)
|
||||||
hashie (~> 5.0)
|
hashie (~> 5.0)
|
||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
method_source (1.0.0)
|
|
||||||
mime-types (3.5.1)
|
mime-types (3.5.1)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2023.0808)
|
mime-types-data (3.2023.0808)
|
||||||
|
@ -444,11 +458,12 @@ GEM
|
||||||
msgpack (1.7.1)
|
msgpack (1.7.1)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.3.0)
|
multipart-post (2.3.0)
|
||||||
|
mutex_m (0.1.2)
|
||||||
net-http (0.3.2)
|
net-http (0.3.2)
|
||||||
uri
|
uri
|
||||||
net-http-persistent (4.0.2)
|
net-http-persistent (4.0.2)
|
||||||
connection_pool (~> 2.2)
|
connection_pool (~> 2.2)
|
||||||
net-imap (0.3.7)
|
net-imap (0.4.1)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.18.0)
|
net-ldap (0.18.0)
|
||||||
|
@ -456,7 +471,7 @@ GEM
|
||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.1)
|
net-protocol (0.2.1)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.3.3)
|
net-smtp (0.4.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.5.9)
|
nio4r (2.5.9)
|
||||||
nokogiri (1.15.4)
|
nokogiri (1.15.4)
|
||||||
|
@ -512,6 +527,8 @@ GEM
|
||||||
net-smtp
|
net-smtp
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
private_address_check (0.5.0)
|
private_address_check (0.5.0)
|
||||||
|
psych (5.1.1)
|
||||||
|
stringio
|
||||||
public_suffix (5.0.3)
|
public_suffix (5.0.3)
|
||||||
puma (6.4.0)
|
puma (6.4.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
|
@ -534,22 +551,27 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-proxy (0.7.6)
|
rack-proxy (0.7.6)
|
||||||
rack
|
rack
|
||||||
|
rack-session (1.0.1)
|
||||||
|
rack (< 3)
|
||||||
rack-test (2.1.0)
|
rack-test (2.1.0)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails (7.0.8)
|
rackup (1.0.0)
|
||||||
actioncable (= 7.0.8)
|
rack (< 3)
|
||||||
actionmailbox (= 7.0.8)
|
webrick
|
||||||
actionmailer (= 7.0.8)
|
rails (7.1.1)
|
||||||
actionpack (= 7.0.8)
|
actioncable (= 7.1.1)
|
||||||
actiontext (= 7.0.8)
|
actionmailbox (= 7.1.1)
|
||||||
actionview (= 7.0.8)
|
actionmailer (= 7.1.1)
|
||||||
activejob (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activemodel (= 7.0.8)
|
actiontext (= 7.1.1)
|
||||||
activerecord (= 7.0.8)
|
actionview (= 7.1.1)
|
||||||
activestorage (= 7.0.8)
|
activejob (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activemodel (= 7.1.1)
|
||||||
|
activerecord (= 7.1.1)
|
||||||
|
activestorage (= 7.1.1)
|
||||||
|
activesupport (= 7.1.1)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.0.8)
|
railties (= 7.1.1)
|
||||||
rails-controller-testing (1.0.5)
|
rails-controller-testing (1.0.5)
|
||||||
actionpack (>= 5.0.1.rc1)
|
actionpack (>= 5.0.1.rc1)
|
||||||
actionview (>= 5.0.1.rc1)
|
actionview (>= 5.0.1.rc1)
|
||||||
|
@ -564,19 +586,22 @@ GEM
|
||||||
rails-i18n (7.0.8)
|
rails-i18n (7.0.8)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (7.0.8)
|
railties (7.1.1)
|
||||||
actionpack (= 7.0.8)
|
actionpack (= 7.1.1)
|
||||||
activesupport (= 7.0.8)
|
activesupport (= 7.1.1)
|
||||||
method_source
|
irb
|
||||||
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0, >= 1.2.2)
|
||||||
zeitwerk (~> 2.5)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rdf (3.2.11)
|
rdf (3.2.11)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.6.1)
|
rdf-normalize (0.6.1)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
|
rdoc (6.5.0)
|
||||||
|
psych (>= 4.0.0)
|
||||||
redcarpet (3.6.0)
|
redcarpet (3.6.0)
|
||||||
redis (4.8.1)
|
redis (4.8.1)
|
||||||
redis-namespace (1.11.0)
|
redis-namespace (1.11.0)
|
||||||
|
@ -584,13 +609,15 @@ GEM
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.8.2)
|
regexp_parser (2.8.2)
|
||||||
|
reline (0.3.9)
|
||||||
|
io-console (~> 0.5)
|
||||||
request_store (1.5.1)
|
request_store (1.5.1)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.1.1)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
rexml (3.2.6)
|
rexml (3.2.6)
|
||||||
rotp (6.2.2)
|
rotp (6.3.0)
|
||||||
rouge (4.1.2)
|
rouge (4.1.2)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
rqrcode (2.2.0)
|
rqrcode (2.2.0)
|
||||||
|
@ -714,6 +741,7 @@ GEM
|
||||||
statsd-ruby (1.5.0)
|
statsd-ruby (1.5.0)
|
||||||
stoplight (3.0.2)
|
stoplight (3.0.2)
|
||||||
redlock (~> 1.0)
|
redlock (~> 1.0)
|
||||||
|
stringio (3.0.8)
|
||||||
strong_migrations (0.8.0)
|
strong_migrations (0.8.0)
|
||||||
activerecord (>= 5.2)
|
activerecord (>= 5.2)
|
||||||
swd (1.3.0)
|
swd (1.3.0)
|
||||||
|
@ -785,6 +813,7 @@ GEM
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
semantic_range (>= 2.3.0)
|
semantic_range (>= 2.3.0)
|
||||||
|
webrick (1.8.1)
|
||||||
websocket (1.2.10)
|
websocket (1.2.10)
|
||||||
websocket-driver (0.7.6)
|
websocket-driver (0.7.6)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
|
@ -880,7 +909,7 @@ DEPENDENCIES
|
||||||
rack-attack (~> 6.6)
|
rack-attack (~> 6.6)
|
||||||
rack-cors (~> 2.0)
|
rack-cors (~> 2.0)
|
||||||
rack-test (~> 2.1)
|
rack-test (~> 2.1)
|
||||||
rails (~> 7.0)
|
rails (~> 7.1.1)
|
||||||
rails-controller-testing (~> 1.0)
|
rails-controller-testing (~> 1.0)
|
||||||
rails-i18n (~> 7.0)
|
rails-i18n (~> 7.0)
|
||||||
rails-settings-cached (~> 0.6)!
|
rails-settings-cached (~> 0.6)!
|
||||||
|
@ -932,4 +961,4 @@ RUBY VERSION
|
||||||
ruby 3.2.2p53
|
ruby 3.2.2p53
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.4.13
|
2.4.20
|
||||||
|
|
|
@ -49,7 +49,7 @@ module Admin
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_instance
|
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
|
end
|
||||||
|
|
||||||
def set_instances
|
def set_instances
|
||||||
|
|
|
@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
|
||||||
include DomainControlHelper
|
include DomainControlHelper
|
||||||
include DatabaseHelper
|
include DatabaseHelper
|
||||||
include AuthorizedFetchHelper
|
include AuthorizedFetchHelper
|
||||||
|
include SelfDestructHelper
|
||||||
|
|
||||||
helper_method :current_account
|
helper_method :current_account
|
||||||
helper_method :current_session
|
helper_method :current_session
|
||||||
|
@ -39,6 +40,8 @@ class ApplicationController < ActionController::Base
|
||||||
service_unavailable
|
service_unavailable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before_action :check_self_destruct!
|
||||||
|
|
||||||
before_action :store_referrer, except: :raise_not_found, if: :devise_controller?
|
before_action :store_referrer, except: :raise_not_found, if: :devise_controller?
|
||||||
before_action :require_functional!, if: :user_signed_in?
|
before_action :require_functional!, if: :user_signed_in?
|
||||||
|
|
||||||
|
@ -170,6 +173,15 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
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
|
def set_cache_control_defaults
|
||||||
response.cache_control.replace(private: true, no_store: true)
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Auth::ChallengesController < ApplicationController
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||||
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha]
|
||||||
before_action :require_captcha_if_needed!, only: [:show]
|
before_action :require_captcha_if_needed!, only: [:show]
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
@ -38,6 +39,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||||
show
|
show
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def redirect_to_app?
|
||||||
|
truthy_param?(:redirect_to_app)
|
||||||
|
end
|
||||||
|
|
||||||
|
helper_method :redirect_to_app?
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def require_captcha_if_needed!
|
def require_captcha_if_needed!
|
||||||
|
@ -81,7 +88,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_confirmation_path_for(_resource_name, user)
|
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
|
user.created_by_application.confirmation_redirect_uri
|
||||||
elsif user_signed_in?
|
elsif user_signed_in?
|
||||||
web_url('start')
|
web_url('start')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :verify_authenticity_token
|
skip_before_action :verify_authenticity_token
|
||||||
|
|
||||||
def self.provides_callback_for(provider)
|
def self.provides_callback_for(provider)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Auth::PasswordsController < Devise::PasswordsController
|
class Auth::PasswordsController < Devise::PasswordsController
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
before_action :check_validity_of_reset_password_token, only: :edit
|
before_action :check_validity_of_reset_password_token, only: :edit
|
||||||
before_action :set_body_classes
|
before_action :set_body_classes
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
before_action :require_rules_acceptance!, only: :new
|
before_action :require_rules_acceptance!, only: :new
|
||||||
before_action :set_registration_form_time, 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]
|
skip_before_action :require_functional!, only: [:edit, :update]
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class Auth::SessionsController < Devise::SessionsController
|
class Auth::SessionsController < Devise::SessionsController
|
||||||
layout 'auth'
|
layout 'auth'
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_no_authentication, only: [:create]
|
skip_before_action :require_no_authentication, only: [:create]
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
skip_before_action :update_user_sign_in
|
skip_before_action :update_user_sign_in
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class BackupsController < ApplicationController
|
class BackupsController < ApplicationController
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
|
|
@ -7,6 +7,7 @@ module ExportControllerConcern
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :load_export
|
before_action :load_export
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ class Settings::ExportsController < Settings::BaseController
|
||||||
include Redisable
|
include Redisable
|
||||||
include Lockable
|
include Lockable
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Settings::LoginActivitiesController < Settings::BaseController
|
class Settings::LoginActivitiesController < Settings::BaseController
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@login_activities = LoginActivity.where(user: current_user).order(id: :desc).page(params[:page])
|
@login_activities = LoginActivity.where(user: current_user).order(id: :desc).page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
module Settings
|
module Settings
|
||||||
module TwoFactorAuthentication
|
module TwoFactorAuthentication
|
||||||
class WebauthnCredentialsController < BaseController
|
class WebauthnCredentialsController < BaseController
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :require_otp_enabled
|
before_action :require_otp_enabled
|
||||||
|
|
|
@ -4,6 +4,7 @@ module Settings
|
||||||
class TwoFactorAuthenticationMethodsController < BaseController
|
class TwoFactorAuthenticationMethodsController < BaseController
|
||||||
include ChallengableConcern
|
include ChallengableConcern
|
||||||
|
|
||||||
|
skip_before_action :check_self_destruct!
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :require_challenge!, only: :disable
|
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 { Button } from './button';
|
||||||
import { FollowersCounter } from './counters';
|
import { FollowersCounter } from './counters';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
import { IconButton } from './icon_button';
|
|
||||||
import { RelativeTimestamp } from './relative_timestamp';
|
import { RelativeTimestamp } from './relative_timestamp';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -46,10 +45,7 @@ class Account extends ImmutablePureComponent {
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
hideButtons: PropTypes.bool,
|
hideButtons: PropTypes.bool,
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool,
|
||||||
actionIcon: PropTypes.string,
|
|
||||||
actionTitle: PropTypes.string,
|
|
||||||
defaultAction: PropTypes.string,
|
defaultAction: PropTypes.string,
|
||||||
onActionClick: PropTypes.func,
|
|
||||||
children: PropTypes.object,
|
children: PropTypes.object,
|
||||||
withBio: PropTypes.bool,
|
withBio: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -78,12 +74,8 @@ class Account extends ImmutablePureComponent {
|
||||||
this.props.onMuteNotifications(this.props.account, false);
|
this.props.onMuteNotifications(this.props.account, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleAction = () => {
|
|
||||||
this.props.onActionClick(this.props.account);
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
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) {
|
if (!account) {
|
||||||
return <EmptyAccount size={size} minimal={minimal} />;
|
return <EmptyAccount size={size} minimal={minimal} />;
|
||||||
|
@ -100,9 +92,7 @@ class Account extends ImmutablePureComponent {
|
||||||
|
|
||||||
let buttons;
|
let buttons;
|
||||||
|
|
||||||
if (actionIcon && onActionClick) {
|
if (!hideButtons && account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||||
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
|
||||||
} else if (!hideButtons && !actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) {
|
|
||||||
const following = account.getIn(['relationship', 'following']);
|
const following = account.getIn(['relationship', 'following']);
|
||||||
const requested = account.getIn(['relationship', 'requested']);
|
const requested = account.getIn(['relationship', 'requested']);
|
||||||
const blocking = account.getIn(['relationship', 'blocking']);
|
const blocking = account.getIn(['relationship', 'blocking']);
|
||||||
|
|
|
@ -7,6 +7,8 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
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 })}>
|
<div className={classNames('attachment-list', { compact })}>
|
||||||
{!compact && (
|
{!compact && (
|
||||||
<div className='attachment-list__icon'>
|
<div className='attachment-list__icon'>
|
||||||
<Icon id='link' />
|
<Icon id='link' icon={LinkIcon} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<li key={attachment.get('id')}>
|
<li key={attachment.get('id')}>
|
||||||
<a href={displayUrl} target='_blank' rel='noopener noreferrer'>
|
<a href={displayUrl} target='_blank' rel='noopener noreferrer'>
|
||||||
{compact && <Icon id='link' />}
|
{compact && <Icon id='link' icon={LinkIcon} />}
|
||||||
{compact && ' ' }
|
{compact && ' ' }
|
||||||
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
|
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { assetHost } from 'mastodon/utils/config';
|
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 {
|
export default class AutosuggestEmoji extends PureComponent {
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { ReactComponent as GroupsIcon } from '@material-design-icons/svg/outlined/group.svg';
|
import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||||
import { ReactComponent as PersonIcon } from '@material-design-icons/svg/outlined/person.svg';
|
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||||
import { ReactComponent as SmartToyIcon } from '@material-design-icons/svg/outlined/smart_toy.svg';
|
import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg';
|
||||||
|
|
||||||
|
|
||||||
export const Badge = ({ icon, label, domain }) => (
|
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 { 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 { Icon } from 'mastodon/components/icon';
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
class ColumnBackButton extends PureComponent {
|
export class ColumnBackButton extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -34,7 +36,7 @@ class ColumnBackButton extends PureComponent {
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
<button onClick={this.handleClick} className='column-back-button'>
|
<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' />
|
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { FormattedMessage } from 'react-intl';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import ColumnBackButton from './column_back_button';
|
import { ColumnBackButton } from './column_back_button';
|
||||||
|
|
||||||
export default class ColumnBackButtonSlim extends ColumnBackButton {
|
export default class ColumnBackButtonSlim extends ColumnBackButton {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className='column-back-button--slim'>
|
<div className='column-back-button--slim'>
|
||||||
<div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
|
<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' />
|
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,13 @@ import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { withRouter } from 'react-router-dom';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
@ -27,6 +34,7 @@ class ColumnHeader extends PureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
title: PropTypes.node,
|
title: PropTypes.node,
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
|
iconComponent: PropTypes.func,
|
||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
extraButton: PropTypes.node,
|
extraButton: PropTypes.node,
|
||||||
|
@ -87,7 +95,7 @@ class ColumnHeader extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
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 { collapsed, animating } = this.state;
|
||||||
|
|
||||||
const wrapperClassName = classNames('column-header__wrapper', {
|
const wrapperClassName = classNames('column-header__wrapper', {
|
||||||
|
@ -118,22 +126,22 @@ class ColumnHeader extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiColumn && pinned) {
|
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 = (
|
moveButtons = (
|
||||||
<div key='move-buttons' className='column-header__setting-arrows'>
|
<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.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' /></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>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (multiColumn && this.props.onPin) {
|
} 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)) {
|
if (!pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) {
|
||||||
backButton = (
|
backButton = (
|
||||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
<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' />
|
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
@ -157,21 +165,21 @@ class ColumnHeader extends PureComponent {
|
||||||
onClick={this.handleToggleClick}
|
onClick={this.handleToggleClick}
|
||||||
>
|
>
|
||||||
<i className='icon-with-badge'>
|
<i className='icon-with-badge'>
|
||||||
<Icon id='sliders' />
|
<Icon id='sliders' icon={TuneIcon} />
|
||||||
{collapseIssues && <i className='icon-with-badge__issue-badge' />}
|
{collapseIssues && <i className='icon-with-badge__issue-badge' />}
|
||||||
</i>
|
</i>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasTitle = icon && title;
|
const hasTitle = (icon || iconComponent) && title;
|
||||||
|
|
||||||
const component = (
|
const component = (
|
||||||
<div className={wrapperClassName}>
|
<div className={wrapperClassName}>
|
||||||
<h1 className={buttonClassName}>
|
<h1 className={buttonClassName}>
|
||||||
{hasTitle && (
|
{hasTitle && (
|
||||||
<button onClick={this.handleTitleClick}>
|
<button onClick={this.handleTitleClick}>
|
||||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
<Icon id={icon} icon={iconComponent} className='column-header__icon' />
|
||||||
{title}
|
{title}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -103,6 +103,7 @@ class CompactedStatus extends ImmutablePureComponent {
|
||||||
updateScrollBottom: PropTypes.func,
|
updateScrollBottom: PropTypes.func,
|
||||||
cacheMediaWidth: PropTypes.func,
|
cacheMediaWidth: PropTypes.func,
|
||||||
cachedMediaWidth: PropTypes.number,
|
cachedMediaWidth: PropTypes.number,
|
||||||
|
history: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avoid checking props that are functions (and whose equality will always
|
// Avoid checking props that are functions (and whose equality will always
|
||||||
|
@ -240,14 +241,14 @@ class CompactedStatus extends ImmutablePureComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { router } = this.context;
|
const { history } = this.props;
|
||||||
const status = this._properStatus();
|
const status = this._properStatus();
|
||||||
|
|
||||||
if (!router) {
|
if (!history) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleHotkeyOpenProfile = () => {
|
handleHotkeyOpenProfile = () => {
|
||||||
|
@ -255,14 +256,14 @@ class CompactedStatus extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
_openProfile = (proper = true) => {
|
_openProfile = (proper = true) => {
|
||||||
const { router } = this.context;
|
const { history } = this.props;
|
||||||
const status = proper ? this._properStatus() : this.props.status;
|
const status = proper ? this._properStatus() : this.props.status;
|
||||||
|
|
||||||
if (!router) {
|
if (!history) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
router.history.push(`/@${status.getIn(['account', 'acct'])}`);
|
history.push(`/@${status.getIn(['account', 'acct'])}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleHotkeyMoveUp = e => {
|
handleHotkeyMoveUp = e => {
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
|
||||||
|
|
||||||
import { bannerSettings } from 'mastodon/settings';
|
import { bannerSettings } from 'mastodon/settings';
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
|
@ -36,6 +38,7 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
||||||
<div className='dismissable-banner__action'>
|
<div className='dismissable-banner__action'>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon='times'
|
icon='times'
|
||||||
|
iconComponent={CloseIcon}
|
||||||
title={intl.formatMessage(messages.dismiss)}
|
title={intl.formatMessage(messages.dismiss)}
|
||||||
onClick={handleDismiss}
|
onClick={handleDismiss}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
||||||
|
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -34,6 +36,7 @@ export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => {
|
||||||
<IconButton
|
<IconButton
|
||||||
active
|
active
|
||||||
icon='unlock'
|
icon='unlock'
|
||||||
|
iconComponent={LockOpenIcon}
|
||||||
title={intl.formatMessage(messages.unblockDomain, { domain })}
|
title={intl.formatMessage(messages.unblockDomain, { domain })}
|
||||||
onClick={handleDomainUnblock}
|
onClick={handleDomainUnblock}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
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 { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import Overlay from 'react-overlays/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
|
@ -163,6 +164,7 @@ class Dropdown extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
|
iconComponent: PropTypes.func,
|
||||||
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
|
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
size: PropTypes.number,
|
size: PropTypes.number,
|
||||||
|
@ -255,7 +257,7 @@ class Dropdown extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
findTarget = () => {
|
findTarget = () => {
|
||||||
return this.target;
|
return this.target?.buttonRef?.current;
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
|
@ -271,6 +273,7 @@ class Dropdown extends PureComponent {
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
icon,
|
icon,
|
||||||
|
iconComponent,
|
||||||
items,
|
items,
|
||||||
size,
|
size,
|
||||||
title,
|
title,
|
||||||
|
@ -291,9 +294,11 @@ class Dropdown extends PureComponent {
|
||||||
onMouseDown: this.handleMouseDown,
|
onMouseDown: this.handleMouseDown,
|
||||||
onKeyDown: this.handleButtonKeyDown,
|
onKeyDown: this.handleButtonKeyDown,
|
||||||
onKeyPress: this.handleKeyPress,
|
onKeyPress: this.handleKeyPress,
|
||||||
|
ref: this.setTargetRef,
|
||||||
}) : (
|
}) : (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={!open ? icon : 'close'}
|
icon={!open ? icon : 'close'}
|
||||||
|
iconComponent={!open ? iconComponent : CloseIcon}
|
||||||
title={title}
|
title={title}
|
||||||
active={open}
|
active={open}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -302,14 +307,14 @@ class Dropdown extends PureComponent {
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
onKeyDown={this.handleButtonKeyDown}
|
onKeyDown={this.handleButtonKeyDown}
|
||||||
onKeyPress={this.handleKeyPress}
|
onKeyPress={this.handleKeyPress}
|
||||||
|
ref={this.setTargetRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span ref={this.setTargetRef}>
|
|
||||||
{button}
|
{button}
|
||||||
</span>
|
|
||||||
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||||
{({ props, arrowProps, placement }) => (
|
{({ props, arrowProps, placement }) => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { openModal } from 'mastodon/actions/modal';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import InlineAccount from 'mastodon/components/inline_account';
|
import InlineAccount from 'mastodon/components/inline_account';
|
||||||
|
@ -66,7 +68,7 @@ class EditedTimestamp extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}>
|
<DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}>
|
||||||
<button className='dropdown-menu__text-button'>
|
<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>
|
</button>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,20 +1,52 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
interface Props extends React.HTMLAttributes<HTMLImageElement> {
|
import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
|
||||||
id: string;
|
|
||||||
className?: string;
|
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
|
||||||
fixedWidth?: boolean;
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IconProp = React.FC<SVGPropsWithTitle>;
|
||||||
|
|
||||||
|
interface Props extends React.SVGProps<SVGSVGElement> {
|
||||||
children?: never;
|
children?: never;
|
||||||
|
id: string;
|
||||||
|
icon: IconProp;
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon: React.FC<Props> = ({
|
export const Icon: React.FC<Props> = ({
|
||||||
id,
|
id,
|
||||||
|
icon: IconComponent,
|
||||||
className,
|
className,
|
||||||
fixedWidth,
|
title: titleProp,
|
||||||
...other
|
...other
|
||||||
}) => (
|
}) => {
|
||||||
<i
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })}
|
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}
|
{...other}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent, createRef } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { AnimatedNumber } from './animated_number';
|
import { AnimatedNumber } from './animated_number';
|
||||||
|
import type { IconProp } from './icon';
|
||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
title: string;
|
title: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
iconComponent: IconProp;
|
||||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||||
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
|
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
|
||||||
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||||
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
|
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||||
size: number;
|
|
||||||
active: boolean;
|
active: boolean;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
@ -33,8 +34,9 @@ interface States {
|
||||||
deactivate: boolean;
|
deactivate: boolean;
|
||||||
}
|
}
|
||||||
export class IconButton extends PureComponent<Props, States> {
|
export class IconButton extends PureComponent<Props, States> {
|
||||||
|
buttonRef = createRef<HTMLButtonElement>();
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
size: 18,
|
|
||||||
active: false,
|
active: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
animate: false,
|
animate: false,
|
||||||
|
@ -86,10 +88,6 @@ export class IconButton extends PureComponent<Props, States> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const style = {
|
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.style,
|
||||||
...(this.props.active ? this.props.activeStyle : {}),
|
...(this.props.active ? this.props.activeStyle : {}),
|
||||||
};
|
};
|
||||||
|
@ -100,6 +98,7 @@ export class IconButton extends PureComponent<Props, States> {
|
||||||
disabled,
|
disabled,
|
||||||
expanded,
|
expanded,
|
||||||
icon,
|
icon,
|
||||||
|
iconComponent,
|
||||||
inverted,
|
inverted,
|
||||||
overlay,
|
overlay,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
|
@ -122,13 +121,9 @@ export class IconButton extends PureComponent<Props, States> {
|
||||||
'icon-button--with-counter': typeof counter !== 'undefined',
|
'icon-button--with-counter': typeof counter !== 'undefined',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof counter !== 'undefined') {
|
|
||||||
style.width = 'auto';
|
|
||||||
}
|
|
||||||
|
|
||||||
let contents = (
|
let contents = (
|
||||||
<>
|
<>
|
||||||
<Icon id={icon} fixedWidth aria-hidden='true' />{' '}
|
<Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '}
|
||||||
{typeof counter !== 'undefined' && (
|
{typeof counter !== 'undefined' && (
|
||||||
<span className='icon-button__counter'>
|
<span className='icon-button__counter'>
|
||||||
<AnimatedNumber value={counter} />
|
<AnimatedNumber value={counter} />
|
||||||
|
@ -161,6 +156,7 @@ export class IconButton extends PureComponent<Props, States> {
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
data-id={data_id}
|
data-id={data_id}
|
||||||
|
ref={this.buttonRef}
|
||||||
>
|
>
|
||||||
{contents}
|
{contents}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
|
import type { IconProp } from './icon';
|
||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
|
|
||||||
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
|
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
|
icon: IconProp;
|
||||||
count: number;
|
count: number;
|
||||||
issueBadge: boolean;
|
issueBadge: boolean;
|
||||||
className: string;
|
className: string;
|
||||||
}
|
}
|
||||||
export const IconWithBadge: React.FC<Props> = ({
|
export const IconWithBadge: React.FC<Props> = ({
|
||||||
id,
|
id,
|
||||||
|
icon,
|
||||||
count,
|
count,
|
||||||
issueBadge,
|
issueBadge,
|
||||||
className,
|
className,
|
||||||
}) => (
|
}) => (
|
||||||
<i className='icon-with-badge'>
|
<i className='icon-with-badge'>
|
||||||
<Icon id={id} fixedWidth className={className} />
|
<Icon id={id} icon={icon} className={className} />
|
||||||
{count > 0 && (
|
{count > 0 && (
|
||||||
<i className='icon-with-badge__badge'>{formatNumber(count)}</i>
|
<i className='icon-with-badge__badge'>{formatNumber(count)}</i>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useIntl, defineMessages } from 'react-intl';
|
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';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -28,7 +30,7 @@ export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => {
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
aria-label={intl.formatMessage(messages.load_more)}
|
aria-label={intl.formatMessage(messages.load_more)}
|
||||||
>
|
>
|
||||||
<Icon id='ellipsis-h' />
|
<Icon id='ellipsis-h' icon={MoreHorizIcon} />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ import classNames from 'classnames';
|
||||||
import { is } from 'immutable';
|
import { is } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
|
@ -342,7 +343,7 @@ class MediaGallery extends PureComponent {
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
} else if (visible) {
|
} 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 {
|
} else {
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
<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 { 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 { removePictureInPicture } from 'mastodon/actions/picture_in_picture';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
|
@ -25,7 +27,7 @@ class PictureInPicturePlaceholder extends PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}>
|
<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' />
|
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 escapeTextContentForBrowser from 'escape-html';
|
||||||
import spring from 'react-motion/lib/spring';
|
import spring from 'react-motion/lib/spring';
|
||||||
|
|
||||||
|
@ -192,7 +193,7 @@ class Poll extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!!voted && <span className='poll__voted'>
|
{!!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>}
|
</span>}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,11 @@ import React from 'react';
|
||||||
|
|
||||||
import { Router as OriginalRouter } from 'react-router';
|
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 { createBrowserHistory } from 'history';
|
||||||
|
|
||||||
import { layoutFromWindow } from 'mastodon/is_mobile';
|
import { layoutFromWindow } from 'mastodon/is_mobile';
|
||||||
|
@ -20,39 +24,55 @@ const browserHistory = createBrowserHistory<
|
||||||
const originalPush = browserHistory.push.bind(browserHistory);
|
const originalPush = browserHistory.push.bind(browserHistory);
|
||||||
const originalReplace = browserHistory.replace.bind(browserHistory);
|
const originalReplace = browserHistory.replace.bind(browserHistory);
|
||||||
|
|
||||||
function extractRealPath(path: HistoryPath) {
|
function normalizePath(
|
||||||
if (typeof path === 'string') return path;
|
path: HistoryPath,
|
||||||
else return path.pathname;
|
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) => {
|
browserHistory.push = (path: HistoryPath, state?: MastodonLocationState) => {
|
||||||
state = state ?? {};
|
const location = normalizePath(path, state);
|
||||||
state.fromMastodon = true;
|
|
||||||
|
|
||||||
const realPath = extractRealPath(path);
|
location.state = location.state ?? {};
|
||||||
if (!realPath) return;
|
location.state.fromMastodon = true;
|
||||||
|
|
||||||
if (layoutFromWindow() === 'multi-column' && !realPath.startsWith('/deck')) {
|
originalPush(location);
|
||||||
originalPush(`/deck${realPath}`, state);
|
|
||||||
} else {
|
|
||||||
originalPush(path, state);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
browserHistory.replace = (path: HistoryPath, state?: MastodonLocationState) => {
|
browserHistory.replace = (path: HistoryPath, state?: MastodonLocationState) => {
|
||||||
|
const location = normalizePath(path, state);
|
||||||
|
|
||||||
|
if (!location.pathname) return;
|
||||||
|
|
||||||
if (browserHistory.location.state?.fromMastodon) {
|
if (browserHistory.location.state?.fromMastodon) {
|
||||||
state = state ?? {};
|
location.state = location.state ?? {};
|
||||||
state.fromMastodon = true;
|
location.state.fromMastodon = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const realPath = extractRealPath(path);
|
originalReplace(location);
|
||||||
if (!realPath) return;
|
|
||||||
|
|
||||||
if (layoutFromWindow() === 'multi-column' && !realPath.startsWith('/deck')) {
|
|
||||||
originalReplace(`/deck${realPath}`, state);
|
|
||||||
} else {
|
|
||||||
originalReplace(path, state);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Router: React.FC<PropsWithChildren> = ({ children }) => {
|
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 ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { HotKeys } from 'react-hotkeys';
|
||||||
|
|
||||||
import AttachmentList from 'mastodon/components/attachment_list';
|
import AttachmentList from 'mastodon/components/attachment_list';
|
||||||
|
@ -30,6 +38,7 @@ import { RelativeTimestamp } from './relative_timestamp';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
import StatusContent from './status_content';
|
import StatusContent from './status_content';
|
||||||
import StatusEmojiReactionsBar from './status_emoji_reactions_bar';
|
import StatusEmojiReactionsBar from './status_emoji_reactions_bar';
|
||||||
|
import { VisibilityIcon } from './visibility_icon';
|
||||||
|
|
||||||
const domParser = new DOMParser();
|
const domParser = new DOMParser();
|
||||||
|
|
||||||
|
@ -399,25 +408,12 @@ class Status extends ImmutablePureComponent {
|
||||||
const connectReply = nextInReplyToId && nextInReplyToId === status.get('id');
|
const connectReply = nextInReplyToId && nextInReplyToId === status.get('id');
|
||||||
const matchedFilters = status.get('matched_filters');
|
const matchedFilters = status.get('matched_filters');
|
||||||
|
|
||||||
const visibilityIconInfo = {
|
let visibilityName = status.get('limited_scope') || status.get('visibility_ex') || status.get('visibility');
|
||||||
'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')];
|
|
||||||
|
|
||||||
if (featured) {
|
if (featured) {
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__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' />
|
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -426,8 +422,8 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__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='retweet' icon={RepeatIcon} className='status__prepend-icon' /></div>
|
||||||
<div className='status__prepend-icon-wrapper'><Icon id={visibilityIcon.icon} 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> }} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -439,7 +435,7 @@ class Status extends ImmutablePureComponent {
|
||||||
} else if (status.get('visibility') === 'direct') {
|
} else if (status.get('visibility') === 'direct') {
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__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' />
|
<FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -448,7 +444,7 @@ class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
prepend = (
|
prepend = (
|
||||||
<div className='status__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> }} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -473,7 +469,7 @@ class Status extends ImmutablePureComponent {
|
||||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||||
<div onClick={this.handleClick} className='status__info'>
|
<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'>
|
<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>}
|
<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>
|
</a>
|
||||||
|
|
||||||
|
@ -608,7 +604,7 @@ class Status extends ImmutablePureComponent {
|
||||||
isCardMediaWithSensitive = status.get('spoiler_text').length > 0;
|
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;
|
let emojiReactionsBar = null;
|
||||||
if (!this.props.withoutEmojiReactions && status.get('emoji_reactions')) {
|
if (!this.props.withoutEmojiReactions && status.get('emoji_reactions')) {
|
||||||
|
@ -621,12 +617,12 @@ class Status extends ImmutablePureComponent {
|
||||||
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
|
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
|
||||||
const expanded = !status.get('hidden') || status.get('spoiler_text').length === 0;
|
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 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' title='Quote' /></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' title='Reference' /></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' title='Expiration' /></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 (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
|
@ -643,7 +639,7 @@ class Status extends ImmutablePureComponent {
|
||||||
{withReference}
|
{withReference}
|
||||||
{withExpiration}
|
{withExpiration}
|
||||||
{withLimited}
|
{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>}
|
<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>
|
</a>
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,17 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
@ -253,7 +264,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleOpenMentions = () => {
|
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 = () => {
|
handleEmbed = () => {
|
||||||
|
@ -413,12 +424,15 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyIcon;
|
let replyIcon;
|
||||||
|
let replyIconComponent;
|
||||||
let replyTitle;
|
let replyTitle;
|
||||||
if (status.get('in_reply_to_id', null) === null) {
|
if (status.get('in_reply_to_id', null) === null) {
|
||||||
replyIcon = 'reply';
|
replyIcon = 'reply';
|
||||||
|
replyIconComponent = ReplyIcon;
|
||||||
replyTitle = intl.formatMessage(messages.reply);
|
replyTitle = intl.formatMessage(messages.reply);
|
||||||
} else {
|
} else {
|
||||||
replyIcon = 'reply-all';
|
replyIcon = 'reply-all';
|
||||||
|
replyIconComponent = ReplyAllIcon;
|
||||||
replyTitle = intl.formatMessage(messages.replyAll);
|
replyTitle = intl.formatMessage(messages.replyAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +450,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterButton = this.props.onFilter && (
|
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';
|
const emojiReactionPolicy = account.getIn(['other_settings', 'emoji_reaction_policy']) || 'allow';
|
||||||
|
@ -446,34 +460,34 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
const outside = emojiReactionPolicy !== 'outside_only' || (relationship && (relationship.get('following') || relationship.get('followed_by')));
|
const outside = emojiReactionPolicy !== 'outside_only' || (relationship && (relationship.get('following') || relationship.get('followed_by')));
|
||||||
const denyFromAll = emojiReactionPolicy !== 'block' && emojiReactionPolicy !== 'block';
|
const denyFromAll = emojiReactionPolicy !== 'block' && emojiReactionPolicy !== 'block';
|
||||||
const emojiPickerButton = (
|
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)) && (
|
const emojiPickerDropdown = enableEmojiReaction && denyFromAll && (writtenByMe || (following && followed && mutual && outside)) && (
|
||||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={emojiPickerButton} />
|
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={emojiPickerButton} />
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='status__action-bar'>
|
<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='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' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
<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' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_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' onClick={this.handleBookmarkClick} />
|
<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}
|
{emojiPickerDropdown}
|
||||||
|
|
||||||
{filterButton}
|
{filterButton}
|
||||||
|
|
||||||
<div className='status__action-bar__dropdown'>
|
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
scrollKey={scrollKey}
|
scrollKey={scrollKey}
|
||||||
status={status}
|
status={status}
|
||||||
items={menu}
|
items={menu}
|
||||||
icon='ellipsis-h'
|
icon='ellipsis-h'
|
||||||
size={18}
|
iconComponent={MoreHorizIcon}
|
||||||
direction='right'
|
direction='right'
|
||||||
title={intl.formatMessage(messages.more)}
|
title={intl.formatMessage(messages.more)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { Link, withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
import PollContainer from 'mastodon/containers/poll_container';
|
import PollContainer from 'mastodon/containers/poll_container';
|
||||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||||
|
@ -262,7 +264,7 @@ class StatusContent extends PureComponent {
|
||||||
|
|
||||||
const readMoreButton = renderReadMore && (
|
const readMoreButton = renderReadMore && (
|
||||||
<button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'>
|
<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>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||||
|
|
||||||
import { Icon } from './icon';
|
import { Icon } from './icon';
|
||||||
|
|
||||||
const domParser = new DOMParser();
|
const domParser = new DOMParser();
|
||||||
|
@ -21,7 +23,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
export const VerifiedBadge: React.FC<Props> = ({ link }) => (
|
export const VerifiedBadge: React.FC<Props> = ({ link }) => (
|
||||||
<span className='verified-badge'>
|
<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 dangerouslySetInnerHTML={stripRelMe(link)} />
|
||||||
</span>
|
</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 ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
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 { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
@ -85,7 +88,7 @@ class Section extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<div className={classNames('about__section', { active: !collapsed })}>
|
<div className={classNames('about__section', { active: !collapsed })}>
|
||||||
<div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
|
<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>
|
</div>
|
||||||
|
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||||
|
@ -22,12 +25,12 @@ export default class FollowRequestNote extends ImmutablePureComponent {
|
||||||
|
|
||||||
<div className='follow-request-banner__action'>
|
<div className='follow-request-banner__action'>
|
||||||
<button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
|
<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' />
|
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
|
<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' />
|
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,12 @@ import { NavLink, withRouter } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { Avatar } from 'mastodon/components/avatar';
|
||||||
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
|
import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
|
@ -264,7 +270,7 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
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')) {
|
if (me !== account.get('id')) {
|
||||||
|
@ -286,7 +292,7 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.get('locked')) {
|
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) {
|
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>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -459,7 +465,7 @@ class Header extends ImmutablePureComponent {
|
||||||
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
|
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
|
||||||
|
|
||||||
<dd className='translate' title={pair.get('value_plain')}>
|
<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>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -5,6 +5,10 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
|
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
|
||||||
|
@ -69,7 +73,7 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
icon = (
|
icon = (
|
||||||
<span className='account-gallery__item__icons'>
|
<span className='account-gallery__item__icons'>
|
||||||
<Icon id='eye-slash' />
|
<Icon id='eye-slash' icon={VisibilityOffIcon} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -84,9 +88,9 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (attachment.get('type') === 'audio') {
|
if (attachment.get('type') === 'audio') {
|
||||||
label = <Icon id='music' />;
|
label = <Icon id='music' icon={AudiotrackIcon} />;
|
||||||
} else {
|
} else {
|
||||||
label = <Icon id='play' />;
|
label = <Icon id='play' icon={PlayArrowIcon} />;
|
||||||
}
|
}
|
||||||
} else if (attachment.get('type') === 'image') {
|
} else if (attachment.get('type') === 'image') {
|
||||||
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
|
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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import { removeFromAntennaAdder, addToAntennaAdder, removeExcludeFromAntennaAdder, addExcludeToAntennaAdder } from '../../../actions/antennas';
|
import { removeFromAntennaAdder, addToAntennaAdder, removeExcludeFromAntennaAdder, addExcludeToAntennaAdder } from '../../../actions/antennas';
|
||||||
|
@ -67,16 +71,16 @@ class Antenna extends ImmutablePureComponent {
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
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 {
|
} 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 (
|
return (
|
||||||
<div className='list'>
|
<div className='list'>
|
||||||
<div className='list__wrapper'>
|
<div className='list__wrapper'>
|
||||||
<div className='list__display-name'>
|
<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')}
|
{antenna.get('title')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,14 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { removeFromAntennaEditor, addToAntennaEditor, removeExcludeFromAntennaEditor, addExcludeToAntennaEditor } from '../../../actions/antennas';
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '../../../components/avatar';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '../../../components/display_name';
|
||||||
|
@ -58,9 +62,9 @@ class Account extends ImmutablePureComponent {
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
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 {
|
} 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 (
|
return (
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||||
|
|
||||||
import { changeAntennaEditorTitle, submitAntennaEditor } from '../../../actions/antennas';
|
import { changeAntennaEditorTitle, submitAntennaEditor } from '../../../actions/antennas';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ class AntennaForm extends PureComponent {
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
icon='check'
|
icon='check'
|
||||||
|
iconComponent={CheckIcon}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,6 +7,9 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import { fetchAntennaSuggestions, clearAntennaSuggestions, changeAntennaSuggestions } from '../../../actions/antennas';
|
import { fetchAntennaSuggestions, clearAntennaSuggestions, changeAntennaSuggestions } from '../../../actions/antennas';
|
||||||
|
@ -69,8 +72,8 @@ class Search extends PureComponent {
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||||
<Icon id='search' className={classNames({ active: !hasValue })} />
|
<Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} />
|
||||||
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
<Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,12 +3,17 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
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 Select, { NonceProvider } from 'react-select';
|
||||||
import Toggle from 'react-toggle';
|
import Toggle from 'react-toggle';
|
||||||
|
|
||||||
|
@ -41,6 +46,7 @@ import ColumnHeader from 'mastodon/components/column_header';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
import RadioPanel from './components/radio_panel';
|
import RadioPanel from './components/radio_panel';
|
||||||
import TextList from './components/text_list';
|
import TextList from './components/text_list';
|
||||||
|
@ -74,10 +80,6 @@ const mapStateToProps = (state, props) => ({
|
||||||
|
|
||||||
class AntennaSetting extends PureComponent {
|
class AntennaSetting extends PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
@ -89,6 +91,7 @@ class AntennaSetting extends PureComponent {
|
||||||
keywords: ImmutablePropTypes.map,
|
keywords: ImmutablePropTypes.map,
|
||||||
tags: ImmutablePropTypes.map,
|
tags: ImmutablePropTypes.map,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
...WithRouterPropTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -109,7 +112,7 @@ class AntennaSetting extends PureComponent {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('ANTENNA', { id: this.props.params.id }));
|
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) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
this.context.router.history.push('/antennasw');
|
this.props.history.push('/antennasw');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -192,7 +195,7 @@ class AntennaSetting extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTimelineClick = () => {
|
handleTimelineClick = () => {
|
||||||
this.context.router.history.push(`/antennast/${this.props.params.id}`);
|
this.props.history.push(`/antennast/${this.props.params.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
onStlToggle = ({ target }) => {
|
onStlToggle = ({ target }) => {
|
||||||
|
@ -376,6 +379,7 @@ class AntennaSetting extends PureComponent {
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='wifi'
|
icon='wifi'
|
||||||
|
iconComponent={AntennaIcon}
|
||||||
title={title}
|
title={title}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onMove={this.handleMove}
|
onMove={this.handleMove}
|
||||||
|
@ -385,15 +389,15 @@ class AntennaSetting extends PureComponent {
|
||||||
>
|
>
|
||||||
<div className='column-settings__row column-header__links'>
|
<div className='column-settings__row column-header__links'>
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditAntennaClick}>
|
<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>
|
||||||
|
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
<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>
|
||||||
|
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleTimelineClick}>
|
<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>
|
</button>
|
||||||
</div>
|
</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 { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
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 { fetchAntenna, deleteAntenna } from 'mastodon/actions/antennas';
|
||||||
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
@ -19,6 +25,7 @@ import { Icon } from 'mastodon/components/icon';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
|
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
|
||||||
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
deleteMessage: { id: 'confirmations.delete_antenna.message', defaultMessage: 'Are you sure you want to permanently delete this antenna?' },
|
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 {
|
class AntennaTimeline extends PureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
@ -44,6 +47,7 @@ class AntennaTimeline extends PureComponent {
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
antenna: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
antenna: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
...WithRouterPropTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
|
@ -53,7 +57,7 @@ class AntennaTimeline extends PureComponent {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('ANTENNA_TIMELINE', { id: this.props.params.id }));
|
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 = () => {
|
handleEditClick = () => {
|
||||||
this.context.router.history.push(`/antennasw/${this.props.params.id}`);
|
this.props.history.push(`/antennasw/${this.props.params.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDeleteClick = () => {
|
handleDeleteClick = () => {
|
||||||
|
@ -128,7 +132,7 @@ class AntennaTimeline extends PureComponent {
|
||||||
if (columnId) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} 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}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='wifi'
|
icon='wifi'
|
||||||
|
iconComponent={AntennaIcon}
|
||||||
active={hasUnread}
|
active={hasUnread}
|
||||||
title={title}
|
title={title}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
|
@ -169,11 +174,11 @@ class AntennaTimeline extends PureComponent {
|
||||||
>
|
>
|
||||||
<div className='column-settings__row column-header__links'>
|
<div className='column-settings__row column-header__links'>
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
<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>
|
||||||
|
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</ColumnHeader>
|
</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 { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
import { ReactComponent as AntennaIcon } from '@material-symbols/svg-600/outlined/wifi.svg';
|
||||||
|
|
||||||
import { fetchAntennas } from 'mastodon/actions/antennas';
|
import { fetchAntennas } from 'mastodon/actions/antennas';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
|
@ -67,7 +69,7 @@ class Antennas extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
<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 />
|
<NewAntennaForm />
|
||||||
|
|
||||||
|
@ -78,7 +80,7 @@ class Antennas extends ImmutablePureComponent {
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{antennas.map(antenna => (
|
{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} />
|
badge={antenna.get('insert_feeds') ? intl.formatMessage(antenna.get('list') ? messages.insert_list : messages.insert_home) : undefined} />
|
||||||
))}
|
))}
|
||||||
</ScrollableList>
|
</ScrollableList>
|
||||||
|
|
|
@ -7,6 +7,12 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import { is } from 'immutable';
|
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 { throttle, debounce } from 'lodash';
|
||||||
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
@ -554,8 +560,8 @@ class Audio extends PureComponent {
|
||||||
<div className='video-player__controls active'>
|
<div className='video-player__controls active'>
|
||||||
<div className='video-player__buttons-bar'>
|
<div className='video-player__buttons-bar'>
|
||||||
<div className='video-player__buttons left'>
|
<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(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'} 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'} icon={muted ? VolumeOffIcon : VolumeUpIcon} /></button>
|
||||||
|
|
||||||
<div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}>
|
<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() }} />
|
<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>
|
||||||
|
|
||||||
<div className='video-player__buttons right'>
|
<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>
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
|
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." />;
|
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} icon='ban' iconComponent={BlockIcon} heading={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnBackButtonSlim />
|
<ColumnBackButtonSlim />
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='blocks'
|
scrollKey='blocks'
|
||||||
|
|
|
@ -9,6 +9,9 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
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 { fetchBookmarkCategories } from 'mastodon/actions/bookmark_categories';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
|
@ -66,11 +69,11 @@ class BookmarkCategories extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
<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 />
|
<NewListForm />
|
||||||
|
|
||||||
<ColumnLink to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.allBookmarks)} />
|
<ColumnLink to='/bookmarks' icon='bookmark' iconComponent={BookmarkIcon} text={intl.formatMessage(messages.allBookmarks)} />
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='bookmark_categories'
|
scrollKey='bookmark_categories'
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
|
@ -78,7 +81,7 @@ class BookmarkCategories extends ImmutablePureComponent {
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{categories.map(category =>
|
{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>
|
</ScrollableList>
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,15 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import { removeFromBookmarkCategoryAdder, addToBookmarkCategoryAdder } from '../../../actions/bookmark_categories';
|
import { removeFromBookmarkCategoryAdder, addToBookmarkCategoryAdder } from '../../../actions/bookmark_categories';
|
||||||
|
@ -46,16 +51,16 @@ class BookmarkCategory extends ImmutablePureComponent {
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
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 {
|
} 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 (
|
return (
|
||||||
<div className='list'>
|
<div className='list'>
|
||||||
<div className='list__wrapper'>
|
<div className='list__wrapper'>
|
||||||
<div className='list__display-name'>
|
<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')}
|
{bookmarkCategory.get('title')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { changeBookmarkCategoryEditorTitle, submitBookmarkCategoryEditor } from '../../../actions/bookmark_categories';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ class EditBookmarkCategoryForm extends PureComponent {
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
icon='check'
|
icon='check'
|
||||||
|
iconComponent={CheckIcon}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,12 +2,18 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { debounce } from 'lodash';
|
||||||
|
|
||||||
import { deleteBookmarkCategory, expandBookmarkCategoryStatuses, fetchBookmarkCategory, fetchBookmarkCategoryStatuses , setupBookmarkCategoryEditor } from 'mastodon/actions/bookmark_categories';
|
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 StatusList from 'mastodon/components/status_list';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
import { getBookmarkCategoryStatusList } from 'mastodon/selectors';
|
import { getBookmarkCategoryStatusList } from 'mastodon/selectors';
|
||||||
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
import EditBookmarkCategoryForm from './components/edit_bookmark_category_form';
|
import EditBookmarkCategoryForm from './components/edit_bookmark_category_form';
|
||||||
|
|
||||||
|
@ -40,10 +47,6 @@ const mapStateToProps = (state, { params }) => ({
|
||||||
|
|
||||||
class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
@ -55,6 +58,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
|
...WithRouterPropTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
UNSAFE_componentWillMount () {
|
UNSAFE_componentWillMount () {
|
||||||
|
@ -69,7 +73,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('BOOKMARKS_EX', { id: this.props.params.id }));
|
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) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} 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)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='bookmark'
|
icon='bookmark'
|
||||||
|
iconComponent={BookmarkIcon}
|
||||||
title={bookmarkCategory.get('title')}
|
title={bookmarkCategory.get('title')}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onMove={this.handleMove}
|
onMove={this.handleMove}
|
||||||
|
@ -153,11 +158,11 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
|
||||||
>
|
>
|
||||||
<div className='column-settings__row column-header__links'>
|
<div className='column-settings__row column-header__links'>
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
<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>
|
||||||
|
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
<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>
|
</button>
|
||||||
|
|
||||||
{editor}
|
{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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks';
|
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks';
|
||||||
|
@ -79,7 +80,8 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='bookmark'
|
icon='bookmarks'
|
||||||
|
iconComponent={BookmarksIcon}
|
||||||
title={intl.formatMessage(messages.heading)}
|
title={intl.formatMessage(messages.heading)}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onMove={this.handleMove}
|
onMove={this.handleMove}
|
||||||
|
|
|
@ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import { removeFromCircleAdder, addToCircleAdder } from '../../../actions/circles';
|
import { removeFromCircleAdder, addToCircleAdder } from '../../../actions/circles';
|
||||||
|
@ -46,16 +50,16 @@ class Circle extends ImmutablePureComponent {
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
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 {
|
} 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 (
|
return (
|
||||||
<div className='list'>
|
<div className='list'>
|
||||||
<div className='list__wrapper'>
|
<div className='list__wrapper'>
|
||||||
<div className='list__display-name'>
|
<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')}
|
{circle.get('title')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,14 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { removeFromCircleEditor, addToCircleEditor } from '../../../actions/circles';
|
||||||
import { Avatar } from '../../../components/avatar';
|
import { Avatar } from '../../../components/avatar';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '../../../components/display_name';
|
||||||
|
@ -53,9 +57,9 @@ class Account extends ImmutablePureComponent {
|
||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
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 {
|
} 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 (
|
return (
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
|
||||||
|
|
||||||
import { changeCircleEditorTitle, submitCircleEditor } from '../../../actions/circles';
|
import { changeCircleEditorTitle, submitCircleEditor } from '../../../actions/circles';
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ class CircleForm extends PureComponent {
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
icon='check'
|
icon='check'
|
||||||
|
iconComponent={CheckIcon}
|
||||||
title={title}
|
title={title}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,6 +7,9 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
|
|
||||||
import { fetchCircleSuggestions, clearCircleSuggestions, changeCircleSuggestions } from '../../../actions/circles';
|
import { fetchCircleSuggestions, clearCircleSuggestions, changeCircleSuggestions } from '../../../actions/circles';
|
||||||
|
@ -69,8 +72,8 @@ class Search extends PureComponent {
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||||
<Icon id='search' className={classNames({ active: !hasValue })} />
|
<Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} />
|
||||||
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
<Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,12 +2,18 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
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 { debounce } from 'lodash';
|
||||||
|
|
||||||
import { deleteCircle, expandCircleStatuses, fetchCircle, fetchCircleStatuses } from 'mastodon/actions/circles';
|
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 StatusList from 'mastodon/components/status_list';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
import { getCircleStatusList } from 'mastodon/selectors';
|
import { getCircleStatusList } from 'mastodon/selectors';
|
||||||
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -38,10 +45,6 @@ const mapStateToProps = (state, { params }) => ({
|
||||||
|
|
||||||
class CircleStatuses extends ImmutablePureComponent {
|
class CircleStatuses extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
|
@ -52,6 +55,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
|
...WithRouterPropTypes,
|
||||||
};
|
};
|
||||||
|
|
||||||
UNSAFE_componentWillMount () {
|
UNSAFE_componentWillMount () {
|
||||||
|
@ -66,7 +70,7 @@ class CircleStatuses extends ImmutablePureComponent {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('CIRCLE_STATUSES', { id: this.props.params.id }));
|
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) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} 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)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='user-circle'
|
icon='user-circle'
|
||||||
|
iconComponent={CircleIcon}
|
||||||
title={circle.get('title')}
|
title={circle.get('title')}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onMove={this.handleMove}
|
onMove={this.handleMove}
|
||||||
|
@ -149,11 +154,11 @@ class CircleStatuses extends ImmutablePureComponent {
|
||||||
>
|
>
|
||||||
<div className='column-settings__row column-header__links'>
|
<div className='column-settings__row column-header__links'>
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
|
<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>
|
||||||
|
|
||||||
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ColumnHeader>
|
</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 { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
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 { fetchCircles, deleteCircle } from 'mastodon/actions/circles';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
|
@ -94,7 +96,7 @@ class Circles extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
|
<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 />
|
<NewCircleForm />
|
||||||
|
|
||||||
|
@ -105,7 +107,7 @@ class Circles extends ImmutablePureComponent {
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{circles.map(circle =>
|
{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>
|
</ScrollableList>
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
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 { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { domain } from 'mastodon/initial_state';
|
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)}>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnHeader
|
<ColumnHeader
|
||||||
icon='users'
|
icon='users'
|
||||||
|
iconComponent={PeopleIcon}
|
||||||
active={hasUnread}
|
active={hasUnread}
|
||||||
title={intl.formatMessage(messages.title)}
|
title={intl.formatMessage(messages.title)}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
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';
|
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -21,7 +23,6 @@ const messages = defineMessages({
|
||||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||||
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
||||||
antennas: { id: 'navigation_bar.antennas', defaultMessage: 'Antennas' },
|
|
||||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||||
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
|
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.blocks), to: '/blocks' });
|
||||||
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_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.filters), href: '/filters' });
|
||||||
menu.push({ text: intl.formatMessage(messages.antennas), href: '/antennas' });
|
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
|
menu.push({ text: intl.formatMessage(messages.logout), action: this.handleLogout });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compose__action-bar'>
|
<div className='compose__action-bar'>
|
||||||
<div className='compose__action-bar-dropdown'>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
|
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
|
||||||
import { length } from 'stringz';
|
import { length } from 'stringz';
|
||||||
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
@ -242,7 +243,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
if (this.props.isEditing) {
|
if (this.props.isEditing) {
|
||||||
publishText = intl.formatMessage(messages.saveChanges);
|
publishText = intl.formatMessage(messages.saveChanges);
|
||||||
} else if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
} 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 {
|
} 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);
|
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 classNames from 'classnames';
|
||||||
|
|
||||||
|
import { ReactComponent as TimerIcon } from '@material-symbols/svg-600/outlined/timer.svg';
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import Overlay from 'react-overlays/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
|
@ -237,6 +238,7 @@ class ExpirationDropdown extends PureComponent {
|
||||||
<IconButton
|
<IconButton
|
||||||
className='expiration-dropdown__value-icon'
|
className='expiration-dropdown__value-icon'
|
||||||
icon='clock-o'
|
icon='clock-o'
|
||||||
|
iconComponent={TimerIcon}
|
||||||
title={intl.formatMessage(messages.add_expiration)}
|
title={intl.formatMessage(messages.add_expiration)}
|
||||||
size={18}
|
size={18}
|
||||||
expanded={open}
|
expanded={open}
|
||||||
|
|
|
@ -3,8 +3,11 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
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';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' },
|
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' },
|
||||||
remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove 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'>
|
<div className='compose-form__poll-button'>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon='tasks'
|
icon='tasks'
|
||||||
|
iconComponent={InsertChartIcon}
|
||||||
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
|
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import classNames from 'classnames';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
@ -108,7 +111,7 @@ class OptionIntl extends PureComponent {
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className='poll__cancel'>
|
<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>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@ -164,7 +167,7 @@ class PollForm extends ImmutablePureComponent {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className='poll__footer'>
|
<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 */}
|
{/* eslint-disable-next-line jsx-a11y/no-onchange */}
|
||||||
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
<select value={expiresIn} onChange={this.handleSelectDuration}>
|
||||||
|
|
|
@ -5,9 +5,19 @@ import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
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 { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import Overlay from 'react-overlays/Overlay';
|
import Overlay from 'react-overlays/Overlay';
|
||||||
|
|
||||||
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { enableLoginPrivacy, enableLocalPrivacy } from 'mastodon/initial_state';
|
import { enableLoginPrivacy, enableLocalPrivacy } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
@ -132,7 +142,7 @@ class PrivacyDropdownMenu extends PureComponent {
|
||||||
{items.map(item => (
|
{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 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'>
|
<div className='privacy-dropdown__option__icon'>
|
||||||
<Icon id={item.icon} fixedWidth />
|
<Icon id={item.icon} icon={item.iconComponent} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='privacy-dropdown__option__content'>
|
<div className='privacy-dropdown__option__content'>
|
||||||
|
@ -231,14 +241,13 @@ class PrivacyDropdown extends PureComponent {
|
||||||
const { intl: { formatMessage } } = this.props;
|
const { intl: { formatMessage } } = this.props;
|
||||||
|
|
||||||
this.options = [
|
this.options = [
|
||||||
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
{ icon: 'globe', iconComponent: PublicIcon, 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: 'cloud', iconComponent: PublicUnlistedIcon, 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: 'key', iconComponent: LoginIcon, 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: 'unlock', iconComponent: LockOpenIcon, 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: 'lock', iconComponent: LockIcon, 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: 'exchange', iconComponent: MutualIcon, 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: 'user-circle', iconComponent: CircleIcon, 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) },
|
|
||||||
];
|
];
|
||||||
this.selectableOptions = [...this.options];
|
this.selectableOptions = [...this.options];
|
||||||
|
|
||||||
|
@ -250,8 +259,10 @@ class PrivacyDropdown extends PureComponent {
|
||||||
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'public_unlisted');
|
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'public_unlisted');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.noDirect) {
|
if (!this.props.noDirect) {
|
||||||
this.selectableOptions = this.selectableOptions.filter((opt) => opt.value !== 'direct');
|
this.options.push(
|
||||||
|
{ icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,11 +285,11 @@ class PrivacyDropdown extends PureComponent {
|
||||||
const valueOption = this.options.find(item => item.value === value) || this.options[0];
|
const valueOption = this.options.find(item => item.value === value) || this.options[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}>
|
<div ref={this.setTargetRef} 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
|
<IconButton
|
||||||
className='privacy-dropdown__value-icon'
|
className='privacy-dropdown__value-icon'
|
||||||
icon={valueOption.icon}
|
icon={valueOption.icon}
|
||||||
|
iconComponent={valueOption.iconComponent}
|
||||||
title={intl.formatMessage(messages.change_privacy)}
|
title={intl.formatMessage(messages.change_privacy)}
|
||||||
size={18}
|
size={18}
|
||||||
expanded={open}
|
expanded={open}
|
||||||
|
@ -290,9 +301,8 @@ class PrivacyDropdown extends PureComponent {
|
||||||
style={{ height: null, lineHeight: '27px' }}
|
style={{ height: null, lineHeight: '27px' }}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<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 }) => (
|
{({ props, placement }) => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
|
<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 ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 AttachmentList from 'mastodon/components/attachment_list';
|
||||||
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
|
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router';
|
||||||
|
|
||||||
|
@ -48,7 +50,7 @@ class ReplyIndicator extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='reply-indicator'>
|
<div className='reply-indicator'>
|
||||||
<div className='reply-indicator__header'>
|
<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'>
|
<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>
|
<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 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 { Icon } from 'mastodon/components/icon';
|
||||||
import { domain, searchEnabled } from 'mastodon/initial_state';
|
import { domain, searchEnabled } from 'mastodon/initial_state';
|
||||||
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
|
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}>
|
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
|
||||||
<Icon id='search' className={hasValue ? '' : 'active'} />
|
<Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} />
|
||||||
<Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
<Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='search__popout'>
|
<div className='search__popout'>
|
||||||
|
@ -348,7 +352,7 @@ class Search extends PureComponent {
|
||||||
{recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => (
|
{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 })}>
|
<button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
|
||||||
<span>{label}</span>
|
<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>
|
</button>
|
||||||
)) : (
|
)) : (
|
||||||
<div className='search__popout__menu__message'>
|
<div className='search__popout__menu__message'>
|
||||||
|
|
|
@ -5,6 +5,11 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
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 { Icon } from 'mastodon/components/icon';
|
||||||
import { LoadMore } from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
import { SearchSection } from 'mastodon/features/explore/components/search_section';
|
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) {
|
if (results.get('accounts') && results.get('accounts').size > 0) {
|
||||||
accounts = (
|
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} />)}
|
{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} />}
|
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
|
||||||
</SearchSection>
|
</SearchSection>
|
||||||
|
@ -53,7 +58,7 @@ class SearchResults extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (results.get('hashtags') && results.get('hashtags').size > 0) {
|
if (results.get('hashtags') && results.get('hashtags').size > 0) {
|
||||||
hashtags = (
|
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} />)}
|
{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} />}
|
{(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
|
||||||
</SearchSection>
|
</SearchSection>
|
||||||
|
@ -62,7 +67,7 @@ class SearchResults extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (results.get('statuses') && results.get('statuses').size > 0) {
|
if (results.get('statuses') && results.get('statuses').size > 0) {
|
||||||
statuses = (
|
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} />)}
|
{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} />}
|
{(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
|
||||||
</SearchSection>
|
</SearchSection>
|
||||||
|
@ -73,7 +78,7 @@ class SearchResults extends ImmutablePureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='search-results'>
|
<div className='search-results'>
|
||||||
<div className='search-results__header'>
|
<div className='search-results__header'>
|
||||||
<Icon id='search' fixedWidth />
|
<Icon id='search' icon={SearchIcon} />
|
||||||
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
|
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
|
||||||
</div>
|
</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