mirror of
https://github.com/funamitech/mastodon
synced 2024-11-23 22:57:05 +09:00
Merge branch 'main' of https://github.com/glitch-soc/mastodon
This commit is contained in:
commit
28a07d2e78
@ -1,20 +1,15 @@
|
|||||||
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
|
||||||
FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye
|
FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
|
||||||
|
|
||||||
# Install Rails
|
# Install node version from .nvmrc
|
||||||
# RUN gem install rails webdrivers
|
WORKDIR /app
|
||||||
|
COPY .nvmrc .
|
||||||
|
RUN /bin/bash --login -i -c "nvm install"
|
||||||
|
|
||||||
ARG NODE_VERSION="20"
|
# Install additional OS packages
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
|
RUN apt-get update && \
|
||||||
|
export DEBIAN_FRONTEND=noninteractive && \
|
||||||
|
apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
|
||||||
|
|
||||||
# [Optional] Uncomment this section to install additional OS packages.
|
# Move welcome message to where VS Code expects it
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
COPY .devcontainer/welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
||||||
&& apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install additional gems.
|
|
||||||
RUN gem install foreman
|
|
||||||
|
|
||||||
# [Optional] Uncomment this line to install global node packages.
|
|
||||||
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && corepack enable" 2>&1
|
|
||||||
|
|
||||||
COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Mastodon on GitHub Codespaces",
|
"name": "Mastodon on GitHub Codespaces",
|
||||||
"dockerComposeFile": "../docker-compose.yml",
|
"dockerComposeFile": "../compose.yaml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
@ -23,6 +23,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"remoteUser": "root",
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent"
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
@ -37,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
"postCreateCommand": "bin/setup",
|
||||||
"waitFor": "postCreateCommand",
|
"waitFor": "postCreateCommand",
|
||||||
|
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
|
working_dir: /workspaces/mastodon/
|
||||||
build:
|
build:
|
||||||
context: .
|
context: ..
|
||||||
dockerfile: Dockerfile
|
dockerfile: .devcontainer/Dockerfile
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/workspaces:cached
|
- ..:/workspaces/mastodon:cached
|
||||||
environment:
|
environment:
|
||||||
RAILS_ENV: development
|
RAILS_ENV: development
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Mastodon on local machine",
|
"name": "Mastodon on local machine",
|
||||||
"dockerComposeFile": "docker-compose.yml",
|
"dockerComposeFile": "compose.yaml",
|
||||||
"service": "app",
|
"service": "app",
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|
||||||
@ -23,12 +23,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"remoteUser": "root",
|
||||||
|
|
||||||
"otherPortsAttributes": {
|
"otherPortsAttributes": {
|
||||||
"onAutoForward": "silent"
|
"onAutoForward": "silent"
|
||||||
},
|
},
|
||||||
|
|
||||||
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||||
"postCreateCommand": ".devcontainer/post-create.sh",
|
"postCreateCommand": "bin/setup",
|
||||||
"waitFor": "postCreateCommand",
|
"waitFor": "postCreateCommand",
|
||||||
|
|
||||||
"customizations": {
|
"customizations": {
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e # Fail the whole script on first error
|
|
||||||
|
|
||||||
# Fetch Ruby gem dependencies
|
|
||||||
bundle config path 'vendor/bundle'
|
|
||||||
bundle config with 'development test'
|
|
||||||
bundle install
|
|
||||||
|
|
||||||
# Make Gemfile.lock pristine again
|
|
||||||
git checkout -- Gemfile.lock
|
|
||||||
|
|
||||||
# Fetch Javascript dependencies
|
|
||||||
corepack prepare
|
|
||||||
yarn install --immutable
|
|
||||||
|
|
||||||
# [re]create, migrate, and seed the test database
|
|
||||||
RAILS_ENV=test ./bin/rails db:setup
|
|
||||||
|
|
||||||
# [re]create, migrate, and seed the development database
|
|
||||||
RAILS_ENV=development ./bin/rails db:setup
|
|
||||||
|
|
||||||
# Precompile assets for development
|
|
||||||
RAILS_ENV=development ./bin/rails assets:precompile
|
|
||||||
|
|
||||||
# Precompile assets for test
|
|
||||||
RAILS_ENV=test ./bin/rails assets:precompile
|
|
@ -1,8 +1,7 @@
|
|||||||
👋 Welcome to "Mastodon" in GitHub Codespaces!
|
👋 Welcome to your Mastodon Dev Container!
|
||||||
|
|
||||||
🛠️ Your environment is fully setup with all the required software.
|
🛠️ Your environment is fully setup with all the required software.
|
||||||
|
|
||||||
🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
|
💥 Run `bin/dev` to start the application processes.
|
||||||
|
|
||||||
📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.
|
|
||||||
|
|
||||||
|
🥼 Run `RAILS_ENV=test bin/rails assets:precompile && RAILS_ENV=test bin/rspec` to run the test suite.
|
||||||
|
@ -4,7 +4,8 @@ NODE_ENV=production
|
|||||||
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||||
LOCAL_HTTPS=true
|
LOCAL_HTTPS=true
|
||||||
|
|
||||||
# Required by ActiveRecord encryption feature
|
# Secret values required by ActiveRecord encryption feature
|
||||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
|
# Use `bin/rails db:encryption:init` to generate fresh secrets
|
||||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
|
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=test_determinist_key_DO_NOT_USE_IN_PRODUCTION
|
||||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
|
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=test_salt_DO_NOT_USE_IN_PRODUCTION
|
||||||
|
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=test_primary_key_DO_NOT_USE_IN_PRODUCTION
|
||||||
|
2
.github/actions/setup-ruby/action.yml
vendored
2
.github/actions/setup-ruby/action.yml
vendored
@ -14,7 +14,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev ${{ inputs.additional-system-dependencies }}
|
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||||
|
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
|
4
.github/codecov.yml
vendored
4
.github/codecov.yml
vendored
@ -3,9 +3,9 @@ coverage:
|
|||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
# Github status check is not blocking
|
# GitHub status check is not blocking
|
||||||
informational: true
|
informational: true
|
||||||
patch:
|
patch:
|
||||||
default:
|
default:
|
||||||
# Github status check is not blocking
|
# GitHub status check is not blocking
|
||||||
informational: true
|
informational: true
|
||||||
|
3
.github/renovate.json5
vendored
3
.github/renovate.json5
vendored
@ -2,6 +2,7 @@
|
|||||||
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||||
extends: [
|
extends: [
|
||||||
'config:recommended',
|
'config:recommended',
|
||||||
|
'customManagers:dockerfileVersions',
|
||||||
':labels(dependencies)',
|
':labels(dependencies)',
|
||||||
':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.
|
||||||
@ -59,7 +60,7 @@
|
|||||||
dependencyDashboardApproval: true,
|
dependencyDashboardApproval: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Update Github Actions and Docker images weekly
|
// Update GitHub Actions and Docker images weekly
|
||||||
matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
|
matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
|
||||||
extends: ['schedule:weekly'],
|
extends: ['schedule:weekly'],
|
||||||
},
|
},
|
||||||
|
93
.github/workflows/test-ruby.yml
vendored
93
.github/workflows/test-ruby.yml
vendored
@ -133,7 +133,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick libpam-dev
|
additional-system-dependencies: ffmpeg 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'
|
||||||
@ -148,6 +148,93 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
test-libvips:
|
||||||
|
name: Libvips tests
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:14-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
options: >-
|
||||||
|
--health-cmd "redis-cli ping"
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
|
||||||
|
env:
|
||||||
|
DB_HOST: localhost
|
||||||
|
DB_USER: postgres
|
||||||
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||||
|
RAILS_ENV: test
|
||||||
|
ALLOW_NOPAM: true
|
||||||
|
PAM_ENABLED: true
|
||||||
|
PAM_DEFAULT_SERVICE: pam_test
|
||||||
|
PAM_CONTROLLED_SERVICE: pam_test_controlled
|
||||||
|
OIDC_ENABLED: true
|
||||||
|
OIDC_SCOPE: read
|
||||||
|
SAML_ENABLED: true
|
||||||
|
CAS_ENABLED: true
|
||||||
|
BUNDLE_WITH: 'pam_authentication test'
|
||||||
|
GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
|
||||||
|
MASTODON_USE_LIBVIPS: true
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
ruby-version:
|
||||||
|
- '3.1'
|
||||||
|
- '3.2'
|
||||||
|
- '.ruby-version'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: './'
|
||||||
|
name: ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Expand archived asset artifacts
|
||||||
|
run: |
|
||||||
|
tar xvzf artifacts.tar.gz
|
||||||
|
|
||||||
|
- name: Set up Ruby environment
|
||||||
|
uses: ./.github/actions/setup-ruby
|
||||||
|
with:
|
||||||
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
|
additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
|
||||||
|
|
||||||
|
- name: Load database schema
|
||||||
|
run: './bin/rails db:create db:schema:load db:seed'
|
||||||
|
|
||||||
|
- run: bin/rspec --tag paperclip_processing
|
||||||
|
|
||||||
|
- name: Upload coverage reports to Codecov
|
||||||
|
if: matrix.ruby-version == '.ruby-version'
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: coverage/lcov/mastodon.lcov
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
test-e2e:
|
test-e2e:
|
||||||
name: End to End testing
|
name: End to End testing
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -209,7 +296,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick
|
additional-system-dependencies: ffmpeg
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
@ -329,7 +416,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version}}
|
ruby-version: ${{ matrix.ruby-version}}
|
||||||
additional-system-dependencies: ffmpeg imagemagick
|
additional-system-dependencies: ffmpeg
|
||||||
|
|
||||||
- name: Set up Javascript environment
|
- name: Set up Javascript environment
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
19
.nanoignore
19
.nanoignore
@ -1,19 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
|
|
||||||
.bundle/
|
|
||||||
.cache/
|
|
||||||
config/deploy/*
|
|
||||||
coverage
|
|
||||||
docs/
|
|
||||||
.env
|
|
||||||
log/*.log
|
|
||||||
neo4j/
|
|
||||||
node_modules/
|
|
||||||
public/assets/
|
|
||||||
public/system/
|
|
||||||
spec/
|
|
||||||
tmp/
|
|
||||||
.vagrant/
|
|
||||||
vendor/bundle/
|
|
@ -211,6 +211,11 @@ Style/PercentLiteralDelimiters:
|
|||||||
Style/RedundantBegin:
|
Style/RedundantBegin:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# Reason: Prevailing style choice
|
||||||
|
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
|
||||||
|
Style/RedundantFetchBlock:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
# Reason: Overridden to reduce implicit StandardError rescues
|
# Reason: Overridden to reduce implicit StandardError rescues
|
||||||
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
|
# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
|
||||||
Style/RescueStandardError:
|
Style/RescueStandardError:
|
||||||
|
@ -169,16 +169,6 @@ Style/RedundantConstantBase:
|
|||||||
- 'config/environments/production.rb'
|
- 'config/environments/production.rb'
|
||||||
- 'config/initializers/sidekiq.rb'
|
- 'config/initializers/sidekiq.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
# Configuration parameters: SafeForConstants.
|
|
||||||
Style/RedundantFetchBlock:
|
|
||||||
Exclude:
|
|
||||||
- 'config/initializers/1_hosts.rb'
|
|
||||||
- 'config/initializers/chewy.rb'
|
|
||||||
- 'config/initializers/devise.rb'
|
|
||||||
- 'config/initializers/paperclip.rb'
|
|
||||||
- 'config/puma.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
||||||
# AllowedMethods: present?, blank?, presence, try, try!
|
# AllowedMethods: present?, blank?, presence, try, try!
|
||||||
@ -186,12 +176,6 @@ Style/SafeNavigation:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- 'app/models/concerns/account/finder_concern.rb'
|
- 'app/models/concerns/account/finder_concern.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
# Configuration parameters: Mode.
|
|
||||||
Style/StringConcatenation:
|
|
||||||
Exclude:
|
|
||||||
- 'config/initializers/paperclip.rb'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: WordRegex.
|
# Configuration parameters: WordRegex.
|
||||||
# SupportedStyles: percent, brackets
|
# SupportedStyles: percent, brackets
|
||||||
|
@ -1 +1 @@
|
|||||||
3.3.1
|
3.3.2
|
||||||
|
22
.simplecov
22
.simplecov
@ -1,22 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
if ENV['CI']
|
|
||||||
require 'simplecov-lcov'
|
|
||||||
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
|
|
||||||
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
|
|
||||||
else
|
|
||||||
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
|
||||||
end
|
|
||||||
|
|
||||||
SimpleCov.start 'rails' do
|
|
||||||
enable_coverage :branch
|
|
||||||
|
|
||||||
add_filter 'lib/linter'
|
|
||||||
|
|
||||||
add_group 'Libraries', 'lib'
|
|
||||||
add_group 'Policies', 'app/policies'
|
|
||||||
add_group 'Presenters', 'app/presenters'
|
|
||||||
add_group 'Serializers', 'app/serializers'
|
|
||||||
add_group 'Services', 'app/services'
|
|
||||||
add_group 'Validators', 'app/validators'
|
|
||||||
end
|
|
55
CHANGELOG.md
55
CHANGELOG.md
@ -2,6 +2,61 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [4.2.9] - 2024-05-30
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Update dependencies
|
||||||
|
- Fix private mention filtering ([GHSA-5fq7-3p3j-9vrf](https://github.com/mastodon/mastodon/security/advisories/GHSA-5fq7-3p3j-9vrf))
|
||||||
|
- Fix password change endpoint not being rate-limited ([GHSA-q3rg-xx5v-4mxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-q3rg-xx5v-4mxh))
|
||||||
|
- Add hardening around rate-limit bypass ([GHSA-c2r5-cfqr-c553](https://github.com/mastodon/mastodon/security/advisories/GHSA-c2r5-cfqr-c553))
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add rate-limit on OAuth application registration ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
|
||||||
|
- Add fallback redirection when getting a webfinger query `WEB_DOMAIN@WEB_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28592))
|
||||||
|
- Add `digest` attribute to `Admin::DomainBlock` entity in REST API ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29092))
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Remove superfluous application-level caching in some controllers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29862))
|
||||||
|
- Remove aggressive OAuth application vacuuming ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix leaking Elasticsearch connections in Sidekiq processes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30450))
|
||||||
|
- Fix language of remote posts not being recognized when using unusual casing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30403))
|
||||||
|
- Fix off-by-one in `tootctl media` commands ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30306))
|
||||||
|
- Fix removal of allowed domains (in `LIMITED_FEDERATION_MODE`) not being recorded in the audit log ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30125))
|
||||||
|
- Fix not being able to block a subdomain of an already-blocked domain through the API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30119))
|
||||||
|
- Fix `Idempotency-Key` being ignored when scheduling a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30084))
|
||||||
|
- Fix crash when supplying the `FFMPEG_BINARY` environment variable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30022))
|
||||||
|
- Fix improper email address validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29838))
|
||||||
|
- Fix results/query in `api/v1/featured_tags/suggestions` ([mjankowski](https://github.com/mastodon/mastodon/pull/29597))
|
||||||
|
- Fix unblocking internationalized domain names under certain conditions ([tribela](https://github.com/mastodon/mastodon/pull/29530))
|
||||||
|
- Fix admin account created by `mastodon:setup` not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29379))
|
||||||
|
- Fix reference to non-existent var in CLI maintenance command ([mjankowski](https://github.com/mastodon/mastodon/pull/28363))
|
||||||
|
|
||||||
|
## [4.2.8] - 2024-02-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add hourly task to automatically require approval for new registrations in the absence of moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29318), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29355))
|
||||||
|
In order to prevent future abandoned Mastodon servers from being used for spam, harassment and other malicious activity, Mastodon will now automatically switch new user registrations to require moderator approval whenever they are left open and no activity (including non-moderation actions from apps) from any logged-in user with permission to access moderation reports has been detected in a full week.
|
||||||
|
When this happens, users with the permission to change server settings will receive an email notification.
|
||||||
|
This feature is disabled when `EMAIL_DOMAIN_ALLOWLIST` is used, and can also be disabled with `DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS=true`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change registrations to be closed by default on new installations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29280))
|
||||||
|
If you are running a server and never changed your registrations mode from the default, updating will automatically close your registrations.
|
||||||
|
Simply re-enable them through the administration interface or using `tootctl settings registrations open` if you want to enable them again.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix processing of remote ActivityPub actors making use of `Link` objects as `Image` `url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29335))
|
||||||
|
- Fix link verifications when page size exceeds 1MB ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29358))
|
||||||
|
|
||||||
## [4.2.7] - 2024-02-16
|
## [4.2.7] - 2024-02-16
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
17
Dockerfile
17
Dockerfile
@ -1,5 +1,8 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
# This file is designed for production server deployment, not local development work
|
||||||
|
# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
|
||||||
|
|
||||||
# Please see https://docs.docker.com/engine/reference/builder for information about
|
# Please see https://docs.docker.com/engine/reference/builder for information about
|
||||||
# the extended buildx capabilities used in this file.
|
# the extended buildx capabilities used in this file.
|
||||||
# Make sure multiarch TARGETPLATFORM is available for interpolation
|
# Make sure multiarch TARGETPLATFORM is available for interpolation
|
||||||
@ -7,22 +10,24 @@
|
|||||||
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
ARG TARGETPLATFORM=${TARGETPLATFORM}
|
||||||
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
ARG BUILDPLATFORM=${BUILDPLATFORM}
|
||||||
|
|
||||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.1"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
|
||||||
ARG RUBY_VERSION="3.3.1"
|
# renovate: datasource=docker depName=docker.io/ruby
|
||||||
|
ARG RUBY_VERSION="3.3.2"
|
||||||
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||||
|
# renovate: datasource=node-version depName=node
|
||||||
ARG NODE_MAJOR_VERSION="20"
|
ARG NODE_MAJOR_VERSION="20"
|
||||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||||
ARG DEBIAN_VERSION="bookworm"
|
ARG DEBIAN_VERSION="bookworm"
|
||||||
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||||
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
|
FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
|
||||||
# Ruby image to use for base image based on combined variables (ex: 3.3.1-slim-bookworm)
|
# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm)
|
||||||
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
|
FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
|
||||||
|
|
||||||
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
|
||||||
# Example: v4.2.0-nightly.2023.11.09+something
|
# Example: v4.2.0-nightly.2023.11.09+something
|
||||||
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
|
# Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
|
||||||
ARG MASTODON_VERSION_PRERELEASE=""
|
ARG MASTODON_VERSION_PRERELEASE=""
|
||||||
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"]
|
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-12345"]
|
||||||
ARG MASTODON_VERSION_METADATA=""
|
ARG MASTODON_VERSION_METADATA=""
|
||||||
|
|
||||||
# Allow Ruby on Rails to serve static files
|
# Allow Ruby on Rails to serve static files
|
||||||
@ -43,6 +48,8 @@ ENV \
|
|||||||
# Apply Mastodon version information
|
# Apply Mastodon version information
|
||||||
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
|
||||||
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
|
||||||
|
# Enable libvips
|
||||||
|
MASTODON_USE_LIBVIPS=true \
|
||||||
# Apply Mastodon static files and YJIT options
|
# Apply Mastodon static files and YJIT options
|
||||||
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
|
||||||
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
|
||||||
@ -97,7 +104,7 @@ RUN \
|
|||||||
curl \
|
curl \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
file \
|
file \
|
||||||
imagemagick \
|
libvips42 \
|
||||||
libjemalloc2 \
|
libjemalloc2 \
|
||||||
patchelf \
|
patchelf \
|
||||||
procps \
|
procps \
|
||||||
|
11
Gemfile
11
Gemfile
@ -23,6 +23,7 @@ gem 'fog-core', '<= 2.4.0'
|
|||||||
gem 'fog-openstack', '~> 1.0', require: false
|
gem 'fog-openstack', '~> 1.0', require: false
|
||||||
gem 'kt-paperclip', '~> 7.2'
|
gem 'kt-paperclip', '~> 7.2'
|
||||||
gem 'md-paperclip-azure', '~> 2.2', require: false
|
gem 'md-paperclip-azure', '~> 2.2', require: false
|
||||||
|
gem 'ruby-vips', '~> 2.2', require: false
|
||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.8'
|
||||||
@ -56,7 +57,7 @@ gem 'hiredis', '~> 0.6'
|
|||||||
gem 'htmlentities', '~> 4.3'
|
gem 'htmlentities', '~> 4.3'
|
||||||
gem 'http', '~> 5.2.0'
|
gem 'http', '~> 5.2.0'
|
||||||
gem 'http_accept_language', '~> 2.1'
|
gem 'http_accept_language', '~> 2.1'
|
||||||
gem 'httplog', '~> 1.6.2'
|
gem 'httplog', '~> 1.7.0'
|
||||||
gem 'i18n'
|
gem 'i18n'
|
||||||
gem 'idn-ruby', require: 'idn'
|
gem 'idn-ruby', require: 'idn'
|
||||||
gem 'inline_svg'
|
gem 'inline_svg'
|
||||||
@ -103,8 +104,10 @@ gem 'rdf-normalize', '~> 0.5'
|
|||||||
|
|
||||||
gem 'private_address_check', '~> 0.5'
|
gem 'private_address_check', '~> 0.5'
|
||||||
|
|
||||||
|
gem 'opentelemetry-api', '~> 1.2.5'
|
||||||
|
|
||||||
group :opentelemetry do
|
group :opentelemetry do
|
||||||
gem 'opentelemetry-exporter-otlp', '~> 0.26.3', require: false
|
gem 'opentelemetry-exporter-otlp', '~> 0.27.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
|
||||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
|
||||||
@ -132,7 +135,7 @@ group :test do
|
|||||||
gem 'email_spec'
|
gem 'email_spec'
|
||||||
|
|
||||||
# Extra RSpec extension methods and helpers for sidekiq
|
# Extra RSpec extension methods and helpers for sidekiq
|
||||||
gem 'rspec-sidekiq', '~> 4.0'
|
gem 'rspec-sidekiq', '~> 5.0'
|
||||||
|
|
||||||
# Browser integration testing
|
# Browser integration testing
|
||||||
gem 'capybara', '~> 3.39'
|
gem 'capybara', '~> 3.39'
|
||||||
@ -178,7 +181,7 @@ group :development do
|
|||||||
|
|
||||||
# Preview mail in the browser
|
# Preview mail in the browser
|
||||||
gem 'letter_opener', '~> 1.8'
|
gem 'letter_opener', '~> 1.8'
|
||||||
gem 'letter_opener_web', '~> 2.0'
|
gem 'letter_opener_web', '~> 3.0'
|
||||||
|
|
||||||
# Security analysis CLI tools
|
# Security analysis CLI tools
|
||||||
gem 'brakeman', '~> 6.0', require: false
|
gem 'brakeman', '~> 6.0', require: false
|
||||||
|
204
Gemfile.lock
204
Gemfile.lock
@ -10,35 +10,35 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (7.1.3.2)
|
actioncable (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (7.1.3.2)
|
actionmailbox (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.2)
|
activejob (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.2)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.2)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
mail (>= 2.7.1)
|
mail (>= 2.7.1)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
actionmailer (7.1.3.2)
|
actionmailer (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
actionview (= 7.1.3.2)
|
actionview (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.2)
|
activejob (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
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.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (7.1.3.2)
|
actionpack (7.1.3.4)
|
||||||
actionview (= 7.1.3.2)
|
actionview (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
racc
|
racc
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
@ -46,15 +46,15 @@ GEM
|
|||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
actiontext (7.1.3.2)
|
actiontext (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.2)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.2)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (7.1.3.2)
|
actionview (7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
@ -64,22 +64,22 @@ GEM
|
|||||||
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.1.3.2)
|
activejob (7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.1.3.2)
|
activemodel (7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
activerecord (7.1.3.2)
|
activerecord (7.1.3.4)
|
||||||
activemodel (= 7.1.3.2)
|
activemodel (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (7.1.3.2)
|
activestorage (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.2)
|
activejob (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.2)
|
activerecord (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (7.1.3.2)
|
activesupport (7.1.3.4)
|
||||||
base64
|
base64
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
@ -100,17 +100,17 @@ GEM
|
|||||||
attr_required (1.0.2)
|
attr_required (1.0.2)
|
||||||
awrence (1.2.1)
|
awrence (1.2.1)
|
||||||
aws-eventstream (1.3.0)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.922.0)
|
aws-partitions (1.940.0)
|
||||||
aws-sdk-core (3.194.1)
|
aws-sdk-core (3.197.0)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.651.0)
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
jmespath (~> 1, >= 1.6.1)
|
jmespath (~> 1, >= 1.6.1)
|
||||||
aws-sdk-kms (1.80.0)
|
aws-sdk-kms (1.83.0)
|
||||||
aws-sdk-core (~> 3, >= 3.193.0)
|
aws-sdk-core (~> 3, >= 3.197.0)
|
||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sdk-s3 (1.149.1)
|
aws-sdk-s3 (1.152.0)
|
||||||
aws-sdk-core (~> 3, >= 3.194.0)
|
aws-sdk-core (~> 3, >= 3.197.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.8)
|
aws-sigv4 (~> 1.8)
|
||||||
aws-sigv4 (1.8.0)
|
aws-sigv4 (1.8.0)
|
||||||
@ -168,7 +168,7 @@ GEM
|
|||||||
climate_control (1.2.0)
|
climate_control (1.2.0)
|
||||||
cocoon (1.2.15)
|
cocoon (1.2.15)
|
||||||
color_diff (0.1)
|
color_diff (0.1)
|
||||||
concurrent-ruby (1.2.3)
|
concurrent-ruby (1.3.3)
|
||||||
connection_pool (2.4.1)
|
connection_pool (2.4.1)
|
||||||
cose (1.3.0)
|
cose (1.3.0)
|
||||||
cbor (~> 0.5.9)
|
cbor (~> 0.5.9)
|
||||||
@ -231,7 +231,7 @@ GEM
|
|||||||
tzinfo
|
tzinfo
|
||||||
excon (0.110.0)
|
excon (0.110.0)
|
||||||
fabrication (2.31.0)
|
fabrication (2.31.0)
|
||||||
faker (3.3.1)
|
faker (3.4.1)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (1.10.3)
|
faraday (1.10.3)
|
||||||
faraday-em_http (~> 1.0)
|
faraday-em_http (~> 1.0)
|
||||||
@ -272,7 +272,7 @@ GEM
|
|||||||
fog-json (1.2.0)
|
fog-json (1.2.0)
|
||||||
fog-core
|
fog-core
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
fog-openstack (1.1.0)
|
fog-openstack (1.1.1)
|
||||||
fog-core (~> 2.1)
|
fog-core (~> 2.1)
|
||||||
fog-json (>= 1.0)
|
fog-json (>= 1.0)
|
||||||
formatador (1.1.0)
|
formatador (1.1.0)
|
||||||
@ -321,7 +321,7 @@ GEM
|
|||||||
http-form_data (2.3.0)
|
http-form_data (2.3.0)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
httplog (1.6.3)
|
httplog (1.7.0)
|
||||||
rack (>= 2.0)
|
rack (>= 2.0)
|
||||||
rainbow (>= 2.0.0)
|
rainbow (>= 2.0.0)
|
||||||
i18n (1.14.5)
|
i18n (1.14.5)
|
||||||
@ -389,10 +389,10 @@ GEM
|
|||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
letter_opener (1.10.0)
|
letter_opener (1.10.0)
|
||||||
launchy (>= 2.2, < 4)
|
launchy (>= 2.2, < 4)
|
||||||
letter_opener_web (2.0.0)
|
letter_opener_web (3.0.0)
|
||||||
actionmailer (>= 5.2)
|
actionmailer (>= 6.1)
|
||||||
letter_opener (~> 1.7)
|
letter_opener (~> 1.9)
|
||||||
railties (>= 5.2)
|
railties (>= 6.1)
|
||||||
rexml
|
rexml
|
||||||
link_header (0.0.8)
|
link_header (0.0.8)
|
||||||
llhttp-ffi (0.5.0)
|
llhttp-ffi (0.5.0)
|
||||||
@ -422,10 +422,10 @@ GEM
|
|||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
mime-types (3.5.2)
|
mime-types (3.5.2)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2024.0305)
|
mime-types-data (3.2024.0507)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.6)
|
mini_portile2 (2.8.7)
|
||||||
minitest (5.22.3)
|
minitest (5.23.1)
|
||||||
msgpack (1.7.2)
|
msgpack (1.7.2)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.4.0)
|
multipart-post (2.4.0)
|
||||||
@ -434,7 +434,7 @@ GEM
|
|||||||
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.4.10)
|
net-imap (0.4.12)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
net-ldap (0.19.0)
|
net-ldap (0.19.0)
|
||||||
@ -444,8 +444,8 @@ GEM
|
|||||||
timeout
|
timeout
|
||||||
net-smtp (0.5.0)
|
net-smtp (0.5.0)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.1)
|
nio4r (2.7.3)
|
||||||
nokogiri (1.16.4)
|
nokogiri (1.16.5)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nsa (0.3.0)
|
nsa (0.3.0)
|
||||||
@ -453,7 +453,7 @@ GEM
|
|||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
sidekiq (>= 3.5)
|
sidekiq (>= 3.5)
|
||||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||||
oj (3.16.3)
|
oj (3.16.4)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
omniauth (2.1.2)
|
omniauth (2.1.2)
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
@ -489,7 +489,7 @@ GEM
|
|||||||
opentelemetry-api (1.2.5)
|
opentelemetry-api (1.2.5)
|
||||||
opentelemetry-common (0.20.1)
|
opentelemetry-common (0.20.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-exporter-otlp (0.26.3)
|
opentelemetry-exporter-otlp (0.27.0)
|
||||||
google-protobuf (~> 3.14)
|
google-protobuf (~> 3.14)
|
||||||
googleapis-common-protos-types (~> 1.3)
|
googleapis-common-protos-types (~> 1.3)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
@ -578,15 +578,15 @@ GEM
|
|||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ox (2.14.18)
|
ox (2.14.18)
|
||||||
parallel (1.24.0)
|
parallel (1.25.1)
|
||||||
parser (3.3.1.0)
|
parser (3.3.2.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pastel (0.8.0)
|
pastel (0.8.0)
|
||||||
tty-color (~> 0.5)
|
tty-color (~> 0.5)
|
||||||
pg (1.5.6)
|
pg (1.5.6)
|
||||||
pghero (3.4.1)
|
pghero (3.5.0)
|
||||||
activerecord (>= 6)
|
activerecord (>= 6)
|
||||||
premailer (1.23.0)
|
premailer (1.23.0)
|
||||||
addressable
|
addressable
|
||||||
@ -597,7 +597,7 @@ 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)
|
||||||
propshaft (0.8.0)
|
propshaft (0.9.0)
|
||||||
actionpack (>= 7.0.0)
|
actionpack (>= 7.0.0)
|
||||||
activesupport (>= 7.0.0)
|
activesupport (>= 7.0.0)
|
||||||
rack
|
rack
|
||||||
@ -610,7 +610,7 @@ GEM
|
|||||||
pundit (2.3.2)
|
pundit (2.3.2)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.7.3)
|
racc (1.8.0)
|
||||||
rack (2.2.9)
|
rack (2.2.9)
|
||||||
rack-attack (6.7.0)
|
rack-attack (6.7.0)
|
||||||
rack (>= 1.0, < 4)
|
rack (>= 1.0, < 4)
|
||||||
@ -634,20 +634,20 @@ GEM
|
|||||||
rackup (1.0.0)
|
rackup (1.0.0)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (7.1.3.2)
|
rails (7.1.3.4)
|
||||||
actioncable (= 7.1.3.2)
|
actioncable (= 7.1.3.4)
|
||||||
actionmailbox (= 7.1.3.2)
|
actionmailbox (= 7.1.3.4)
|
||||||
actionmailer (= 7.1.3.2)
|
actionmailer (= 7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
actiontext (= 7.1.3.2)
|
actiontext (= 7.1.3.4)
|
||||||
actionview (= 7.1.3.2)
|
actionview (= 7.1.3.4)
|
||||||
activejob (= 7.1.3.2)
|
activejob (= 7.1.3.4)
|
||||||
activemodel (= 7.1.3.2)
|
activemodel (= 7.1.3.4)
|
||||||
activerecord (= 7.1.3.2)
|
activerecord (= 7.1.3.4)
|
||||||
activestorage (= 7.1.3.2)
|
activestorage (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 7.1.3.2)
|
railties (= 7.1.3.4)
|
||||||
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)
|
||||||
@ -662,9 +662,9 @@ GEM
|
|||||||
rails-i18n (7.0.9)
|
rails-i18n (7.0.9)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 6.0.0, < 8)
|
railties (>= 6.0.0, < 8)
|
||||||
railties (7.1.3.2)
|
railties (7.1.3.4)
|
||||||
actionpack (= 7.1.3.2)
|
actionpack (= 7.1.3.4)
|
||||||
activesupport (= 7.1.3.2)
|
activesupport (= 7.1.3.4)
|
||||||
irb
|
irb
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
@ -685,15 +685,16 @@ GEM
|
|||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.9.0)
|
regexp_parser (2.9.2)
|
||||||
reline (0.5.6)
|
reline (0.5.8)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
request_store (1.6.0)
|
request_store (1.6.0)
|
||||||
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.8)
|
||||||
|
strscan (>= 3.0.9)
|
||||||
rotp (6.3.0)
|
rotp (6.3.0)
|
||||||
rouge (4.2.1)
|
rouge (4.2.1)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
@ -708,7 +709,7 @@ GEM
|
|||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-github (2.4.0)
|
rspec-github (2.4.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
rspec-mocks (3.13.0)
|
rspec-mocks (3.13.1)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-rails (6.1.2)
|
rspec-rails (6.1.2)
|
||||||
@ -719,13 +720,13 @@ GEM
|
|||||||
rspec-expectations (~> 3.13)
|
rspec-expectations (~> 3.13)
|
||||||
rspec-mocks (~> 3.13)
|
rspec-mocks (~> 3.13)
|
||||||
rspec-support (~> 3.13)
|
rspec-support (~> 3.13)
|
||||||
rspec-sidekiq (4.2.0)
|
rspec-sidekiq (5.0.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
rspec-expectations (~> 3.0)
|
rspec-expectations (~> 3.0)
|
||||||
rspec-mocks (~> 3.0)
|
rspec-mocks (~> 3.0)
|
||||||
sidekiq (>= 5, < 8)
|
sidekiq (>= 5, < 8)
|
||||||
rspec-support (3.13.1)
|
rspec-support (3.13.1)
|
||||||
rubocop (1.63.5)
|
rubocop (1.64.1)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (>= 3.17.0)
|
language_server-protocol (>= 3.17.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
@ -738,19 +739,19 @@ GEM
|
|||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.31.3)
|
rubocop-ast (1.31.3)
|
||||||
parser (>= 3.3.1.0)
|
parser (>= 3.3.1.0)
|
||||||
rubocop-capybara (2.20.0)
|
rubocop-capybara (2.21.0)
|
||||||
rubocop (~> 1.41)
|
rubocop (~> 1.41)
|
||||||
rubocop-factory_bot (2.25.1)
|
rubocop-factory_bot (2.25.1)
|
||||||
rubocop (~> 1.41)
|
rubocop (~> 1.41)
|
||||||
rubocop-performance (1.21.0)
|
rubocop-performance (1.21.0)
|
||||||
rubocop (>= 1.48.1, < 2.0)
|
rubocop (>= 1.48.1, < 2.0)
|
||||||
rubocop-ast (>= 1.31.1, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rails (2.24.1)
|
rubocop-rails (2.25.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.33.0, < 2.0)
|
rubocop (>= 1.33.0, < 2.0)
|
||||||
rubocop-ast (>= 1.31.1, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rspec (2.29.2)
|
rubocop-rspec (2.31.0)
|
||||||
rubocop (~> 1.40)
|
rubocop (~> 1.40)
|
||||||
rubocop-capybara (~> 2.17)
|
rubocop-capybara (~> 2.17)
|
||||||
rubocop-factory_bot (~> 2.22)
|
rubocop-factory_bot (~> 2.22)
|
||||||
@ -762,6 +763,8 @@ GEM
|
|||||||
ruby-saml (1.16.0)
|
ruby-saml (1.16.0)
|
||||||
nokogiri (>= 1.13.10)
|
nokogiri (>= 1.13.10)
|
||||||
rexml
|
rexml
|
||||||
|
ruby-vips (2.2.1)
|
||||||
|
ffi (~> 1.12)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
rufus-scheduler (3.9.1)
|
rufus-scheduler (3.9.1)
|
||||||
@ -774,7 +777,7 @@ GEM
|
|||||||
scenic (1.8.0)
|
scenic (1.8.0)
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
selenium-webdriver (4.20.1)
|
selenium-webdriver (4.21.1)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
rubyzip (>= 1.2.2, < 3.0)
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
@ -798,7 +801,7 @@ GEM
|
|||||||
thor (>= 0.20, < 3.0)
|
thor (>= 0.20, < 3.0)
|
||||||
simple-navigation (4.4.0)
|
simple-navigation (4.4.0)
|
||||||
activesupport (>= 2.3.2)
|
activesupport (>= 2.3.2)
|
||||||
simple_form (5.3.0)
|
simple_form (5.3.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activemodel (>= 5.2)
|
activemodel (>= 5.2)
|
||||||
simplecov (0.22.0)
|
simplecov (0.22.0)
|
||||||
@ -815,6 +818,7 @@ GEM
|
|||||||
stringio (3.1.0)
|
stringio (3.1.0)
|
||||||
strong_migrations (1.8.0)
|
strong_migrations (1.8.0)
|
||||||
activerecord (>= 5.2)
|
activerecord (>= 5.2)
|
||||||
|
strscan (3.1.0)
|
||||||
swd (1.3.0)
|
swd (1.3.0)
|
||||||
activesupport (>= 3)
|
activesupport (>= 3)
|
||||||
attr_required (>= 0.0.5)
|
attr_required (>= 0.0.5)
|
||||||
@ -875,7 +879,7 @@ GEM
|
|||||||
webfinger (1.2.0)
|
webfinger (1.2.0)
|
||||||
activesupport
|
activesupport
|
||||||
httpclient (>= 2.4)
|
httpclient (>= 2.4)
|
||||||
webmock (3.23.0)
|
webmock (3.23.1)
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
@ -893,7 +897,7 @@ GEM
|
|||||||
xorcist (1.1.3)
|
xorcist (1.1.3)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.6.13)
|
zeitwerk (2.6.15)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@ -943,7 +947,7 @@ DEPENDENCIES
|
|||||||
htmlentities (~> 4.3)
|
htmlentities (~> 4.3)
|
||||||
http (~> 5.2.0)
|
http (~> 5.2.0)
|
||||||
http_accept_language (~> 2.1)
|
http_accept_language (~> 2.1)
|
||||||
httplog (~> 1.6.2)
|
httplog (~> 1.7.0)
|
||||||
i18n
|
i18n
|
||||||
i18n-tasks (~> 1.0)
|
i18n-tasks (~> 1.0)
|
||||||
idn-ruby
|
idn-ruby
|
||||||
@ -955,7 +959,7 @@ DEPENDENCIES
|
|||||||
kaminari (~> 1.2)
|
kaminari (~> 1.2)
|
||||||
kt-paperclip (~> 7.2)
|
kt-paperclip (~> 7.2)
|
||||||
letter_opener (~> 1.8)
|
letter_opener (~> 1.8)
|
||||||
letter_opener_web (~> 2.0)
|
letter_opener_web (~> 3.0)
|
||||||
link_header (~> 0.0)
|
link_header (~> 0.0)
|
||||||
lograge (~> 0.12)
|
lograge (~> 0.12)
|
||||||
mail (~> 2.8)
|
mail (~> 2.8)
|
||||||
@ -973,7 +977,8 @@ DEPENDENCIES
|
|||||||
omniauth-rails_csrf_protection (~> 1.0)
|
omniauth-rails_csrf_protection (~> 1.0)
|
||||||
omniauth-saml (~> 2.0)
|
omniauth-saml (~> 2.0)
|
||||||
omniauth_openid_connect (~> 0.6.1)
|
omniauth_openid_connect (~> 0.6.1)
|
||||||
opentelemetry-exporter-otlp (~> 0.26.3)
|
opentelemetry-api (~> 1.2.5)
|
||||||
|
opentelemetry-exporter-otlp (~> 0.27.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.7.1)
|
opentelemetry-instrumentation-active_job (~> 0.7.1)
|
||||||
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
|
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
|
||||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
|
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
|
||||||
@ -1012,7 +1017,7 @@ DEPENDENCIES
|
|||||||
rqrcode (~> 2.2)
|
rqrcode (~> 2.2)
|
||||||
rspec-github (~> 2.4)
|
rspec-github (~> 2.4)
|
||||||
rspec-rails (~> 6.0)
|
rspec-rails (~> 6.0)
|
||||||
rspec-sidekiq (~> 4.0)
|
rspec-sidekiq (~> 5.0)
|
||||||
rubocop
|
rubocop
|
||||||
rubocop-capybara
|
rubocop-capybara
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
@ -1020,6 +1025,7 @@ DEPENDENCIES
|
|||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
ruby-prof
|
ruby-prof
|
||||||
ruby-progressbar (~> 1.13)
|
ruby-progressbar (~> 1.13)
|
||||||
|
ruby-vips (~> 2.2)
|
||||||
rubyzip (~> 2.3)
|
rubyzip (~> 2.3)
|
||||||
sanitize (~> 6.0)
|
sanitize (~> 6.0)
|
||||||
scenic (~> 1.7)
|
scenic (~> 1.7)
|
||||||
@ -1047,7 +1053,7 @@ DEPENDENCIES
|
|||||||
xorcist (~> 1.1)
|
xorcist (~> 1.1)
|
||||||
|
|
||||||
RUBY VERSION
|
RUBY VERSION
|
||||||
ruby 3.3.1p55
|
ruby 3.3.2p78
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.5.9
|
2.5.11
|
||||||
|
67
README.md
67
README.md
@ -100,7 +100,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
|
|||||||
- **Ruby** 3.1+
|
- **Ruby** 3.1+
|
||||||
- **Node.js** 18+
|
- **Node.js** 18+
|
||||||
|
|
||||||
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@ -114,40 +114,51 @@ A **Vagrant** configuration is included for development purposes. To use it, com
|
|||||||
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
|
- Run `vagrant ssh -c "cd /vagrant && bin/dev"`
|
||||||
- Open `http://mastodon.local` in your browser
|
- Open `http://mastodon.local` in your browser
|
||||||
|
|
||||||
### MacOS
|
### macOS
|
||||||
|
|
||||||
To set up **MacOS** for native development, complete the following steps:
|
To set up **macOS** for native development, complete the following steps:
|
||||||
|
|
||||||
- Use a Ruby version manager to install the specified version from `.ruby-version`
|
- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick
|
||||||
- Run `bundle` to install required gems
|
libidn nvm` to install the required project dependencies
|
||||||
- Run `brew install postgresql@14 redis imagemagick libidn` to install required dependencies
|
- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
|
||||||
- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from `.nvmrc`
|
`nvm use` to activate the node version from `.nvmrc`
|
||||||
- Run `yarn` to install required packages
|
- Run the `bin/setup` script, which will install the required ruby gems and node
|
||||||
- Run `corepack enable && corepack prepare`
|
packages and prepare the database for local development
|
||||||
- Run `RAILS_ENV=development bundle exec rails db:setup`
|
- Finally, run the `bin/dev` script which will launch services via `overmind`
|
||||||
- Finally, run `bin/dev` which will launch the local services via `overmind` (if installed) or `foreman`
|
(if installed) or `foreman`
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
For development with **Docker**, complete the following steps:
|
For production hosting and deployment with **Docker**, use the `Dockerfile` and
|
||||||
|
`docker-compose.yml` in the project root directory.
|
||||||
|
|
||||||
- Install Docker Desktop
|
For local development, install and launch [Docker], and run:
|
||||||
- Run `docker compose -f .devcontainer/docker-compose.yml up -d`
|
|
||||||
- Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh`
|
|
||||||
- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app bin/dev`
|
|
||||||
|
|
||||||
If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers).
|
```shell
|
||||||
|
docker compose -f .devcontainer/compose.yaml up -d
|
||||||
|
docker compose -f .devcontainer/compose.yaml exec app bin/setup
|
||||||
|
docker compose -f .devcontainer/compose.yaml exec app bin/dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dev Containers
|
||||||
|
|
||||||
|
Within IDEs that support the [Development Containers] specification, start the
|
||||||
|
"Mastodon on local machine" container from the editor. The necessary `docker
|
||||||
|
compose` commands to build and setup the container should run automatically. For
|
||||||
|
**Visual Studio Code** this requires installing the [Dev Container extension].
|
||||||
|
|
||||||
### GitHub Codespaces
|
### GitHub Codespaces
|
||||||
|
|
||||||
To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project..
|
[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
|
||||||
|
development environment configured with the software needed for this project.
|
||||||
|
|
||||||
- Click this button to create a new codespace:<br>
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace]
|
||||||
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json)
|
|
||||||
- Wait for the environment to build. This will take a few minutes.
|
- Click the button to create a new codespace, and confirm the options
|
||||||
- When the editor is ready, run `bin/dev` in the terminal.
|
- Wait for the environment to build (takes a few minutes)
|
||||||
- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon.
|
- When the editor is ready, run `bin/dev` in the terminal
|
||||||
- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_.
|
- Wait for an _Open in Browser_ prompt. This will open Mastodon
|
||||||
|
- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@ -166,4 +177,10 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
>>>>>>> 3341db939cd077820ad598b0445d02ab2382eaf4
|
|
||||||
|
[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json
|
||||||
|
[Dev Container extension]: https://containers.dev/supporting#dev-containers
|
||||||
|
[Development Containers]: https://containers.dev/supporting
|
||||||
|
[Docker]: https://docs.docker.com
|
||||||
|
[GitHub Codespaces]: https://docs.github.com/en/codespaces
|
||||||
|
[Homebrew]: https://brew.sh
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
|
If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
|
||||||
|
|
||||||
- open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
|
- open a [GitHub security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
|
||||||
- reach us at <security@joinmastodon.org>
|
- reach us at <security@joinmastodon.org>
|
||||||
|
|
||||||
You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
|
You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
|
||||||
|
6
Vagrantfile
vendored
6
Vagrantfile
vendored
@ -151,6 +151,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.vm.provider :libvirt do |libvirt|
|
||||||
|
libvirt.cpus = 3
|
||||||
|
libvirt.memory = 8192
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# This uses the vagrant-hostsupdater plugin, and lets you
|
# This uses the vagrant-hostsupdater plugin, and lets you
|
||||||
# access the development site at http://mastodon.local.
|
# access the development site at http://mastodon.local.
|
||||||
# If you change it, also change it in .env.vagrant before provisioning
|
# If you change it, also change it in .env.vagrant before provisioning
|
||||||
|
@ -25,7 +25,7 @@ class AccountsController < ApplicationController
|
|||||||
|
|
||||||
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
|
||||||
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
@statuses = filtered_statuses.without_reblogs.limit(limit)
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = preload_collection(@statuses, Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
|
@ -18,7 +18,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||||||
def set_items
|
def set_items
|
||||||
case params[:id]
|
case params[:id]
|
||||||
when 'featured'
|
when 'featured'
|
||||||
@items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
|
@items = for_signed_account { preload_collection(@account.pinned_statuses.not_local_only, Status) }
|
||||||
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
|
@items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
|
||||||
when 'tags'
|
when 'tags'
|
||||||
@items = for_signed_account { @account.featured_tags }
|
@items = for_signed_account { @account.featured_tags }
|
||||||
|
@ -60,7 +60,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||||||
def set_statuses
|
def set_statuses
|
||||||
return unless page_requested?
|
return unless page_requested?
|
||||||
|
|
||||||
@statuses = cache_collection_paginated_by_id(
|
@statuses = preload_collection_paginated_by_id(
|
||||||
AccountStatusesFilter.new(@account, signed_request_account).results,
|
AccountStatusesFilter.new(@account, signed_request_account).results,
|
||||||
Status,
|
Status,
|
||||||
LIMIT,
|
LIMIT,
|
||||||
|
@ -128,7 +128,7 @@ module Admin
|
|||||||
def unblock_email
|
def unblock_email
|
||||||
authorize @account, :unblock_email?
|
authorize @account, :unblock_email?
|
||||||
|
|
||||||
CanonicalEmailBlock.matching_account(@account).delete_all
|
CanonicalEmailBlock.where(reference_account: @account).delete_all
|
||||||
|
|
||||||
log_action :unblock_email, @account
|
log_action :unblock_email, @account
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts', :'read:me' }, except: [:update]
|
before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }, except: [:update]
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
|
|
||||||
|
@ -60,8 +60,4 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -60,8 +60,4 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -19,11 +19,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
@account.unavailable? ? [] : cached_account_statuses
|
@account.unavailable? ? [] : preloaded_account_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_account_statuses
|
def preloaded_account_statuses
|
||||||
cache_collection_paginated_by_id(
|
preload_collection_paginated_by_id(
|
||||||
AccountStatusesFilter.new(@account, current_account, params).results,
|
AccountStatusesFilter.new(@account, current_account, params).results,
|
||||||
Status,
|
Status,
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
|
@ -106,11 +106,11 @@ class Api::V1::AccountsController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def account_ids
|
def account_ids
|
||||||
Array(accounts_params[:ids]).uniq.map(&:to_i)
|
Array(accounts_params[:id]).uniq.map(&:to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def accounts_params
|
def accounts_params
|
||||||
params.permit(ids: [])
|
params.permit(id: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
|
@ -16,8 +16,6 @@ class Api::V1::Admin::CanonicalEmailBlocksController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(limit).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :canonical_email_block, :index?
|
authorize :canonical_email_block, :index?
|
||||||
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||||
@ -80,8 +78,4 @@ class Api::V1::Admin::CanonicalEmailBlocksController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@canonical_email_blocks.size == limit_param(LIMIT)
|
@canonical_email_blocks.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -14,8 +14,6 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(limit).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :domain_allow, :index?
|
authorize :domain_allow, :index?
|
||||||
render json: @domain_allows, each_serializer: REST::Admin::DomainAllowSerializer
|
render json: @domain_allows, each_serializer: REST::Admin::DomainAllowSerializer
|
||||||
@ -77,10 +75,6 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
|
|||||||
@domain_allows.size == limit_param(LIMIT)
|
@domain_allows.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.permit(:domain)
|
params.permit(:domain)
|
||||||
end
|
end
|
||||||
|
@ -14,8 +14,6 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(limit).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :domain_block, :index?
|
authorize :domain_block, :index?
|
||||||
render json: @domain_blocks, each_serializer: REST::Admin::DomainBlockSerializer
|
render json: @domain_blocks, each_serializer: REST::Admin::DomainBlockSerializer
|
||||||
@ -93,10 +91,6 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
|
|||||||
@domain_blocks.size == limit_param(LIMIT)
|
@domain_blocks.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
params.permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
||||||
end
|
end
|
||||||
|
@ -14,10 +14,6 @@ class Api::V1::Admin::EmailDomainBlocksController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(
|
|
||||||
limit
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :email_domain_block, :index?
|
authorize :email_domain_block, :index?
|
||||||
render json: @email_domain_blocks, each_serializer: REST::Admin::EmailDomainBlockSerializer
|
render json: @email_domain_blocks, each_serializer: REST::Admin::EmailDomainBlockSerializer
|
||||||
@ -73,8 +69,4 @@ class Api::V1::Admin::EmailDomainBlocksController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@email_domain_blocks.size == limit_param(LIMIT)
|
@email_domain_blocks.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -14,10 +14,6 @@ class Api::V1::Admin::IpBlocksController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(
|
|
||||||
limit
|
|
||||||
).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :ip_block, :index?
|
authorize :ip_block, :index?
|
||||||
render json: @ip_blocks, each_serializer: REST::Admin::IpBlockSerializer
|
render json: @ip_blocks, each_serializer: REST::Admin::IpBlockSerializer
|
||||||
@ -78,8 +74,4 @@ class Api::V1::Admin::IpBlocksController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@ip_blocks.size == limit_param(LIMIT)
|
@ip_blocks.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -12,7 +12,6 @@ class Api::V1::Admin::TagsController < Api::BaseController
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
LIMIT = 100
|
LIMIT = 100
|
||||||
PAGINATION_PARAMS = %i(limit).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :tag, :index?
|
authorize :tag, :index?
|
||||||
@ -59,8 +58,4 @@ class Api::V1::Admin::TagsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@tags.size == limit_param(LIMIT)
|
@tags.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -12,8 +12,6 @@ class Api::V1::Admin::Trends::Links::PreviewCardProvidersController < Api::BaseC
|
|||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
after_action :insert_pagination_headers, only: :index
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
PAGINATION_PARAMS = %i(limit).freeze
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :preview_card_provider, :index?
|
authorize :preview_card_provider, :index?
|
||||||
|
|
||||||
@ -57,8 +55,4 @@ class Api::V1::Admin::Trends::Links::PreviewCardProvidersController < Api::BaseC
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@providers.size == limit_param(LIMIT)
|
@providers.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
|
|||||||
def show
|
def show
|
||||||
return doorkeeper_render_error unless valid_doorkeeper_token?
|
return doorkeeper_render_error unless valid_doorkeeper_token?
|
||||||
|
|
||||||
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key client_id scopes)
|
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ class Api::V1::AppsController < Api::BaseController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
@app = Doorkeeper::Application.create!(application_options)
|
@app = Doorkeeper::Application.create!(application_options)
|
||||||
render json: @app, serializer: REST::ApplicationSerializer
|
render json: @app, serializer: REST::CredentialApplicationSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -24,6 +24,6 @@ class Api::V1::AppsController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def app_params
|
def app_params
|
||||||
params.permit(:client_name, :redirect_uris, :scopes, :website)
|
params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: [])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -43,8 +43,4 @@ class Api::V1::BlocksController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
paginated_blocks.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -13,11 +13,11 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_bookmarks
|
preloaded_bookmarks
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_bookmarks
|
def preloaded_bookmarks
|
||||||
cache_collection(results.map(&:status), Status)
|
preload_collection(results.map(&:status), Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@ -46,8 +46,4 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -38,15 +38,15 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||||||
def paginated_conversations
|
def paginated_conversations
|
||||||
AccountConversation.where(account: current_account)
|
AccountConversation.where(account: current_account)
|
||||||
.includes(
|
.includes(
|
||||||
account: :account_stat,
|
account: [:account_stat, user: :role],
|
||||||
last_status: [
|
last_status: [
|
||||||
:media_attachments,
|
:media_attachments,
|
||||||
:status_stat,
|
:status_stat,
|
||||||
:tags,
|
:tags,
|
||||||
{
|
{
|
||||||
preview_cards_status: :preview_card,
|
preview_cards_status: { preview_card: { author_account: [:account_stat, user: :role] } },
|
||||||
active_mentions: [account: :account_stat],
|
active_mentions: :account,
|
||||||
account: :account_stat,
|
account: [:account_stat, user: :role],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -72,8 +72,4 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@conversations.size == limit_param(LIMIT)
|
@conversations.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -44,8 +44,4 @@ class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@encrypted_messages.size == limit_param(LIMIT)
|
@encrypted_messages.size == limit_param(LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -54,10 +54,6 @@ class Api::V1::DomainBlocksController < Api::BaseController
|
|||||||
@blocks.size == limit_param(BLOCK_LIMIT)
|
@blocks.size == limit_param(BLOCK_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def domain_block_params
|
def domain_block_params
|
||||||
params.permit(:domain)
|
params.permit(:domain)
|
||||||
end
|
end
|
||||||
|
@ -48,10 +48,6 @@ class Api::V1::EndorsementsController < Api::BaseController
|
|||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unlimited?
|
def unlimited?
|
||||||
params[:limit] == '0'
|
params[:limit] == '0'
|
||||||
end
|
end
|
||||||
|
@ -13,11 +13,11 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_favourites
|
preloaded_favourites
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_favourites
|
def preloaded_favourites
|
||||||
cache_collection(results.map(&:status), Status)
|
preload_collection(results.map(&:status), Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@ -46,8 +46,4 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -67,8 +67,4 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -37,8 +37,4 @@ class Api::V1::FollowedTagsController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@results.size == limit_param(TAGS_LIMIT)
|
@results.size == limit_param(TAGS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ class Api::V1::Instances::ExtendedDescriptionsController < Api::V1::Instances::B
|
|||||||
|
|
||||||
before_action :set_extended_description
|
before_action :set_extended_description
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
||||||
def current_user
|
def current_user
|
||||||
super if limited_federation_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ class Api::V1::Instances::PeersController < Api::V1::Instances::BaseController
|
|||||||
|
|
||||||
skip_around_action :set_locale
|
skip_around_action :set_locale
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
||||||
def current_user
|
def current_user
|
||||||
super if limited_federation_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ class Api::V1::Instances::RulesController < Api::V1::Instances::BaseController
|
|||||||
|
|
||||||
before_action :set_rules
|
before_action :set_rules
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
||||||
def current_user
|
def current_user
|
||||||
super if limited_federation_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ class Api::V1::InstancesController < Api::BaseController
|
|||||||
|
|
||||||
vary_by ''
|
vary_by ''
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in whitelist mode
|
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
||||||
def current_user
|
def current_user
|
||||||
super if limited_federation_mode?
|
super if limited_federation_mode?
|
||||||
end
|
end
|
||||||
|
@ -75,10 +75,6 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def unlimited?
|
def unlimited?
|
||||||
params[:limit] == '0'
|
params[:limit] == '0'
|
||||||
end
|
end
|
||||||
|
@ -43,8 +43,4 @@ class Api::V1::MutesController < Api::BaseController
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -41,7 +41,7 @@ class Api::V1::Notifications::RequestsController < Api::BaseController
|
|||||||
)
|
)
|
||||||
|
|
||||||
NotificationRequest.preload_cache_collection(requests) do |statuses|
|
NotificationRequest.preload_cache_collection(requests) do |statuses|
|
||||||
cache_collection(statuses, Status)
|
preload_collection(statuses, Status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||||||
)
|
)
|
||||||
|
|
||||||
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
||||||
cache_collection(target_statuses, Status)
|
preload_collection(target_statuses, Status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,10 +43,6 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
|
|||||||
params.permit(:scheduled_at)
|
params.permit(:scheduled_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id) if records_continue?
|
api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id) if records_continue?
|
||||||
end
|
end
|
||||||
|
@ -53,8 +53,4 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::Bas
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -49,8 +49,4 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::Base
|
|||||||
def records_continue?
|
def records_continue?
|
||||||
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -26,13 +26,13 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
DESCENDANTS_DEPTH_LIMIT = 20
|
DESCENDANTS_DEPTH_LIMIT = 20
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = cache_collection(@statuses, Status)
|
@statuses = preload_collection(@statuses, Status)
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
cache_if_unauthenticated!
|
cache_if_unauthenticated!
|
||||||
@status = cache_collection([@status], Status).first
|
@status = preload_collection([@status], Status).first
|
||||||
render json: @status, serializer: REST::StatusSerializer
|
render json: @status, serializer: REST::StatusSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
|
|
||||||
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account)
|
ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(ancestors_limit, current_account)
|
||||||
descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
|
descendants_results = @status.descendants(descendants_limit, current_account, descendants_depth_limit)
|
||||||
loaded_ancestors = cache_collection(ancestors_results, Status)
|
loaded_ancestors = preload_collection(ancestors_results, Status)
|
||||||
loaded_descendants = cache_collection(descendants_results, Status)
|
loaded_descendants = preload_collection(descendants_results, Status)
|
||||||
|
|
||||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||||
statuses = [@status] + @context.ancestors + @context.descendants
|
statuses = [@status] + @context.ancestors + @context.descendants
|
||||||
@ -143,11 +143,11 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def status_ids
|
def status_ids
|
||||||
Array(statuses_params[:ids]).uniq.map(&:to_i)
|
Array(statuses_params[:id]).uniq.map(&:to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def statuses_params
|
def statuses_params
|
||||||
params.permit(ids: [])
|
params.permit(id: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_params
|
def status_params
|
||||||
@ -191,8 +191,4 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
def serialized_accounts(accounts)
|
def serialized_accounts(accounts)
|
||||||
ActiveModel::Serializer::CollectionSerializer.new(accounts, serializer: REST::AccountSerializer)
|
ActiveModel::Serializer::CollectionSerializer.new(accounts, serializer: REST::AccountSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -15,11 +15,11 @@ class Api::V1::Timelines::DirectController < Api::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_direct_statuses
|
preloaded_direct_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_direct_statuses
|
def preloaded_direct_statuses
|
||||||
cache_collection direct_statuses, Status
|
preload_collection direct_statuses, Status
|
||||||
end
|
end
|
||||||
|
|
||||||
def direct_statuses
|
def direct_statuses
|
||||||
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
|
|||||||
private
|
private
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_home_statuses
|
preloaded_home_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_home_statuses
|
def preloaded_home_statuses
|
||||||
cache_collection home_statuses, Status
|
preload_collection home_statuses, Status
|
||||||
end
|
end
|
||||||
|
|
||||||
def home_statuses
|
def home_statuses
|
||||||
|
52
app/controllers/api/v1/timelines/link_controller.rb
Normal file
52
app/controllers/api/v1/timelines/link_controller.rb
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
|
||||||
|
before_action :set_preview_card
|
||||||
|
before_action :set_statuses
|
||||||
|
|
||||||
|
PERMITTED_PARAMS = %i(
|
||||||
|
url
|
||||||
|
limit
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
def show
|
||||||
|
cache_if_unauthenticated!
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def require_auth?
|
||||||
|
!Setting.timeline_preview
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_preview_card
|
||||||
|
@preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_statuses
|
||||||
|
@statuses = @preview_card.nil? ? [] : preload_collection(link_timeline_statuses, Status)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_timeline_statuses
|
||||||
|
link_feed.get(
|
||||||
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
|
params[:max_id],
|
||||||
|
params[:since_id],
|
||||||
|
params[:min_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_feed
|
||||||
|
LinkFeed.new(@preview_card, current_account)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_timelines_link_url next_path_params
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_timelines_link_url prev_path_params
|
||||||
|
end
|
||||||
|
end
|
@ -21,11 +21,11 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
@statuses = cached_list_statuses
|
@statuses = preloaded_list_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_list_statuses
|
def preloaded_list_statuses
|
||||||
cache_collection list_statuses, Status
|
preload_collection list_statuses, Status
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_statuses
|
def list_statuses
|
||||||
|
@ -18,11 +18,11 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_public_statuses_page
|
preloaded_public_statuses_page
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_public_statuses_page
|
def preloaded_public_statuses_page
|
||||||
cache_collection(public_statuses, Status)
|
preload_collection(public_statuses, Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_statuses
|
def public_statuses
|
||||||
|
@ -23,11 +23,11 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load_statuses
|
def load_statuses
|
||||||
cached_tagged_statuses
|
preloaded_tagged_statuses
|
||||||
end
|
end
|
||||||
|
|
||||||
def cached_tagged_statuses
|
def preloaded_tagged_statuses
|
||||||
@tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status)
|
@tag.nil? ? [] : preload_collection(tag_timeline_statuses, Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_timeline_statuses
|
def tag_timeline_statuses
|
||||||
|
@ -34,10 +34,6 @@ class Api::V1::Trends::LinksController < Api::BaseController
|
|||||||
scope
|
scope
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT)) if records_continue?
|
api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT)) if records_continue?
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
|||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
@statuses = if enabled?
|
@statuses = if enabled?
|
||||||
cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
preload_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
@ -32,10 +32,6 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
|||||||
scope
|
scope
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT)) if records_continue?
|
api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT)) if records_continue?
|
||||||
end
|
end
|
||||||
|
@ -30,10 +30,6 @@ class Api::V1::Trends::TagsController < Api::BaseController
|
|||||||
Trends.tags.query.allowed
|
Trends.tags.query.allowed
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
|
||||||
params.slice(:limit).permit(:limit).merge(core_params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT)) if records_continue?
|
api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT)) if records_continue?
|
||||||
end
|
end
|
||||||
|
91
app/controllers/api/v2_alpha/notifications_controller.rb
Normal file
91
app/controllers/api/v2_alpha/notifications_controller.rb
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V2Alpha::NotificationsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
|
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
|
||||||
|
before_action :require_user!
|
||||||
|
after_action :insert_pagination_headers, only: :index
|
||||||
|
|
||||||
|
DEFAULT_NOTIFICATIONS_LIMIT = 40
|
||||||
|
|
||||||
|
def index
|
||||||
|
with_read_replica do
|
||||||
|
@notifications = load_notifications
|
||||||
|
@group_metadata = load_group_metadata
|
||||||
|
@relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@notification = current_account.notifications.without_suspended.find_by!(group_key: params[:id])
|
||||||
|
render json: NotificationGroup.from_notification(@notification), serializer: REST::NotificationGroupSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
current_account.notifications.delete_all
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
def dismiss
|
||||||
|
current_account.notifications.where(group_key: params[:id]).destroy_all
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_notifications
|
||||||
|
notifications = browserable_account_notifications.includes(from_account: [:account_stat, :user]).to_a_grouped_paginated_by_id(
|
||||||
|
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||||
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
|
||||||
|
preload_collection(target_statuses, Status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_group_metadata
|
||||||
|
return {} if @notifications.empty?
|
||||||
|
|
||||||
|
browserable_account_notifications
|
||||||
|
.where(group_key: @notifications.filter_map(&:group_key))
|
||||||
|
.where(id: (@notifications.last.id)..(@notifications.first.id))
|
||||||
|
.group(:group_key)
|
||||||
|
.pluck(:group_key, 'min(notifications.id) as min_id', 'max(notifications.id) as max_id', 'max(notifications.created_at) as latest_notification_at')
|
||||||
|
.to_h { |group_key, min_id, max_id, latest_notification_at| [group_key, { min_id: min_id, max_id: max_id, latest_notification_at: latest_notification_at }] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def browserable_account_notifications
|
||||||
|
current_account.notifications.without_suspended.browserable(
|
||||||
|
types: Array(browserable_params[:types]),
|
||||||
|
exclude_types: Array(browserable_params[:exclude_types]),
|
||||||
|
include_filtered: truthy_param?(:include_filtered)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def target_statuses_from_notifications
|
||||||
|
@notifications.filter_map(&:target_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v2_alpha_notifications_url pagination_params(max_id: pagination_max_id) unless @notifications.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v2_alpha_notifications_url pagination_params(min_id: pagination_since_id) unless @notifications.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_collection
|
||||||
|
@notifications
|
||||||
|
end
|
||||||
|
|
||||||
|
def browserable_params
|
||||||
|
params.permit(:include_filtered, types: [], exclude_types: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit, :types, :exclude_types, :include_filtered).permit(:limit, :include_filtered, types: [], exclude_types: []).merge(core_params)
|
||||||
|
end
|
||||||
|
end
|
@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base
|
|||||||
include UserTrackingConcern
|
include UserTrackingConcern
|
||||||
include SessionTrackingConcern
|
include SessionTrackingConcern
|
||||||
include CacheConcern
|
include CacheConcern
|
||||||
|
include PreloadingConcern
|
||||||
include DomainControlHelper
|
include DomainControlHelper
|
||||||
include ThemingConcern
|
include ThemingConcern
|
||||||
include DatabaseHelper
|
include DatabaseHelper
|
||||||
|
@ -44,7 +44,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_resource(hash = nil)
|
def build_resource(hash = nil)
|
||||||
super(hash)
|
super
|
||||||
|
|
||||||
resource.locale = I18n.locale
|
resource.locale = I18n.locale
|
||||||
resource.invite_code = @invite&.code if resource.invite_code.blank?
|
resource.invite_code = @invite&.code if resource.invite_code.blank?
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
module Api::Pagination
|
module Api::Pagination
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
PAGINATION_PARAMS = %i(limit).freeze
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def pagination_max_id
|
def pagination_max_id
|
||||||
@ -24,6 +26,13 @@ module Api::Pagination
|
|||||||
render json: { error: 'Pagination values for `offset` and `limit` must be positive' }, status: 400 if pagination_options_invalid?
|
render json: { error: 'Pagination values for `offset` and `limit` must be positive' }, status: 400 if pagination_options_invalid?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params
|
||||||
|
.slice(*PAGINATION_PARAMS)
|
||||||
|
.permit(*PAGINATION_PARAMS)
|
||||||
|
.merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
|
@ -45,20 +45,4 @@ module CacheConcern
|
|||||||
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
|
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Rename this method, as it does not perform any caching anymore.
|
|
||||||
def cache_collection(raw, klass)
|
|
||||||
return raw unless klass.respond_to?(:preload_cacheable_associations)
|
|
||||||
|
|
||||||
records = raw.to_a
|
|
||||||
|
|
||||||
klass.preload_cacheable_associations(records)
|
|
||||||
|
|
||||||
records
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Rename this method, as it does not perform any caching anymore.
|
|
||||||
def cache_collection_paginated_by_id(raw, klass, limit, options)
|
|
||||||
cache_collection raw.to_a_paginated_by_id(limit, options), klass
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
17
app/controllers/concerns/preloading_concern.rb
Normal file
17
app/controllers/concerns/preloading_concern.rb
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module PreloadingConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def preload_collection(scope, klass)
|
||||||
|
return scope unless klass.respond_to?(:preload_cacheable_associations)
|
||||||
|
|
||||||
|
scope.to_a.tap do |records|
|
||||||
|
klass.preload_cacheable_associations(records)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def preload_collection_paginated_by_id(scope, klass, limit, options)
|
||||||
|
preload_collection scope.to_a_paginated_by_id(limit, options), klass
|
||||||
|
end
|
||||||
|
end
|
@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
|
|||||||
def new
|
def new
|
||||||
@application = Doorkeeper::Application.new(
|
@application = Doorkeeper::Application.new(
|
||||||
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
|
||||||
scopes: 'read write follow'
|
scopes: 'profile'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class TagsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_statuses
|
def set_statuses
|
||||||
@statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
@statuses = preload_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def limit_param
|
def limit_param
|
||||||
|
@ -241,11 +241,20 @@ module ApplicationHelper
|
|||||||
EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s
|
EmojiFormatter.new(html, custom_emojis, other_options.merge(animate: prefers_autoplay?)).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def site_icon_path(type, size = '48')
|
def mascot_url
|
||||||
icon = SiteUpload.find_by(var: type)
|
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
||||||
return nil unless icon
|
end
|
||||||
|
|
||||||
icon.file.url(size)
|
def instance_presenter
|
||||||
|
@instance_presenter ||= InstancePresenter.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def favicon_path(size = '48')
|
||||||
|
instance_presenter.favicon&.file&.url(size)
|
||||||
|
end
|
||||||
|
|
||||||
|
def app_icon_path(size = '48')
|
||||||
|
instance_presenter.app_icon&.file&.url(size)
|
||||||
end
|
end
|
||||||
|
|
||||||
# glitch-soc addition to handle the multiple flavors
|
# glitch-soc addition to handle the multiple flavors
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module MascotHelper
|
|
||||||
def mascot_url
|
|
||||||
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def instance_presenter
|
|
||||||
@instance_presenter ||= InstancePresenter.new
|
|
||||||
end
|
|
||||||
end
|
|
@ -65,7 +65,7 @@ window.addEventListener('message', (e) => {
|
|||||||
{
|
{
|
||||||
type: 'setHeight',
|
type: 'setHeight',
|
||||||
id: data.id,
|
id: data.id,
|
||||||
height: document.getElementsByTagName('html')[0].scrollHeight,
|
height: document.getElementsByTagName('html')[0]?.scrollHeight,
|
||||||
},
|
},
|
||||||
'*',
|
'*',
|
||||||
);
|
);
|
||||||
@ -135,7 +135,7 @@ function loaded() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
const todayFormat = new IntlMessageFormat(
|
const todayFormat = new IntlMessageFormat(
|
||||||
localeData['relative_format.today'] || 'Today at {time}',
|
localeData['relative_format.today'] ?? 'Today at {time}',
|
||||||
locale,
|
locale,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -288,13 +288,13 @@ function loaded() {
|
|||||||
if (statusEl.dataset.spoiler === 'expanded') {
|
if (statusEl.dataset.spoiler === 'expanded') {
|
||||||
statusEl.dataset.spoiler = 'folded';
|
statusEl.dataset.spoiler = 'folded';
|
||||||
this.textContent = new IntlMessageFormat(
|
this.textContent = new IntlMessageFormat(
|
||||||
localeData['status.show_more'] || 'Show more',
|
localeData['status.show_more'] ?? 'Show more',
|
||||||
locale,
|
locale,
|
||||||
).format() as string;
|
).format() as string;
|
||||||
} else {
|
} else {
|
||||||
statusEl.dataset.spoiler = 'expanded';
|
statusEl.dataset.spoiler = 'expanded';
|
||||||
this.textContent = new IntlMessageFormat(
|
this.textContent = new IntlMessageFormat(
|
||||||
localeData['status.show_less'] || 'Show less',
|
localeData['status.show_less'] ?? 'Show less',
|
||||||
locale,
|
locale,
|
||||||
).format() as string;
|
).format() as string;
|
||||||
}
|
}
|
||||||
@ -316,8 +316,8 @@ function loaded() {
|
|||||||
|
|
||||||
const message =
|
const message =
|
||||||
statusEl.dataset.spoiler === 'expanded'
|
statusEl.dataset.spoiler === 'expanded'
|
||||||
? localeData['status.show_less'] || 'Show less'
|
? localeData['status.show_less'] ?? 'Show less'
|
||||||
: localeData['status.show_more'] || 'Show more';
|
: localeData['status.show_more'] ?? 'Show more';
|
||||||
spoilerLink.textContent = new IntlMessageFormat(
|
spoilerLink.textContent = new IntlMessageFormat(
|
||||||
message,
|
message,
|
||||||
locale,
|
locale,
|
||||||
|
@ -67,7 +67,9 @@ const fetchInteractionURLFailure = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidDomain = (value: string) => {
|
const isValidDomain = (value: unknown) => {
|
||||||
|
if (typeof value !== 'string') return false;
|
||||||
|
|
||||||
const url = new URL('https:///path');
|
const url = new URL('https:///path');
|
||||||
url.hostname = value;
|
url.hostname = value;
|
||||||
return url.hostname === value;
|
return url.hostname === value;
|
||||||
@ -124,6 +126,11 @@ const fromAcct = (acct: string) => {
|
|||||||
const domain = segments[1];
|
const domain = segments[1];
|
||||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||||
|
|
||||||
|
if (!domain) {
|
||||||
|
fetchInteractionURLFailure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.get(`https://${domain}/.well-known/webfinger`, {
|
.get(`https://${domain}/.well-known/webfinger`, {
|
||||||
params: { resource: `acct:${acct}` },
|
params: { resource: `acct:${acct}` },
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
import type { ApiRelationshipJSON } from 'flavours/glitch/api_types/relationships';
|
import { apiSubmitAccountNote } from 'flavours/glitch/api/accounts';
|
||||||
import { createAppAsyncThunk } from 'flavours/glitch/store/typed_functions';
|
import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
|
||||||
|
|
||||||
import api from '../api';
|
export const submitAccountNote = createDataLoadingThunk(
|
||||||
|
|
||||||
export const submitAccountNote = createAppAsyncThunk(
|
|
||||||
'account_note/submit',
|
'account_note/submit',
|
||||||
async (args: { id: string; value: string }, { getState }) => {
|
({ accountId, note }: { accountId: string; note: string }) =>
|
||||||
const response = await api(getState).post<ApiRelationshipJSON>(
|
apiSubmitAccountNote(accountId, note),
|
||||||
`/api/v1/accounts/${args.id}/note`,
|
(relationship) => ({ relationship }),
|
||||||
{
|
{ skipLoading: true },
|
||||||
comment: args.value,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return { relationship: response.data };
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
@ -89,11 +89,11 @@ export const ACCOUNT_REVEAL = 'ACCOUNT_REVEAL';
|
|||||||
export * from './accounts_typed';
|
export * from './accounts_typed';
|
||||||
|
|
||||||
export function fetchAccount(id) {
|
export function fetchAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchRelationships([id]));
|
dispatch(fetchRelationships([id]));
|
||||||
dispatch(fetchAccountRequest(id));
|
dispatch(fetchAccountRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/accounts/${id}`).then(response => {
|
api().get(`/api/v1/accounts/${id}`).then(response => {
|
||||||
dispatch(importFetchedAccount(response.data));
|
dispatch(importFetchedAccount(response.data));
|
||||||
dispatch(fetchAccountSuccess());
|
dispatch(fetchAccountSuccess());
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -102,10 +102,10 @@ export function fetchAccount(id) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const lookupAccount = acct => (dispatch, getState) => {
|
export const lookupAccount = acct => (dispatch) => {
|
||||||
dispatch(lookupAccountRequest(acct));
|
dispatch(lookupAccountRequest(acct));
|
||||||
|
|
||||||
api(getState).get('/api/v1/accounts/lookup', { params: { acct } }).then(response => {
|
api().get('/api/v1/accounts/lookup', { params: { acct } }).then(response => {
|
||||||
dispatch(fetchRelationships([response.data.id]));
|
dispatch(fetchRelationships([response.data.id]));
|
||||||
dispatch(importFetchedAccount(response.data));
|
dispatch(importFetchedAccount(response.data));
|
||||||
dispatch(lookupAccountSuccess());
|
dispatch(lookupAccountSuccess());
|
||||||
@ -159,7 +159,7 @@ export function followAccount(id, options = { reblogs: true }) {
|
|||||||
|
|
||||||
dispatch(followAccountRequest({ id, locked }));
|
dispatch(followAccountRequest({ id, locked }));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/follow`, options).then(response => {
|
api().post(`/api/v1/accounts/${id}/follow`, options).then(response => {
|
||||||
dispatch(followAccountSuccess({relationship: response.data, alreadyFollowing}));
|
dispatch(followAccountSuccess({relationship: response.data, alreadyFollowing}));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(followAccountFail({ id, error, locked }));
|
dispatch(followAccountFail({ id, error, locked }));
|
||||||
@ -171,7 +171,7 @@ export function unfollowAccount(id) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(unfollowAccountRequest(id));
|
dispatch(unfollowAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
|
api().post(`/api/v1/accounts/${id}/unfollow`).then(response => {
|
||||||
dispatch(unfollowAccountSuccess({relationship: response.data, statuses: getState().get('statuses')}));
|
dispatch(unfollowAccountSuccess({relationship: response.data, statuses: getState().get('statuses')}));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(unfollowAccountFail({ id, error }));
|
dispatch(unfollowAccountFail({ id, error }));
|
||||||
@ -183,7 +183,7 @@ export function blockAccount(id) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(blockAccountRequest(id));
|
dispatch(blockAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
|
api().post(`/api/v1/accounts/${id}/block`).then(response => {
|
||||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||||
dispatch(blockAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
|
dispatch(blockAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -193,10 +193,10 @@ export function blockAccount(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unblockAccount(id) {
|
export function unblockAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unblockAccountRequest(id));
|
dispatch(unblockAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
|
api().post(`/api/v1/accounts/${id}/unblock`).then(response => {
|
||||||
dispatch(unblockAccountSuccess({ relationship: response.data }));
|
dispatch(unblockAccountSuccess({ relationship: response.data }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(unblockAccountFail({ id, error }));
|
dispatch(unblockAccountFail({ id, error }));
|
||||||
@ -236,7 +236,7 @@ export function muteAccount(id, notifications, duration=0) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(muteAccountRequest(id));
|
dispatch(muteAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications, duration }).then(response => {
|
api().post(`/api/v1/accounts/${id}/mute`, { notifications, duration }).then(response => {
|
||||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||||
dispatch(muteAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
|
dispatch(muteAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -246,10 +246,10 @@ export function muteAccount(id, notifications, duration=0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unmuteAccount(id) {
|
export function unmuteAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unmuteAccountRequest(id));
|
dispatch(unmuteAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unmute`).then(response => {
|
api().post(`/api/v1/accounts/${id}/unmute`).then(response => {
|
||||||
dispatch(unmuteAccountSuccess({ relationship: response.data }));
|
dispatch(unmuteAccountSuccess({ relationship: response.data }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(unmuteAccountFail({ id, error }));
|
dispatch(unmuteAccountFail({ id, error }));
|
||||||
@ -287,10 +287,10 @@ export function unmuteAccountFail(error) {
|
|||||||
|
|
||||||
|
|
||||||
export function fetchFollowers(id) {
|
export function fetchFollowers(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchFollowersRequest(id));
|
dispatch(fetchFollowersRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => {
|
api().get(`/api/v1/accounts/${id}/followers`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -337,7 +337,7 @@ export function expandFollowers(id) {
|
|||||||
|
|
||||||
dispatch(expandFollowersRequest(id));
|
dispatch(expandFollowersRequest(id));
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -374,10 +374,10 @@ export function expandFollowersFail(id, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchFollowing(id) {
|
export function fetchFollowing(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchFollowingRequest(id));
|
dispatch(fetchFollowingRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/accounts/${id}/following`).then(response => {
|
api().get(`/api/v1/accounts/${id}/following`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -424,7 +424,7 @@ export function expandFollowing(id) {
|
|||||||
|
|
||||||
dispatch(expandFollowingRequest(id));
|
dispatch(expandFollowingRequest(id));
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -473,7 +473,7 @@ export function fetchRelationships(accountIds) {
|
|||||||
|
|
||||||
dispatch(fetchRelationshipsRequest(newAccountIds));
|
dispatch(fetchRelationshipsRequest(newAccountIds));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
|
api().get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
|
||||||
dispatch(fetchRelationshipsSuccess({ relationships: response.data }));
|
dispatch(fetchRelationshipsSuccess({ relationships: response.data }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchRelationshipsFail(error));
|
dispatch(fetchRelationshipsFail(error));
|
||||||
@ -499,10 +499,10 @@ export function fetchRelationshipsFail(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchFollowRequests() {
|
export function fetchFollowRequests() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchFollowRequestsRequest());
|
dispatch(fetchFollowRequestsRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/follow_requests').then(response => {
|
api().get('/api/v1/follow_requests').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||||
@ -541,7 +541,7 @@ export function expandFollowRequests() {
|
|||||||
|
|
||||||
dispatch(expandFollowRequestsRequest());
|
dispatch(expandFollowRequestsRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
|
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||||
@ -571,10 +571,10 @@ export function expandFollowRequestsFail(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function authorizeFollowRequest(id) {
|
export function authorizeFollowRequest(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(authorizeFollowRequestRequest(id));
|
dispatch(authorizeFollowRequestRequest(id));
|
||||||
|
|
||||||
api(getState)
|
api()
|
||||||
.post(`/api/v1/follow_requests/${id}/authorize`)
|
.post(`/api/v1/follow_requests/${id}/authorize`)
|
||||||
.then(() => dispatch(authorizeFollowRequestSuccess({ id })))
|
.then(() => dispatch(authorizeFollowRequestSuccess({ id })))
|
||||||
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
|
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
|
||||||
@ -598,10 +598,10 @@ export function authorizeFollowRequestFail(id, error) {
|
|||||||
|
|
||||||
|
|
||||||
export function rejectFollowRequest(id) {
|
export function rejectFollowRequest(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(rejectFollowRequestRequest(id));
|
dispatch(rejectFollowRequestRequest(id));
|
||||||
|
|
||||||
api(getState)
|
api()
|
||||||
.post(`/api/v1/follow_requests/${id}/reject`)
|
.post(`/api/v1/follow_requests/${id}/reject`)
|
||||||
.then(() => dispatch(rejectFollowRequestSuccess({ id })))
|
.then(() => dispatch(rejectFollowRequestSuccess({ id })))
|
||||||
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
|
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
|
||||||
@ -624,10 +624,10 @@ export function rejectFollowRequestFail(id, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function pinAccount(id) {
|
export function pinAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(pinAccountRequest(id));
|
dispatch(pinAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => {
|
api().post(`/api/v1/accounts/${id}/pin`).then(response => {
|
||||||
dispatch(pinAccountSuccess({ relationship: response.data }));
|
dispatch(pinAccountSuccess({ relationship: response.data }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(pinAccountFail(error));
|
dispatch(pinAccountFail(error));
|
||||||
@ -636,10 +636,10 @@ export function pinAccount(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unpinAccount(id) {
|
export function unpinAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unpinAccountRequest(id));
|
dispatch(unpinAccountRequest(id));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => {
|
api().post(`/api/v1/accounts/${id}/unpin`).then(response => {
|
||||||
dispatch(unpinAccountSuccess({ relationship: response.data }));
|
dispatch(unpinAccountSuccess({ relationship: response.data }));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(unpinAccountFail(error));
|
dispatch(unpinAccountFail(error));
|
||||||
@ -676,10 +676,10 @@ export function unpinAccountFail(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchPinnedAccounts() {
|
export function fetchPinnedAccounts() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchPinnedAccountsRequest());
|
dispatch(fetchPinnedAccountsRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/endorsements', { params: { limit: 0 } }).then(response => {
|
api().get('/api/v1/endorsements', { params: { limit: 0 } }).then(response => {
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchPinnedAccountsSuccess(response.data));
|
dispatch(fetchPinnedAccountsSuccess(response.data));
|
||||||
}).catch(err => dispatch(fetchPinnedAccountsFail(err)));
|
}).catch(err => dispatch(fetchPinnedAccountsFail(err)));
|
||||||
@ -707,7 +707,7 @@ export function fetchPinnedAccountsFail(error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => {
|
export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch) => {
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
|
|
||||||
data.append('display_name', displayName);
|
data.append('display_name', displayName);
|
||||||
@ -717,13 +717,13 @@ export const updateAccount = ({ displayName, note, avatar, header, discoverable,
|
|||||||
data.append('discoverable', discoverable);
|
data.append('discoverable', discoverable);
|
||||||
data.append('indexable', indexable);
|
data.append('indexable', indexable);
|
||||||
|
|
||||||
return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => {
|
return api().patch('/api/v1/accounts/update_credentials', data).then(response => {
|
||||||
dispatch(importFetchedAccount(response.data));
|
dispatch(importFetchedAccount(response.data));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export function fetchPinnedAccountsSuggestions(q) {
|
export function fetchPinnedAccountsSuggestions(q) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchPinnedAccountsSuggestionsRequest());
|
dispatch(fetchPinnedAccountsSuggestionsRequest());
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@ -733,7 +733,7 @@ export function fetchPinnedAccountsSuggestions(q) {
|
|||||||
following: true,
|
following: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
api(getState).get('/api/v1/accounts/search', { params }).then(response => {
|
api().get('/api/v1/accounts/search', { params }).then(response => {
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchPinnedAccountsSuggestionsSuccess(q, response.data));
|
dispatch(fetchPinnedAccountsSuggestionsSuccess(q, response.data));
|
||||||
}).catch(err => dispatch(fetchPinnedAccountsSuggestionsFail(err)));
|
}).catch(err => dispatch(fetchPinnedAccountsSuggestionsFail(err)));
|
||||||
|
@ -26,10 +26,10 @@ export const ANNOUNCEMENTS_TOGGLE_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
|
|||||||
|
|
||||||
const noOp = () => {};
|
const noOp = () => {};
|
||||||
|
|
||||||
export const fetchAnnouncements = (done = noOp) => (dispatch, getState) => {
|
export const fetchAnnouncements = (done = noOp) => (dispatch) => {
|
||||||
dispatch(fetchAnnouncementsRequest());
|
dispatch(fetchAnnouncementsRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/announcements').then(response => {
|
api().get('/api/v1/announcements').then(response => {
|
||||||
dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x))));
|
dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x))));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchAnnouncementsFail(error));
|
dispatch(fetchAnnouncementsFail(error));
|
||||||
@ -61,10 +61,10 @@ export const updateAnnouncements = announcement => ({
|
|||||||
announcement: normalizeAnnouncement(announcement),
|
announcement: normalizeAnnouncement(announcement),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const dismissAnnouncement = announcementId => (dispatch, getState) => {
|
export const dismissAnnouncement = announcementId => (dispatch) => {
|
||||||
dispatch(dismissAnnouncementRequest(announcementId));
|
dispatch(dismissAnnouncementRequest(announcementId));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => {
|
api().post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => {
|
||||||
dispatch(dismissAnnouncementSuccess(announcementId));
|
dispatch(dismissAnnouncementSuccess(announcementId));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(dismissAnnouncementFail(announcementId, error));
|
dispatch(dismissAnnouncementFail(announcementId, error));
|
||||||
@ -103,7 +103,7 @@ export const addReaction = (announcementId, name) => (dispatch, getState) => {
|
|||||||
dispatch(addReactionRequest(announcementId, name, alreadyAdded));
|
dispatch(addReactionRequest(announcementId, name, alreadyAdded));
|
||||||
}
|
}
|
||||||
|
|
||||||
api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
api().put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
||||||
dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
|
dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
if (!alreadyAdded) {
|
if (!alreadyAdded) {
|
||||||
@ -134,10 +134,10 @@ export const addReactionFail = (announcementId, name, error) => ({
|
|||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const removeReaction = (announcementId, name) => (dispatch, getState) => {
|
export const removeReaction = (announcementId, name) => (dispatch) => {
|
||||||
dispatch(removeReactionRequest(announcementId, name));
|
dispatch(removeReactionRequest(announcementId, name));
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
api().delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
||||||
dispatch(removeReactionSuccess(announcementId, name));
|
dispatch(removeReactionSuccess(announcementId, name));
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
dispatch(removeReactionFail(announcementId, name, err));
|
dispatch(removeReactionFail(announcementId, name, err));
|
||||||
|
@ -13,10 +13,10 @@ export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
|
|||||||
export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
|
export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
|
||||||
|
|
||||||
export function fetchBlocks() {
|
export function fetchBlocks() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchBlocksRequest());
|
dispatch(fetchBlocksRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/blocks').then(response => {
|
api().get('/api/v1/blocks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
@ -56,7 +56,7 @@ export function expandBlocks() {
|
|||||||
|
|
||||||
dispatch(expandBlocksRequest());
|
dispatch(expandBlocksRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
|
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
|
@ -18,7 +18,7 @@ export function fetchBookmarkedStatuses() {
|
|||||||
|
|
||||||
dispatch(fetchBookmarkedStatusesRequest());
|
dispatch(fetchBookmarkedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/bookmarks').then(response => {
|
api().get('/api/v1/bookmarks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
@ -59,7 +59,7 @@ export function expandBookmarkedStatuses() {
|
|||||||
|
|
||||||
dispatch(expandBookmarkedStatusesRequest());
|
dispatch(expandBookmarkedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
|
@ -211,7 +211,7 @@ export function submitCompose(routerHistory, overridePrivacy = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
api(getState).request({
|
api().request({
|
||||||
url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
|
url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
|
||||||
method: statusId === null ? 'post' : 'put',
|
method: statusId === null ? 'post' : 'put',
|
||||||
data: {
|
data: {
|
||||||
@ -338,7 +338,7 @@ export function uploadCompose(files) {
|
|||||||
// Account for disparity in size of original image and resized data
|
// Account for disparity in size of original image and resized data
|
||||||
total += file.size - f.size;
|
total += file.size - f.size;
|
||||||
|
|
||||||
return api(getState).post('/api/v2/media', data, {
|
return api().post('/api/v2/media', data, {
|
||||||
onUploadProgress: function({ loaded }){
|
onUploadProgress: function({ loaded }){
|
||||||
progress[i] = loaded;
|
progress[i] = loaded;
|
||||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||||
@ -355,7 +355,7 @@ export function uploadCompose(files) {
|
|||||||
let tryCount = 1;
|
let tryCount = 1;
|
||||||
|
|
||||||
const poll = () => {
|
const poll = () => {
|
||||||
api(getState).get(`/api/v1/media/${data.id}`).then(response => {
|
api().get(`/api/v1/media/${data.id}`).then(response => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
dispatch(uploadComposeSuccess(response.data, f));
|
dispatch(uploadComposeSuccess(response.data, f));
|
||||||
} else if (response.status === 206) {
|
} else if (response.status === 206) {
|
||||||
@ -378,7 +378,7 @@ export const uploadComposeProcessing = () => ({
|
|||||||
type: COMPOSE_UPLOAD_PROCESSING,
|
type: COMPOSE_UPLOAD_PROCESSING,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const uploadThumbnail = (id, file) => (dispatch, getState) => {
|
export const uploadThumbnail = (id, file) => (dispatch) => {
|
||||||
dispatch(uploadThumbnailRequest());
|
dispatch(uploadThumbnailRequest());
|
||||||
|
|
||||||
const total = file.size;
|
const total = file.size;
|
||||||
@ -386,7 +386,7 @@ export const uploadThumbnail = (id, file) => (dispatch, getState) => {
|
|||||||
|
|
||||||
data.append('thumbnail', file);
|
data.append('thumbnail', file);
|
||||||
|
|
||||||
api(getState).put(`/api/v1/media/${id}`, data, {
|
api().put(`/api/v1/media/${id}`, data, {
|
||||||
onUploadProgress: ({ loaded }) => {
|
onUploadProgress: ({ loaded }) => {
|
||||||
dispatch(uploadThumbnailProgress(loaded, total));
|
dispatch(uploadThumbnailProgress(loaded, total));
|
||||||
},
|
},
|
||||||
@ -469,7 +469,7 @@ export function changeUploadCompose(id, params) {
|
|||||||
|
|
||||||
dispatch(changeUploadComposeSuccess(data, true));
|
dispatch(changeUploadComposeSuccess(data, true));
|
||||||
} else {
|
} else {
|
||||||
api(getState).put(`/api/v1/media/${id}`, params).then(response => {
|
api().put(`/api/v1/media/${id}`, params).then(response => {
|
||||||
dispatch(changeUploadComposeSuccess(response.data, false));
|
dispatch(changeUploadComposeSuccess(response.data, false));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(changeUploadComposeFail(id, error));
|
dispatch(changeUploadComposeFail(id, error));
|
||||||
@ -557,7 +557,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) =>
|
|||||||
|
|
||||||
fetchComposeSuggestionsAccountsController = new AbortController();
|
fetchComposeSuggestionsAccountsController = new AbortController();
|
||||||
|
|
||||||
api(getState).get('/api/v1/accounts/search', {
|
api().get('/api/v1/accounts/search', {
|
||||||
signal: fetchComposeSuggestionsAccountsController.signal,
|
signal: fetchComposeSuggestionsAccountsController.signal,
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
@ -591,7 +591,7 @@ const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => {
|
|||||||
|
|
||||||
fetchComposeSuggestionsTagsController = new AbortController();
|
fetchComposeSuggestionsTagsController = new AbortController();
|
||||||
|
|
||||||
api(getState).get('/api/v2/search', {
|
api().get('/api/v2/search', {
|
||||||
signal: fetchComposeSuggestionsTagsController.signal,
|
signal: fetchComposeSuggestionsTagsController.signal,
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
|
@ -28,13 +28,13 @@ export const unmountConversations = () => ({
|
|||||||
type: CONVERSATIONS_UNMOUNT,
|
type: CONVERSATIONS_UNMOUNT,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const markConversationRead = conversationId => (dispatch, getState) => {
|
export const markConversationRead = conversationId => (dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONVERSATIONS_READ,
|
type: CONVERSATIONS_READ,
|
||||||
id: conversationId,
|
id: conversationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
api().post(`/api/v1/conversations/${conversationId}/read`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
|
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
|
||||||
@ -48,7 +48,7 @@ export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
|
|||||||
|
|
||||||
const isLoadingRecent = !!params.since_id;
|
const isLoadingRecent = !!params.since_id;
|
||||||
|
|
||||||
api(getState).get('/api/v1/conversations', { params })
|
api().get('/api/v1/conversations', { params })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
@ -88,10 +88,10 @@ export const updateConversations = conversation => dispatch => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteConversation = conversationId => (dispatch, getState) => {
|
export const deleteConversation = conversationId => (dispatch) => {
|
||||||
dispatch(deleteConversationRequest(conversationId));
|
dispatch(deleteConversationRequest(conversationId));
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/conversations/${conversationId}`)
|
api().delete(`/api/v1/conversations/${conversationId}`)
|
||||||
.then(() => dispatch(deleteConversationSuccess(conversationId)))
|
.then(() => dispatch(deleteConversationSuccess(conversationId)))
|
||||||
.catch(error => dispatch(deleteConversationFail(conversationId, error)));
|
.catch(error => dispatch(deleteConversationFail(conversationId, error)));
|
||||||
};
|
};
|
||||||
|
@ -5,10 +5,10 @@ export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS';
|
|||||||
export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL';
|
export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL';
|
||||||
|
|
||||||
export function fetchCustomEmojis() {
|
export function fetchCustomEmojis() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchCustomEmojisRequest());
|
dispatch(fetchCustomEmojisRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/custom_emojis').then(response => {
|
api().get('/api/v1/custom_emojis').then(response => {
|
||||||
dispatch(fetchCustomEmojisSuccess(response.data));
|
dispatch(fetchCustomEmojisSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchCustomEmojisFail(error));
|
dispatch(fetchCustomEmojisFail(error));
|
||||||
|
@ -11,10 +11,10 @@ export const DIRECTORY_EXPAND_REQUEST = 'DIRECTORY_EXPAND_REQUEST';
|
|||||||
export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS';
|
export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS';
|
||||||
export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL';
|
export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL';
|
||||||
|
|
||||||
export const fetchDirectory = params => (dispatch, getState) => {
|
export const fetchDirectory = params => (dispatch) => {
|
||||||
dispatch(fetchDirectoryRequest());
|
dispatch(fetchDirectoryRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => {
|
api().get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => {
|
||||||
dispatch(importFetchedAccounts(data));
|
dispatch(importFetchedAccounts(data));
|
||||||
dispatch(fetchDirectorySuccess(data));
|
dispatch(fetchDirectorySuccess(data));
|
||||||
dispatch(fetchRelationships(data.map(x => x.id)));
|
dispatch(fetchRelationships(data.map(x => x.id)));
|
||||||
@ -40,7 +40,7 @@ export const expandDirectory = params => (dispatch, getState) => {
|
|||||||
|
|
||||||
const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size;
|
const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size;
|
||||||
|
|
||||||
api(getState).get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => {
|
api().get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => {
|
||||||
dispatch(importFetchedAccounts(data));
|
dispatch(importFetchedAccounts(data));
|
||||||
dispatch(expandDirectorySuccess(data));
|
dispatch(expandDirectorySuccess(data));
|
||||||
dispatch(fetchRelationships(data.map(x => x.id)));
|
dispatch(fetchRelationships(data.map(x => x.id)));
|
||||||
|
@ -24,7 +24,7 @@ export function blockDomain(domain) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(blockDomainRequest(domain));
|
dispatch(blockDomainRequest(domain));
|
||||||
|
|
||||||
api(getState).post('/api/v1/domain_blocks', { domain }).then(() => {
|
api().post('/api/v1/domain_blocks', { domain }).then(() => {
|
||||||
const at_domain = '@' + domain;
|
const at_domain = '@' + domain;
|
||||||
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export function unblockDomain(domain) {
|
|||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(unblockDomainRequest(domain));
|
dispatch(unblockDomainRequest(domain));
|
||||||
|
|
||||||
api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => {
|
api().delete('/api/v1/domain_blocks', { params: { domain } }).then(() => {
|
||||||
const at_domain = '@' + domain;
|
const at_domain = '@' + domain;
|
||||||
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
||||||
dispatch(unblockDomainSuccess({ domain, accounts }));
|
dispatch(unblockDomainSuccess({ domain, accounts }));
|
||||||
@ -80,10 +80,10 @@ export function unblockDomainFail(domain, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchDomainBlocks() {
|
export function fetchDomainBlocks() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchDomainBlocksRequest());
|
dispatch(fetchDomainBlocksRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/domain_blocks').then(response => {
|
api().get('/api/v1/domain_blocks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@ -123,7 +123,7 @@ export function expandDomainBlocks() {
|
|||||||
|
|
||||||
dispatch(expandDomainBlocksRequest());
|
dispatch(expandDomainBlocksRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null));
|
dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
@ -18,7 +18,7 @@ export function fetchFavouritedStatuses() {
|
|||||||
|
|
||||||
dispatch(fetchFavouritedStatusesRequest());
|
dispatch(fetchFavouritedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/favourites').then(response => {
|
api().get('/api/v1/favourites').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
@ -62,7 +62,7 @@ export function expandFavouritedStatuses() {
|
|||||||
|
|
||||||
dispatch(expandFavouritedStatusesRequest());
|
dispatch(expandFavouritedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
|
@ -11,7 +11,7 @@ export const fetchFeaturedTags = (id) => (dispatch, getState) => {
|
|||||||
|
|
||||||
dispatch(fetchFeaturedTagsRequest(id));
|
dispatch(fetchFeaturedTagsRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/accounts/${id}/featured_tags`)
|
api().get(`/api/v1/accounts/${id}/featured_tags`)
|
||||||
.then(({ data }) => dispatch(fetchFeaturedTagsSuccess(id, data)))
|
.then(({ data }) => dispatch(fetchFeaturedTagsSuccess(id, data)))
|
||||||
.catch(err => dispatch(fetchFeaturedTagsFail(id, err)));
|
.catch(err => dispatch(fetchFeaturedTagsFail(id, err)));
|
||||||
};
|
};
|
||||||
|
@ -23,13 +23,13 @@ export const initAddFilter = (status, { contextType }) => dispatch =>
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const fetchFilters = () => (dispatch, getState) => {
|
export const fetchFilters = () => (dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: FILTERS_FETCH_REQUEST,
|
type: FILTERS_FETCH_REQUEST,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
api(getState)
|
api()
|
||||||
.get('/api/v2/filters')
|
.get('/api/v2/filters')
|
||||||
.then(({ data }) => dispatch({
|
.then(({ data }) => dispatch({
|
||||||
type: FILTERS_FETCH_SUCCESS,
|
type: FILTERS_FETCH_SUCCESS,
|
||||||
@ -44,10 +44,10 @@ export const fetchFilters = () => (dispatch, getState) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createFilterStatus = (params, onSuccess, onFail) => (dispatch, getState) => {
|
export const createFilterStatus = (params, onSuccess, onFail) => (dispatch) => {
|
||||||
dispatch(createFilterStatusRequest());
|
dispatch(createFilterStatusRequest());
|
||||||
|
|
||||||
api(getState).post(`/api/v2/filters/${params.filter_id}/statuses`, params).then(response => {
|
api().post(`/api/v2/filters/${params.filter_id}/statuses`, params).then(response => {
|
||||||
dispatch(createFilterStatusSuccess(response.data));
|
dispatch(createFilterStatusSuccess(response.data));
|
||||||
if (onSuccess) onSuccess();
|
if (onSuccess) onSuccess();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -70,10 +70,10 @@ export const createFilterStatusFail = error => ({
|
|||||||
error,
|
error,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const createFilter = (params, onSuccess, onFail) => (dispatch, getState) => {
|
export const createFilter = (params, onSuccess, onFail) => (dispatch) => {
|
||||||
dispatch(createFilterRequest());
|
dispatch(createFilterRequest());
|
||||||
|
|
||||||
api(getState).post('/api/v2/filters', params).then(response => {
|
api().post('/api/v2/filters', params).then(response => {
|
||||||
dispatch(createFilterSuccess(response.data));
|
dispatch(createFilterSuccess(response.data));
|
||||||
if (onSuccess) onSuccess(response.data);
|
if (onSuccess) onSuccess(response.data);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@ -15,7 +15,7 @@ export const fetchHistory = statusId => (dispatch, getState) => {
|
|||||||
|
|
||||||
dispatch(fetchHistoryRequest(statusId));
|
dispatch(fetchHistoryRequest(statusId));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/statuses/${statusId}/history`).then(({ data }) => {
|
api().get(`/api/v1/statuses/${statusId}/history`).then(({ data }) => {
|
||||||
dispatch(importFetchedAccounts(data.map(x => x.account)));
|
dispatch(importFetchedAccounts(data.map(x => x.account)));
|
||||||
dispatch(fetchHistorySuccess(statusId, data));
|
dispatch(fetchHistorySuccess(statusId, data));
|
||||||
}).catch(error => dispatch(fetchHistoryFail(error)));
|
}).catch(error => dispatch(fetchHistoryFail(error)));
|
||||||
|
@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
|
|||||||
import { fetchRelationships } from './accounts';
|
import { fetchRelationships } from './accounts';
|
||||||
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
||||||
|
|
||||||
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
|
|
||||||
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
|
|
||||||
export const REBLOG_FAIL = 'REBLOG_FAIL';
|
|
||||||
|
|
||||||
export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
|
export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
|
||||||
export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
|
export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
|
||||||
export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
|
export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
|
||||||
@ -15,10 +11,6 @@ export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
|
|||||||
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
|
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
|
||||||
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
|
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
|
||||||
|
|
||||||
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
|
|
||||||
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
|
|
||||||
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
|
|
||||||
|
|
||||||
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
|
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
|
||||||
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
|
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
|
||||||
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
|
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
|
||||||
@ -51,99 +43,13 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST';
|
|||||||
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
|
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
|
||||||
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
|
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
|
||||||
|
|
||||||
export const REACTION_UPDATE = 'REACTION_UPDATE';
|
export * from "./interactions_typed";
|
||||||
|
|
||||||
export const REACTION_ADD_REQUEST = 'REACTION_ADD_REQUEST';
|
|
||||||
export const REACTION_ADD_SUCCESS = 'REACTION_ADD_SUCCESS';
|
|
||||||
export const REACTION_ADD_FAIL = 'REACTION_ADD_FAIL';
|
|
||||||
|
|
||||||
export const REACTION_REMOVE_REQUEST = 'REACTION_REMOVE_REQUEST';
|
|
||||||
export const REACTION_REMOVE_SUCCESS = 'REACTION_REMOVE_SUCCESS';
|
|
||||||
export const REACTION_REMOVE_FAIL = 'REACTION_REMOVE_FAIL';
|
|
||||||
|
|
||||||
export function reblog(status, visibility) {
|
|
||||||
return function (dispatch, getState) {
|
|
||||||
dispatch(reblogRequest(status));
|
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`, { visibility }).then(function (response) {
|
|
||||||
// The reblog API method returns a new status wrapped around the original. In this case we are only
|
|
||||||
// interested in how the original is modified, hence passing it skipping the wrapper
|
|
||||||
dispatch(importFetchedStatus(response.data.reblog));
|
|
||||||
dispatch(reblogSuccess(status));
|
|
||||||
}).catch(function (error) {
|
|
||||||
dispatch(reblogFail(status, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unreblog(status) {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
dispatch(unreblogRequest(status));
|
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
|
|
||||||
dispatch(importFetchedStatus(response.data));
|
|
||||||
dispatch(unreblogSuccess(status));
|
|
||||||
}).catch(error => {
|
|
||||||
dispatch(unreblogFail(status, error));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reblogRequest(status) {
|
|
||||||
return {
|
|
||||||
type: REBLOG_REQUEST,
|
|
||||||
status: status,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reblogSuccess(status) {
|
|
||||||
return {
|
|
||||||
type: REBLOG_SUCCESS,
|
|
||||||
status: status,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reblogFail(status, error) {
|
|
||||||
return {
|
|
||||||
type: REBLOG_FAIL,
|
|
||||||
status: status,
|
|
||||||
error: error,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unreblogRequest(status) {
|
|
||||||
return {
|
|
||||||
type: UNREBLOG_REQUEST,
|
|
||||||
status: status,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unreblogSuccess(status) {
|
|
||||||
return {
|
|
||||||
type: UNREBLOG_SUCCESS,
|
|
||||||
status: status,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unreblogFail(status, error) {
|
|
||||||
return {
|
|
||||||
type: UNREBLOG_FAIL,
|
|
||||||
status: status,
|
|
||||||
error: error,
|
|
||||||
skipLoading: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function favourite(status) {
|
export function favourite(status) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch) {
|
||||||
dispatch(favouriteRequest(status));
|
dispatch(favouriteRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
api().post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(favouriteSuccess(status));
|
dispatch(favouriteSuccess(status));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
@ -153,10 +59,10 @@ export function favourite(status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unfavourite(status) {
|
export function unfavourite(status) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unfavouriteRequest(status));
|
dispatch(unfavouriteRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
api().post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(unfavouriteSuccess(status));
|
dispatch(unfavouriteSuccess(status));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -216,10 +122,10 @@ export function unfavouriteFail(status, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function bookmark(status) {
|
export function bookmark(status) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch) {
|
||||||
dispatch(bookmarkRequest(status));
|
dispatch(bookmarkRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/bookmark`).then(function (response) {
|
api().post(`/api/v1/statuses/${status.get('id')}/bookmark`).then(function (response) {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(bookmarkSuccess(status, response.data));
|
dispatch(bookmarkSuccess(status, response.data));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
@ -229,10 +135,10 @@ export function bookmark(status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unbookmark(status) {
|
export function unbookmark(status) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unbookmarkRequest(status));
|
dispatch(unbookmarkRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then(response => {
|
api().post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then(response => {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(unbookmarkSuccess(status, response.data));
|
dispatch(unbookmarkSuccess(status, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -288,10 +194,10 @@ export function unbookmarkFail(status, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchReblogs(id) {
|
export function fetchReblogs(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchReblogsRequest(id));
|
dispatch(fetchReblogsRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
|
api().get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null));
|
dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null));
|
||||||
@ -335,7 +241,7 @@ export function expandReblogs(id) {
|
|||||||
|
|
||||||
dispatch(expandReblogsRequest(id));
|
dispatch(expandReblogsRequest(id));
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -370,10 +276,10 @@ export function expandReblogsFail(id, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchFavourites(id) {
|
export function fetchFavourites(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(fetchFavouritesRequest(id));
|
dispatch(fetchFavouritesRequest(id));
|
||||||
|
|
||||||
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
|
api().get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null));
|
dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null));
|
||||||
@ -417,7 +323,7 @@ export function expandFavourites(id) {
|
|||||||
|
|
||||||
dispatch(expandFavouritesRequest(id));
|
dispatch(expandFavouritesRequest(id));
|
||||||
|
|
||||||
api(getState).get(url).then(response => {
|
api().get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
|
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -452,10 +358,10 @@ export function expandFavouritesFail(id, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function pin(status) {
|
export function pin(status) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(pinRequest(status));
|
dispatch(pinRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
|
api().post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(pinSuccess(status));
|
dispatch(pinSuccess(status));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -490,10 +396,10 @@ export function pinFail(status, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function unpin (status) {
|
export function unpin (status) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch) => {
|
||||||
dispatch(unpinRequest(status));
|
dispatch(unpinRequest(status));
|
||||||
|
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
|
api().post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
|
||||||
dispatch(importFetchedStatus(response.data));
|
dispatch(importFetchedStatus(response.data));
|
||||||
dispatch(unpinSuccess(status));
|
dispatch(unpinSuccess(status));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
35
app/javascript/flavours/glitch/actions/interactions_typed.ts
Normal file
35
app/javascript/flavours/glitch/actions/interactions_typed.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { apiReblog, apiUnreblog } from 'flavours/glitch/api/interactions';
|
||||||
|
import type { StatusVisibility } from 'flavours/glitch/models/status';
|
||||||
|
import { createDataLoadingThunk } from 'flavours/glitch/store/typed_functions';
|
||||||
|
|
||||||
|
import { importFetchedStatus } from './importer';
|
||||||
|
|
||||||
|
export const reblog = createDataLoadingThunk(
|
||||||
|
'status/reblog',
|
||||||
|
({
|
||||||
|
statusId,
|
||||||
|
visibility,
|
||||||
|
}: {
|
||||||
|
statusId: string;
|
||||||
|
visibility: StatusVisibility;
|
||||||
|
}) => apiReblog(statusId, visibility),
|
||||||
|
(data, { dispatch, discardLoadData }) => {
|
||||||
|
// The reblog API method returns a new status wrapped around the original. In this case we are only
|
||||||
|
// interested in how the original is modified, hence passing it skipping the wrapper
|
||||||
|
dispatch(importFetchedStatus(data.reblog));
|
||||||
|
|
||||||
|
// The payload is not used in any actions
|
||||||
|
return discardLoadData;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const unreblog = createDataLoadingThunk(
|
||||||
|
'status/unreblog',
|
||||||
|
({ statusId }: { statusId: string }) => apiUnreblog(statusId),
|
||||||
|
(data, { dispatch, discardLoadData }) => {
|
||||||
|
dispatch(importFetchedStatus(data));
|
||||||
|
|
||||||
|
// The payload is not used in any actions
|
||||||
|
return discardLoadData;
|
||||||
|
},
|
||||||
|
);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user