1
0
Fork 0
forked from gitea/nas

Merge commit '4b65740722' into kb_development

This commit is contained in:
KMY 2023-07-25 15:25:28 +09:00
commit 83065ff389
398 changed files with 3376 additions and 2449 deletions

View file

@ -1,56 +0,0 @@
name: Bug Report
description: If something isn't working as expected
labels: [bug]
body:
- type: markdown
attributes:
value: |
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
description: What should have happened?
validations:
required: true
- type: input
attributes:
label: Actual behaviour
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Detailed description
validations:
required: false
- type: textarea
attributes:
label: Specifications
description: |
What version or commit hash of Mastodon did you find this bug in?
If a front-end issue, what browser and operating systems were you using?
placeholder: |
Mastodon 3.5.3 (or Edge)
Ruby 2.7.6 (or v3.1.2)
Node.js 16.18.0
Google Chrome 106.0.5249.119
Firefox 105.0.3
etc...
validations:
required: true

View file

@ -0,0 +1,76 @@
name: Bug Report (Web Interface)
description: If you are using Mastodon's web interface and something is not working as expected
labels: [bug, 'status/to triage', 'area/web interface']
body:
- type: markdown
attributes:
value: |
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
description: What should have happened?
validations:
required: true
- type: input
attributes:
label: Actual behaviour
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Detailed description
validations:
required: false
- type: input
attributes:
label: Mastodon instance
description: The address of the Mastodon instance where you experienced the issue
placeholder: mastodon.social
validations:
required: true
- type: input
attributes:
label: Mastodon version
description: |
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
placeholder: v4.1.2
validations:
required: true
- type: input
attributes:
label: Browser name and version
description: |
What browser are you using when getting this bug? Please specify the version as well.
placeholder: Firefox 105.0.3
validations:
required: true
- type: input
attributes:
label: Operating system
description: |
What OS are you running? Please specify the version as well.
placeholder: macOS 13.4.1
validations:
required: true
- type: textarea
attributes:
label: Technical details
description: |
Any additional technical details you may have. This can include the full error log, inspector's output…
validations:
required: false

View file

@ -0,0 +1,65 @@
name: Bug Report (server / API)
description: |
If something is not working as expected, but is not from using the web interface.
labels: [bug, 'status/to triage']
body:
- type: markdown
attributes:
value: |
Make sure that you are submitting a new bug that was not previously reported or already fixed.
Please use a concise and distinct title for the issue.
- type: textarea
attributes:
label: Steps to reproduce the problem
description: What were you trying to do?
value: |
1.
2.
3.
...
validations:
required: true
- type: input
attributes:
label: Expected behaviour
description: What should have happened?
validations:
required: true
- type: input
attributes:
label: Actual behaviour
description: What happened?
validations:
required: true
- type: textarea
attributes:
label: Detailed description
validations:
required: false
- type: input
attributes:
label: Mastodon instance
description: The address of the Mastodon instance where you experienced the issue
placeholder: mastodon.social
validations:
required: false
- type: input
attributes:
label: Mastodon version
description: |
This is displayed at the bottom of the About page, eg. `v4.1.2+nightly-20230627`
placeholder: v4.1.2
validations:
required: false
- type: textarea
attributes:
label: Technical details
description: |
Any additional technical details you may have, like logs or error traces
value: |
If this is happening on your own Mastodon server, please fill out those:
- Ruby version: (from `ruby --version`, eg. v3.1.2)
- Node.js version: (from `node --version`, eg. v18.16.0)
validations:
required: false

View file

@ -9,7 +9,9 @@
], ],
stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
// packageRules order is important, they are applied from top to bottom and are merged, // packageRules order is important, they are applied from top to bottom and are merged,
// so for example grouping rules needs to be at the bottom // meaning the most important ones must be at the bottom, for example grouping rules
// If we do not want a package to be grouped with others, we need to set its groupName
// to `null` after any other rule set it to something.
packageRules: [ packageRules: [
{ {
// Ignore major version bumps for these node packages // Ignore major version bumps for these node packages
@ -45,6 +47,7 @@
// Ignore major version bumps for these Ruby packages // Ignore major version bumps for these Ruby packages
matchManagers: ['bundler'], matchManagers: ['bundler'],
matchPackageNames: [ matchPackageNames: [
'rack', // Needs to be synced with Rails version
'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x 'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
'strong_migrations', // Requires manual upgrade 'strong_migrations', // Requires manual upgrade
'sidekiq', // Requires manual upgrade 'sidekiq', // Requires manual upgrade
@ -84,12 +87,17 @@
// Update devDependencies every week, with one grouped PR // Update devDependencies every week, with one grouped PR
matchDepTypes: 'devDependencies', matchDepTypes: 'devDependencies',
matchUpdateTypes: ['patch', 'minor'], matchUpdateTypes: ['patch', 'minor'],
excludePackageNames: [
'typescript', // Typescript has many changes in minor versions, needs to be checked every time
],
groupName: 'devDependencies (non-major)', groupName: 'devDependencies (non-major)',
extends: ['schedule:weekly'], extends: ['schedule:weekly'],
}, },
{
// Group all eslint-related packages with `eslint` in the same PR
matchManagers: ['npm'],
matchPackageNames: ['eslint'],
matchPackagePrefixes: ['eslint-', '@typescript-eslint/'],
matchUpdateTypes: ['patch', 'minor'],
groupName: 'eslint (non-major)',
},
{ {
// Update @types/* packages every week, with one grouped PR // Update @types/* packages every week, with one grouped PR
matchPackagePrefixes: '@types/', matchPackagePrefixes: '@types/',
@ -98,6 +106,14 @@
extends: ['schedule:weekly'], extends: ['schedule:weekly'],
addLabels: ['typescript'], addLabels: ['typescript'],
}, },
{
// We want those packages to always have their own PR
matchManagers: ['npm'],
matchPackageNames: [
'typescript', // Typescript has code-impacting changes in minor versions
],
groupName: null, // We dont want them to belong to any group
},
// Add labels depending on package manager // Add labels depending on package manager
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] }, { matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] }, { matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },

View file

@ -0,0 +1,94 @@
on:
workflow_call:
inputs:
platforms:
required: true
type: string
use_native_arm64_builder:
type: boolean
push_to_images:
type: string
version_suffix:
type: string
flavor:
type: string
tags:
type: string
labels:
type: string
jobs:
build-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v2
if: contains(inputs.platforms, 'linux/arm64') && !inputs.use_native_arm64_builder
- uses: docker/setup-buildx-action@v2
id: buildx
if: ${{ !(inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')) }}
- name: Start a local Docker Builder
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
run: |
docker run --rm -d --name buildkitd -p 1234:1234 --privileged moby/buildkit:latest --addr tcp://0.0.0.0:1234
- uses: docker/setup-buildx-action@v2
id: buildx-native
if: inputs.use_native_arm64_builder && contains(inputs.platforms, 'linux/arm64')
with:
driver: remote
endpoint: tcp://localhost:1234
platforms: linux/amd64
append: |
- endpoint: tcp://${{ vars.DOCKER_BUILDER_HETZNER_ARM64_01_HOST }}:13865
platforms: linux/arm64
name: mastodon-docker-builder-arm64-01
driver-opts:
- servername=mastodon-docker-builder-arm64-01
env:
BUILDER_NODE_1_AUTH_TLS_CACERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CACERT }}
BUILDER_NODE_1_AUTH_TLS_CERT: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_CERT }}
BUILDER_NODE_1_AUTH_TLS_KEY: ${{ secrets.DOCKER_BUILDER_HETZNER_ARM64_01_KEY }}
- name: Log in to Docker Hub
if: contains(inputs.push_to_images, 'tootsuite')
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to the Github Container registry
if: contains(inputs.push_to_images, 'ghcr.io')
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v4
id: meta
if: ${{ inputs.push_to_images != '' }}
with:
images: ${{ inputs.push_to_images }}
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: ${{ inputs.flavor }}
tags: ${{ inputs.tags }}
labels: ${{ inputs.labels }}
- uses: docker/build-push-action@v4
with:
context: .
build-args: MASTODON_VERSION_SUFFIX=${{ inputs.version_suffix }}
platforms: ${{ inputs.platforms }}
provenance: false
builder: ${{ steps.buildx.outputs.name || steps.buildx-native.outputs.name }}
push: ${{ inputs.push_to_images != '' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -1,79 +0,0 @@
name: Build container image
on:
workflow_dispatch:
push:
branches:
- 'main'
tags:
- '*'
pull_request:
paths:
- .github/workflows/build-image.yml
- Dockerfile
permissions:
contents: read
packages: write
jobs:
build-image:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: hadolint/hadolint-action@v3.1.0
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
if: github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request'
- name: Log in to the Github Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
if: github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request'
- uses: docker/metadata-action@v4
id: meta
with:
images: |
tootsuite/mastodon
ghcr.io/mastodon/mastodon
# Only tag with latest when ran against the latest stable branch
# This needs to be updated after each minor version release
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/v4.1.') }}
tags: |
type=edge,branch=main
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
type=ref,event=pr
- name: Generate version suffix
id: version_vars
if: github.repository == 'mastodon/mastodon' && github.event_name == 'push' && github.ref_name == 'main'
run: |
echo mastodon_version_suffix=+edge-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
- uses: docker/build-push-action@v4
with:
context: .
build-args: MASTODON_VERSION_SUFFIX=${{ steps.version_vars.outputs.mastodon_version_suffix }}
platforms: linux/amd64,linux/arm64
provenance: false
builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -3,58 +3,40 @@ on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 2 * * *' # run at 2 AM UTC - cron: '0 2 * * *' # run at 2 AM UTC
permissions: permissions:
contents: read contents: read
packages: write packages: write
jobs: jobs:
build-nightly-image: compute-suffix:
runs-on: ubuntu-latest runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v3 - id: version_vars
- uses: hadolint/hadolint-action@v3.1.0 env:
- uses: docker/setup-qemu-action@v2 TZ: Etc/UTC
- uses: docker/setup-buildx-action@v2
- name: Log in to the Github Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v4
id: meta
with:
images: |
ghcr.io/mastodon/mastodon
flavor: |
latest=auto
tags: |
type=raw,value=nightly
type=schedule,pattern=nightly-{{date 'YYYY-MM-DD' tz='Etc/UTC'}}
labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes
- name: Generate version suffix
id: version_vars
run: | run: |
echo mastodon_version_suffix=+nightly-$(date +'%Y%m%d') >> $GITHUB_OUTPUT echo mastodon_version_suffix=nightly-$(date +'%Y-%m-%d')>> $GITHUB_OUTPUT
outputs:
suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }}
- uses: docker/build-push-action@v4 build-image:
with: needs: compute-suffix
context: . uses: ./.github/workflows/build-container-image.yml
build-args: MASTODON_VERSION_SUFFIX=${{ steps.version_vars.outputs.mastodon_version_suffix }} with:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
provenance: false use_native_arm64_builder: true
builder: ${{ steps.buildx.outputs.name }} push_to_images: |
push: ${{ github.repository == 'mastodon/mastodon' && github.event_name != 'pull_request' }} tootsuite/mastodon
tags: ${{ steps.meta.outputs.tags }} ghcr.io/mastodon/mastodon
labels: ${{ steps.meta.outputs.labels }} # The `+` is important here, result will be v4.1.2+nightly-2022-03-05
cache-from: type=gha version_suffix: +${{ needs.compute-suffix.outputs.suffix }}
cache-to: type=gha,mode=max labels: |
org.opencontainers.image.description=Nightly build image used for testing purposes
flavor: |
latest=auto
tags: |
type=raw,value=edge
type=raw,value=nightly
type=schedule,pattern=${{ needs.compute-suffix.outputs.suffix }}
secrets: inherit

41
.github/workflows/build-push-pr.yml vendored Normal file
View file

@ -0,0 +1,41 @@
name: Build container image for PR
on:
pull_request:
types: [labeled, synchronize, reopened, ready_for_review, opened]
permissions:
contents: read
packages: write
jobs:
compute-suffix:
runs-on: ubuntu-latest
# This is only allowed to run if:
# - the PR branch is in the `mastodon/mastodon` repository
# - the PR is not a draft
# - the PR has the "build-image" label
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !github.event.pull_request.draft && contains(github.event.pull_request.labels.*.name, 'build-image') }}
steps:
# Repository needs to be cloned so `git rev-parse` below works
- name: Clone repository
uses: actions/checkout@v3
- id: version_vars
run: |
echo mastodon_version_suffix=+pr-${{ github.event.pull_request.number }}-$(git rev-parse --short HEAD) >> $GITHUB_OUTPUT
outputs:
suffix: ${{ steps.version_vars.outputs.mastodon_version_suffix }}
build-image:
needs: compute-suffix
uses: ./.github/workflows/build-container-image.yml
with:
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
ghcr.io/mastodon/mastodon
version_suffix: ${{ needs.compute-suffix.outputs.suffix }}
flavor: |
latest=auto
tags: |
type=ref,event=pr
secrets: inherit

25
.github/workflows/build-releases.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Build container release images
on:
push:
tags:
- '*'
permissions:
contents: read
packages: write
jobs:
build-image:
uses: ./.github/workflows/build-container-image.yml
with:
platforms: linux/amd64,linux/arm64
use_native_arm64_builder: true
push_to_images: |
tootsuite/mastodon
ghcr.io/mastodon/mastodon
flavor: |
latest=${{ startsWith(github.ref, 'refs/tags/v4.1.') }}
tags: |
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
secrets: inherit

76
.github/workflows/crowdin-download.yml vendored Normal file
View file

@ -0,0 +1,76 @@
name: Crowdin / Download translations
on:
schedule:
- cron: '17 4 * * *' # Every day
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
download-translations:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Increase Git http.postBuffer
# This is needed due to a bug in Ubuntu's cURL version?
# See https://github.com/orgs/community/discussions/55820
run: |
git config --global http.version HTTP/1.1
git config --global http.postBuffer 157286400
# Download the translation files from Crowdin
- name: crowdin action
uses: crowdin/github-action@v1
with:
upload_sources: false
upload_translations: false
download_translations: true
crowdin_branch_name: main
push_translations: false
create_pull_request: false
env:
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
# As the files are extracted from a Docker container, they belong to root:root
# We need to fix this before the next steps
- name: Fix file permissions
run: sudo chown -R runner:docker .
# This is needed to run the normalize step
- name: Install native Ruby dependencies
run: sudo apt-get install -y libicu-dev libidn11-dev
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Run i18n normalize task
run: bundle exec i18n-tasks normalize
# Create or update the pull request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5.0.2
with:
commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations (automated)'
author: 'GitHub Actions <noreply@github.com>'
body: |
New Crowdin translations, automated with Github Actions
See `.github/workflows/crowdin-download.yml`
This PR will be updated every day with new translations.
Due to a limitation in Github Actions, checks are not running on this PR without manual action.
If you want to run the checks, then close and re-open it.
branch: i18n/crowdin/translations
base: main
labels: i18n

35
.github/workflows/crowdin-upload.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Crowdin / Upload translations
on:
push:
branches:
- main
paths:
- crowdin.yml
- app/javascript/mastodon/locales/en.json
- config/locales/en.yml
- config/locales/simple_form.en.yml
- config/locales/activerecord.en.yml
- config/locales/devise.en.yml
- config/locales/doorkeeper.en.yml
- .github/workflows/crowdin-upload.yml
jobs:
upload-translations:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: crowdin action
uses: crowdin/github-action@v1
with:
upload_sources: true
upload_translations: false
download_translations: false
crowdin_branch_name: main
env:
CROWDIN_PROJECT_ID: ${{ vars.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View file

@ -23,5 +23,5 @@ jobs:
repoToken: '${{ secrets.GITHUB_TOKEN }}' repoToken: '${{ secrets.GITHUB_TOKEN }}'
commentOnClean: This pull request has resolved merge conflicts and is ready for review. commentOnClean: This pull request has resolved merge conflicts and is ready for review.
commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged. commentOnDirty: This pull request has merge conflicts that must be resolved before it can be merged.
retryMax: 10 retryMax: 30
continueOnMissingPermissions: false continueOnMissingPermissions: false

21
.github/workflows/test-image-build.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Test container image build
on:
pull_request:
paths:
- .github/workflows/build-nightly.yml
- .github/workflows/build-push-pr.yml
- .github/workflows/build-releases.yml
- .github/workflows/test-image-build.yml
- Dockerfile
permissions:
contents: read
jobs:
build-image:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
uses: ./.github/workflows/build-container-image.yml
with:
platforms: linux/amd64 # Testing only on native platform so it is performant

View file

@ -1,17 +1,13 @@
# This configuration was generated by # This configuration was generated by
# `haml-lint --auto-gen-config` # `haml-lint --auto-gen-config`
# on 2023-07-11 23:58:05 +0200 using Haml-Lint version 0.48.0. # on 2023-07-20 09:47:50 -0400 using Haml-Lint version 0.48.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the lints are removed from the code base. # one by one as the lints are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of Haml-Lint, may require this file to be generated again. # versions of Haml-Lint, may require this file to be generated again.
linters: linters:
# Offense count: 94 # Offense count: 951
RuboCop:
enabled: false
# Offense count: 960
LineLength: LineLength:
enabled: false enabled: false
@ -19,6 +15,10 @@ linters:
UnnecessaryStringOutput: UnnecessaryStringOutput:
enabled: false enabled: false
# Offense count: 57
RuboCop:
enabled: false
# Offense count: 3 # Offense count: 3
ViewLength: ViewLength:
exclude: exclude:
@ -26,27 +26,18 @@ linters:
- 'app/views/admin/reports/show.html.haml' - 'app/views/admin/reports/show.html.haml'
- 'app/views/disputes/strikes/show.html.haml' - 'app/views/disputes/strikes/show.html.haml'
# Offense count: 41 # Offense count: 32
InstanceVariables: InstanceVariables:
exclude: exclude:
- 'app/views/admin/reports/_actions.html.haml' - 'app/views/admin/reports/_actions.html.haml'
- 'app/views/admin/roles/_form.html.haml' - 'app/views/admin/roles/_form.html.haml'
- 'app/views/admin/webhooks/_form.html.haml' - 'app/views/admin/webhooks/_form.html.haml'
- 'app/views/auth/registrations/_sessions.html.haml'
- 'app/views/auth/registrations/_status.html.haml' - 'app/views/auth/registrations/_status.html.haml'
- 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml' - 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml'
- 'app/views/authorize_interactions/_post_follow_actions.html.haml' - 'app/views/authorize_interactions/_post_follow_actions.html.haml'
- 'app/views/invites/_form.html.haml' - 'app/views/invites/_form.html.haml'
- 'app/views/relationships/_account.html.haml' - 'app/views/relationships/_account.html.haml'
- 'app/views/shared/_og.html.haml' - 'app/views/shared/_og.html.haml'
- 'app/views/statuses/_status.html.haml'
# Offense count: 6
ConsecutiveSilentScripts:
exclude:
- 'app/views/admin/settings/shared/_links.html.haml'
- 'app/views/settings/login_activities/_login_activity.html.haml'
- 'app/views/statuses/_poll.html.haml'
# Offense count: 3 # Offense count: 3
IdNames: IdNames:

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.54.1. # using RuboCop version 1.54.2.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -127,12 +127,6 @@ Lint/UselessAssignment:
- 'spec/services/resolve_url_service_spec.rb' - 'spec/services/resolve_url_service_spec.rb'
- 'spec/views/statuses/show.html.haml_spec.rb' - 'spec/views/statuses/show.html.haml_spec.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'spec/services/resolve_account_service_spec.rb'
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
Max: 150 Max: 150
@ -194,7 +188,6 @@ RSpec/AnyInstance:
- 'spec/models/account_spec.rb' - 'spec/models/account_spec.rb'
- 'spec/models/setting_spec.rb' - 'spec/models/setting_spec.rb'
- 'spec/services/activitypub/process_collection_service_spec.rb' - 'spec/services/activitypub/process_collection_service_spec.rb'
- 'spec/validators/blacklisted_email_validator_spec.rb'
- 'spec/validators/follow_limit_validator_spec.rb' - 'spec/validators/follow_limit_validator_spec.rb'
- 'spec/workers/activitypub/delivery_worker_spec.rb' - 'spec/workers/activitypub/delivery_worker_spec.rb'
- 'spec/workers/web/push_notification_worker_spec.rb' - 'spec/workers/web/push_notification_worker_spec.rb'
@ -296,6 +289,7 @@ RSpec/LetSetup:
- 'spec/controllers/oauth/tokens_controller_spec.rb' - 'spec/controllers/oauth/tokens_controller_spec.rb'
- 'spec/controllers/settings/imports_controller_spec.rb' - 'spec/controllers/settings/imports_controller_spec.rb'
- 'spec/lib/activitypub/activity/delete_spec.rb' - 'spec/lib/activitypub/activity/delete_spec.rb'
- 'spec/lib/vacuum/applications_vacuum_spec.rb'
- 'spec/lib/vacuum/preview_cards_vacuum_spec.rb' - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
- 'spec/models/account_spec.rb' - 'spec/models/account_spec.rb'
- 'spec/models/account_statuses_cleanup_policy_spec.rb' - 'spec/models/account_statuses_cleanup_policy_spec.rb'
@ -333,11 +327,7 @@ RSpec/MessageChain:
RSpec/MessageSpies: RSpec/MessageSpies:
Exclude: Exclude:
- 'spec/controllers/admin/accounts_controller_spec.rb' - 'spec/controllers/admin/accounts_controller_spec.rb'
- 'spec/controllers/api/base_controller_spec.rb'
- 'spec/controllers/auth/registrations_controller_spec.rb'
- 'spec/helpers/admin/account_moderation_notes_helper_spec.rb' - 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
- 'spec/helpers/application_helper_spec.rb'
- 'spec/lib/status_finder_spec.rb'
- 'spec/lib/webfinger_resource_spec.rb' - 'spec/lib/webfinger_resource_spec.rb'
- 'spec/models/admin/account_action_spec.rb' - 'spec/models/admin/account_action_spec.rb'
- 'spec/models/concerns/remotable_spec.rb' - 'spec/models/concerns/remotable_spec.rb'
@ -372,7 +362,7 @@ Rails/ApplicationController:
# Configuration parameters: Database, Include. # Configuration parameters: Database, Include.
# SupportedDatabases: mysql, postgresql # SupportedDatabases: mysql, postgresql
# Include: db/migrate/*.rb # Include: db/**/*.rb
Rails/BulkChangeTable: Rails/BulkChangeTable:
Exclude: Exclude:
- 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb' - 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb'
@ -408,7 +398,7 @@ Rails/BulkChangeTable:
- 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb' - 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb'
# Configuration parameters: Include. # Configuration parameters: Include.
# Include: db/migrate/*.rb # Include: db/**/*.rb
Rails/CreateTableWithTimestamps: Rails/CreateTableWithTimestamps:
Exclude: Exclude:
- 'db/migrate/20170508230434_create_conversation_mutes.rb' - 'db/migrate/20170508230434_create_conversation_mutes.rb'
@ -808,7 +798,6 @@ Style/MutableConstant:
Exclude: Exclude:
- 'app/models/tag.rb' - 'app/models/tag.rb'
- 'app/services/delete_account_service.rb' - 'app/services/delete_account_service.rb'
- 'config/initializers/twitter_regex.rb'
- 'lib/mastodon/migration_warning.rb' - 'lib/mastodon/migration_warning.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).

View file

@ -2,6 +2,26 @@
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.1.5] - 2023-07-21
### Added
- Add check preventing Sidekiq workers from running with Makara configured ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25850))
### Changed
- Change request timeout handling to use a longer deadline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26055))
### Fixed
- Fix moderation interface for remote instances with a .zip TLD ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25885))
- Fix remote accounts being possibly persisted to database with incomplete protocol values ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886))
- Fix trending publishers table not rendering correctly on narrow screens ([vmstan](https://github.com/mastodon/mastodon/pull/25945))
### Security
- Fix CSP headers being unintentionally wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26105))
## [4.1.4] - 2023-07-07 ## [4.1.4] - 2023-07-07
### Fixed ### Fixed

View file

@ -18,6 +18,7 @@ gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'fog-core', '<= 2.4.0' gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 0.3', require: false gem 'fog-openstack', '~> 0.3', require: false
gem 'kt-paperclip', '~> 7.2' gem 'kt-paperclip', '~> 7.2'
gem 'md-paperclip-azure', '~> 2.2', require: false
gem 'blurhash', '~> 0.1' gem 'blurhash', '~> 0.1'
gem 'active_model_serializers', '~> 0.10' gem 'active_model_serializers', '~> 0.10'

View file

@ -103,21 +103,29 @@ GEM
attr_required (1.0.1) attr_required (1.0.1)
awrence (1.2.1) awrence (1.2.1)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.780.0) aws-partitions (1.791.0)
aws-sdk-core (3.175.0) aws-sdk-core (3.178.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5) aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.67.0) aws-sdk-kms (1.71.0)
aws-sdk-core (~> 3, >= 3.174.0) aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.126.0) aws-sdk-s3 (1.131.0)
aws-sdk-core (~> 3, >= 3.174.0) aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.6)
aws-sigv4 (1.5.2) aws-sigv4 (1.6.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
azure-storage-blob (2.0.3)
azure-storage-common (~> 2.0)
nokogiri (~> 1, >= 1.10.8)
azure-storage-common (2.0.4)
faraday (~> 1.0)
faraday_middleware (~> 1.0, >= 1.0.0.rc1)
net-http-persistent (~> 4.0)
nokogiri (~> 1, >= 1.10.8)
bcrypt (3.1.18) bcrypt (3.1.18)
better_errors (2.10.1) better_errors (2.10.1)
erubi (>= 1.0.0) erubi (>= 1.0.0)
@ -136,7 +144,7 @@ GEM
blurhash (0.1.7) blurhash (0.1.7)
bootsnap (1.16.0) bootsnap (1.16.0)
msgpack (~> 1.2) msgpack (~> 1.2)
brakeman (6.0.0) brakeman (6.0.1)
browser (5.3.1) browser (5.3.1)
brpoplpush-redis_script (0.1.3) brpoplpush-redis_script (0.1.3)
concurrent-ruby (~> 1.0, >= 1.0.5) concurrent-ruby (~> 1.0, >= 1.0.5)
@ -261,6 +269,8 @@ GEM
faraday-patron (1.0.0) faraday-patron (1.0.0)
faraday-rack (1.0.0) faraday-rack (1.0.0)
faraday-retry (1.0.3) faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fast_blank (1.0.1) fast_blank (1.0.1)
fastimage (2.2.7) fastimage (2.2.7)
ffi (1.15.5) ffi (1.15.5)
@ -297,7 +307,7 @@ GEM
activesupport (>= 5.1) activesupport (>= 5.1)
haml (>= 4.0.6) haml (>= 4.0.6)
railties (>= 5.1) railties (>= 5.1)
haml_lint (0.48.0) haml_lint (0.49.1)
haml (>= 4.0, < 6.2) haml (>= 4.0, < 6.2)
parallel (~> 1.10) parallel (~> 1.10)
rainbow rainbow
@ -410,6 +420,10 @@ GEM
mario-redis-lock (1.2.1) mario-redis-lock (1.2.1)
redis (>= 3.0.5) redis (>= 3.0.5)
matrix (0.4.2) matrix (0.4.2)
md-paperclip-azure (2.2.0)
addressable (~> 2.5)
azure-storage-blob (~> 2.0.1)
hashie (~> 5.0)
memory_profiler (1.0.1) memory_profiler (1.0.1)
method_source (1.0.0) method_source (1.0.0)
mime-types (3.4.1) mime-types (3.4.1)
@ -423,6 +437,8 @@ GEM
multipart-post (2.3.0) multipart-post (2.3.0)
net-http (0.3.2) net-http (0.3.2)
uri uri
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.3.6) net-imap (0.3.6)
date date
net-protocol net-protocol
@ -472,7 +488,7 @@ GEM
openssl-signature_algorithm (1.3.0) openssl-signature_algorithm (1.3.0)
openssl (> 2.0) openssl (> 2.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ox (2.14.16) ox (2.14.17)
parallel (1.23.0) parallel (1.23.0)
parser (3.2.2.3) parser (3.2.2.3)
ast (~> 2.4.1) ast (~> 2.4.1)
@ -493,7 +509,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)
public_suffix (5.0.1) public_suffix (5.0.3)
puma (6.3.0) puma (6.3.0)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.3.0) pundit (2.3.0)
@ -553,7 +569,7 @@ GEM
rake (13.0.6) rake (13.0.6)
rdf (3.2.11) rdf (3.2.11)
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.6.0) rdf-normalize (0.6.1)
rdf (~> 3.2) rdf (~> 3.2)
redcarpet (3.6.0) redcarpet (3.6.0)
redis (4.8.1) redis (4.8.1)
@ -596,7 +612,7 @@ GEM
sidekiq (>= 2.4.0) sidekiq (>= 2.4.0)
rspec-support (3.12.0) rspec-support (3.12.0)
rspec_chunked (0.6) rspec_chunked (0.6)
rubocop (1.54.1) rubocop (1.54.2)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -822,6 +838,7 @@ DEPENDENCIES
link_header (~> 0.0) link_header (~> 0.0)
lograge (~> 0.12) lograge (~> 0.12)
mario-redis-lock (~> 1.2) mario-redis-lock (~> 1.2)
md-paperclip-azure (~> 2.2)
memory_profiler memory_profiler
mime-types (~> 3.4.1) mime-types (~> 3.4.1)
net-http (~> 0.3.2) net-http (~> 0.3.2)

View file

@ -17,13 +17,16 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
if fav if fav
@status = fav.status @status = fav.status
count = [@status.favourites_count - 1, 0].max
UnfavouriteWorker.perform_async(current_account.id, @status.id) UnfavouriteWorker.perform_async(current_account.id, @status.id)
else else
@status = Status.find(params[:status_id]) @status = Status.find(params[:status_id])
count = @status.favourites_count
authorize @status, :show? authorize @status, :show?
end end
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }) relationships = StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false }, attributes_map: { @status.id => { favourites_count: count } })
render json: @status, serializer: REST::StatusSerializer, relationships: relationships
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found
end end

View file

@ -24,15 +24,18 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController
if @status if @status
authorize @status, :unreblog? authorize @status, :unreblog?
@reblog = @status.reblog
count = [@reblog.reblogs_count - 1, 0].max
@status.discard @status.discard
RemovalWorker.perform_async(@status.id) RemovalWorker.perform_async(@status.id)
@reblog = @status.reblog
else else
@reblog = Status.find(params[:status_id]) @reblog = Status.find(params[:status_id])
count = @reblog.reblogs_count
authorize @reblog, :show? authorize @reblog, :show?
end end
render json: @reblog, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }) relationships = StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }, attributes_map: { @reblog.id => { reblogs_count: count } })
render json: @reblog, serializer: REST::StatusSerializer, relationships: relationships
rescue Mastodon::NotPermittedError rescue Mastodon::NotPermittedError
not_found not_found
end end

View file

@ -19,6 +19,7 @@ class Api::V1::TagsController < Api::BaseController
def unfollow def unfollow
TagFollow.find_by(account: current_account, tag: @tag)&.destroy! TagFollow.find_by(account: current_account, tag: @tag)&.destroy!
TagUnmergeWorker.perform_async(@tag.id, current_account.id)
render json: @tag, serializer: REST::TagSerializer render json: @tag, serializer: REST::TagSerializer
end end

View file

@ -108,7 +108,7 @@ class Auth::SessionsController < Devise::SessionsController
end end
def home_paths(resource) def home_paths(resource)
paths = [about_path] paths = [about_path, '/explore']
paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User) paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)

View file

@ -249,6 +249,6 @@ module ApplicationHelper
private private
def storage_host_var def storage_host_var
ENV.fetch('S3_ALIAS_HOST', nil) || ENV.fetch('S3_CLOUDFRONT_HOST', nil) ENV.fetch('S3_ALIAS_HOST', nil) || ENV.fetch('S3_CLOUDFRONT_HOST', nil) || ENV.fetch('AZURE_ALIAS_HOST', nil)
end end
end end

View file

@ -2,10 +2,10 @@
module DatabaseHelper module DatabaseHelper
def with_read_replica(&block) def with_read_replica(&block)
ApplicationRecord.connected_to(role: :read, prevent_writes: true, &block) ApplicationRecord.connected_to(role: :reading, prevent_writes: true, &block)
end end
def with_primary(&block) def with_primary(&block)
ApplicationRecord.connected_to(role: :primary, &block) ApplicationRecord.connected_to(role: :writing, &block)
end end
end end

View file

@ -65,33 +65,6 @@ module StatusesHelper
embedded_view? ? '_blank' : nil embedded_view? ? '_blank' : nil
end end
def style_classes(status, is_predecessor, is_successor, include_threads)
classes = ['entry']
classes << 'entry-predecessor' if is_predecessor
classes << 'entry-reblog' if status.reblog?
classes << 'entry-successor' if is_successor
classes << 'entry-center' if include_threads
classes.join(' ')
end
def microformats_classes(status, is_direct_parent, is_direct_child)
classes = []
classes << 'p-in-reply-to' if is_direct_parent
classes << 'p-repost-of' if status.reblog? && is_direct_parent
classes << 'p-comment' if is_direct_child
classes.join(' ')
end
def microformats_h_class(status, is_predecessor, is_successor, include_threads)
if is_predecessor || status.reblog? || is_successor
'h-cite'
elsif include_threads
''
else
'h-entry'
end
end
def fa_visibility_icon(status) def fa_visibility_icon(status)
case status.visibility case status.visibility
when 'public' when 'public'

View file

@ -23,9 +23,7 @@ export default class ColumnBackButton extends PureComponent {
if (onClick) { if (onClick) {
onClick(); onClick();
// Check if there is a previous page in the app to go back to per https://stackoverflow.com/a/70532858/9703201 } else if (router.history.location?.state?.fromMastodon) {
// When upgrading to V6, check `location.key !== 'default'` instead per https://github.com/remix-run/history/blob/main/docs/api-reference.md#location
} else if (router.route.location.key) {
router.history.goBack(); router.history.goBack();
} else { } else {
router.history.push('/'); router.history.push('/');

View file

@ -63,10 +63,12 @@ class ColumnHeader extends PureComponent {
}; };
handleBackClick = () => { handleBackClick = () => {
if (window.history && window.history.state) { const { router } = this.context;
this.context.router.history.goBack();
if (router.history.location?.state?.fromMastodon) {
router.history.goBack();
} else { } else {
this.context.router.history.push('/'); router.history.push('/');
} }
}; };
@ -83,6 +85,7 @@ class ColumnHeader extends PureComponent {
}; };
render () { render () {
const { router } = this.context;
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props; const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
const { collapsed, animating } = this.state; const { collapsed, animating } = this.state;
@ -126,7 +129,7 @@ class ColumnHeader extends PureComponent {
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>; pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
} }
if (!pinned && (multiColumn || showBackButton)) { if (!pinned && ((multiColumn && router.history.location?.state?.fromMastodon) || showBackButton)) {
backButton = ( backButton = (
<button onClick={this.handleBackClick} className='column-header__back-button'> <button onClick={this.handleBackClick} className='column-header__back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />

View file

@ -297,7 +297,7 @@ export default class Dropdown extends PureComponent {
onKeyPress: this.handleKeyPress, onKeyPress: this.handleKeyPress,
}) : ( }) : (
<IconButton <IconButton
icon={icon} icon={!open ? icon : 'close'}
title={title} title={title}
active={open} active={open}
disabled={disabled} disabled={disabled}

View file

@ -114,7 +114,7 @@ export default class IntersectionObserverArticle extends Component {
aria-setsize={listLength} aria-setsize={listLength}
style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }} style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
data-id={id} data-id={id}
tabIndex={0} tabIndex={-1}
> >
{children && cloneElement(children, { hidden: true })} {children && cloneElement(children, { hidden: true })}
</article> </article>
@ -122,7 +122,7 @@ export default class IntersectionObserverArticle extends Component {
} }
return ( return (
<article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={0}> <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={-1}>
{children && cloneElement(children, { hidden: false })} {children && cloneElement(children, { hidden: false })}
</article> </article>
); );

View file

@ -12,7 +12,7 @@ import { debounce } from 'lodash';
import { Blurhash } from 'mastodon/components/blurhash'; import { Blurhash } from 'mastodon/components/blurhash';
import { autoPlayGif, cropImages, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state'; import { autoPlayGif, displayMedia, displayMediaExpand, useBlurhash } from '../initial_state';
import { IconButton } from './icon_button'; import { IconButton } from './icon_button';
@ -225,7 +225,6 @@ class MediaGallery extends PureComponent {
static propTypes = { static propTypes = {
sensitive: PropTypes.bool, sensitive: PropTypes.bool,
standalone: PropTypes.bool,
media: ImmutablePropTypes.list.isRequired, media: ImmutablePropTypes.list.isRequired,
lang: PropTypes.string, lang: PropTypes.string,
size: PropTypes.object, size: PropTypes.object,
@ -239,10 +238,6 @@ class MediaGallery extends PureComponent {
onToggleVisibility: PropTypes.func, onToggleVisibility: PropTypes.func,
}; };
static defaultProps = {
standalone: false,
};
state = { state = {
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'), visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
width: this.props.defaultWidth, width: this.props.defaultWidth,
@ -311,7 +306,7 @@ class MediaGallery extends PureComponent {
} }
render () { render () {
const { media, lang, intl, sensitive, defaultWidth, standalone, autoplay } = this.props; const { media, lang, intl, sensitive, defaultWidth, autoplay } = this.props;
const { visible } = this.state; const { visible } = this.state;
const width = this.state.width || defaultWidth; const width = this.state.width || defaultWidth;
@ -319,10 +314,10 @@ class MediaGallery extends PureComponent {
const style = {}; const style = {};
if (this.isFullSizeEligible() && (standalone || !cropImages)) { if (this.isFullSizeEligible()) {
style.aspectRatio = `${this.props.media.getIn([0, 'meta', 'small', 'aspect'])}`; style.aspectRatio = `${this.props.media.getIn([0, 'meta', 'small', 'aspect'])}`;
} else { } else {
style.aspectRatio = '16 / 9'; style.aspectRatio = '3 / 2';
} }
const maxSize = displayMediaExpand ? 16 : 4; const maxSize = displayMediaExpand ? 16 : 4;
@ -330,7 +325,7 @@ class MediaGallery extends PureComponent {
const size = media.take(maxSize).size; const size = media.take(maxSize).size;
const uncached = media.every(attachment => attachment.get('type') === 'unknown'); const uncached = media.every(attachment => attachment.get('type') === 'unknown');
if (standalone && this.isFullSizeEligible()) { if (this.isFullSizeEligible()) {
children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />; children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
} else { } else {
children = media.take(maxSize).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />); children = media.take(maxSize).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);

View file

@ -12,6 +12,7 @@ class PictureInPicturePlaceholder extends PureComponent {
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
aspectRatio: PropTypes.string,
}; };
handleClick = () => { handleClick = () => {
@ -20,8 +21,10 @@ class PictureInPicturePlaceholder extends PureComponent {
}; };
render () { render () {
const { aspectRatio } = this.props;
return ( return (
<div className='picture-in-picture-placeholder' role='button' tabIndex={0} onClick={this.handleClick}> <div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}>
<Icon id='window-restore' /> <Icon id='window-restore' />
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' /> <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
</div> </div>

View file

@ -1,16 +1,26 @@
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import React from 'react'; import React from 'react';
import type { History } from 'history';
import { createBrowserHistory } from 'history'; import { createBrowserHistory } from 'history';
import { Router as OriginalRouter } from 'react-router'; import { Router as OriginalRouter } from 'react-router';
import { layoutFromWindow } from 'mastodon/is_mobile'; import { layoutFromWindow } from 'mastodon/is_mobile';
const browserHistory = createBrowserHistory(); interface MastodonLocationState {
const originalPush = browserHistory.push.bind(browserHistory); fromMastodon?: boolean;
mastodonModalKey?: string;
}
const browserHistory = createBrowserHistory<
MastodonLocationState | undefined
>();
const originalPush = browserHistory.push.bind(browserHistory);
const originalReplace = browserHistory.replace.bind(browserHistory);
browserHistory.push = (path: string, state?: MastodonLocationState) => {
state = state ?? {};
state.fromMastodon = true;
browserHistory.push = (path: string, state: History.LocationState) => {
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) { if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
originalPush(`/deck${path}`, state); originalPush(`/deck${path}`, state);
} else { } else {
@ -18,6 +28,19 @@ browserHistory.push = (path: string, state: History.LocationState) => {
} }
}; };
browserHistory.replace = (path: string, state?: MastodonLocationState) => {
if (browserHistory.location.state?.fromMastodon) {
state = state ?? {};
state.fromMastodon = true;
}
if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) {
originalReplace(`/deck${path}`, state);
} else {
originalReplace(path, state);
}
};
export const Router: React.FC<PropsWithChildren> = ({ children }) => { export const Router: React.FC<PropsWithChildren> = ({ children }) => {
return <OriginalRouter history={browserHistory}>{children}</OriginalRouter>; return <OriginalRouter history={browserHistory}>{children}</OriginalRouter>;
}; };

View file

@ -19,7 +19,6 @@ import Bundle from '../features/ui/components/bundle';
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
import { displayMedia } from '../initial_state'; import { displayMedia } from '../initial_state';
import AttachmentList from './attachment_list';
import { Avatar } from './avatar'; import { Avatar } from './avatar';
import { AvatarOverlay } from './avatar_overlay'; import { AvatarOverlay } from './avatar_overlay';
import { DisplayName } from './display_name'; import { DisplayName } from './display_name';
@ -198,17 +197,35 @@ class Status extends ImmutablePureComponent {
this.props.onTranslate(this._properStatus()); this.props.onTranslate(this._properStatus());
}; };
renderLoadingMediaGallery () { getAttachmentAspectRatio () {
return <div className='media-gallery' style={{ height: '110px' }} />; const attachments = this._properStatus().get('media_attachments');
if (attachments.getIn([0, 'type']) === 'video') {
return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`;
} else if (attachments.getIn([0, 'type']) === 'audio') {
return '16 / 9';
} else {
return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2'
}
} }
renderLoadingVideoPlayer () { renderLoadingMediaGallery = () => {
return <div className='video-player' style={{ height: '110px' }} />; return (
} <div className='media-gallery' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
);
};
renderLoadingAudioPlayer () { renderLoadingVideoPlayer = () => {
return <div className='audio-player' style={{ height: '110px' }} />; return (
} <div className='video-player' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
);
};
renderLoadingAudioPlayer = () => {
return (
<div className='audio-player' style={{ aspectRatio: this.getAttachmentAspectRatio() }} />
);
};
handleOpenVideo = (options) => { handleOpenVideo = (options) => {
const status = this._properStatus(); const status = this._properStatus();
@ -445,18 +462,11 @@ class Status extends ImmutablePureComponent {
} }
if (pictureInPicture.get('inUse')) { if (pictureInPicture.get('inUse')) {
media = <PictureInPicturePlaceholder />; media = <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
} else if (status.get('media_attachments').size > 0) { } else if (status.get('media_attachments').size > 0) {
const language = status.getIn(['translation', 'language']) || status.get('language'); const language = status.getIn(['translation', 'language']) || status.get('language');
if (this.props.muted) { if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
media = (
<AttachmentList
compact
media={status.get('media_attachments')}
/>
);
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
@ -494,11 +504,11 @@ class Status extends ImmutablePureComponent {
<Component <Component
preview={attachment.get('preview_url')} preview={attachment.get('preview_url')}
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])} frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${attachment.getIn(['meta', 'original', 'width'])} / ${attachment.getIn(['meta', 'original', 'height'])}`}
blurhash={attachment.get('blurhash')} blurhash={attachment.get('blurhash')}
src={attachment.get('url')} src={attachment.get('url')}
alt={description} alt={description}
lang={language} lang={language}
inline
sensitive={status.get('sensitive')} sensitive={status.get('sensitive')}
onOpenVideo={this.handleOpenVideo} onOpenVideo={this.handleOpenVideo}
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined} deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
@ -527,7 +537,7 @@ class Status extends ImmutablePureComponent {
</Bundle> </Bundle>
); );
} }
} else if (status.get('spoiler_text').length === 0 && status.get('card') && !this.props.muted) { } else if (status.get('spoiler_text').length === 0 && status.get('card')) {
media = ( media = (
<Card <Card
onOpenMedia={this.handleOpenMedia} onOpenMedia={this.handleOpenMedia}

View file

@ -34,7 +34,7 @@ const messages = defineMessages({
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' }, reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
emojiReaction: { id: 'status.emoji_reaction', defaultMessage: 'Stamp' }, emojiReaction: { id: 'status.emoji_reaction', defaultMessage: 'Stamp' },
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' }, bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' }, removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },

View file

@ -57,7 +57,7 @@ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' }, editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },

View file

@ -48,7 +48,7 @@ const messages = defineMessages({
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' }, pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' }, followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },

View file

@ -470,6 +470,7 @@ class Audio extends PureComponent {
const progress = Math.min((currentTime / duration) * 100, 100); const progress = Math.min((currentTime / duration) * 100, 100);
let warning; let warning;
if (sensitive) { if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />; warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else { } else {
@ -515,7 +516,10 @@ class Audio extends PureComponent {
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}> <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}> <button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
<span className='spoiler-button__overlay__label'>{warning}</span> <span className='spoiler-button__overlay__label'>
{warning}
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
</span>
</button> </button>
</div> </div>

View file

@ -86,7 +86,6 @@ class Bookmarks extends ImmutablePureComponent {
onClick={this.handleHeaderClick} onClick={this.handleHeaderClick}
pinned={pinned} pinned={pinned}
multiColumn={multiColumn} multiColumn={multiColumn}
showBackButton
/> />
<StatusList <StatusList

View file

@ -13,7 +13,7 @@ const messages = defineMessages({
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
reaction_deck: { id: 'navigation_bar.reaction_deck', defaultMessage: 'Reaction deck' }, reaction_deck: { id: 'navigation_bar.reaction_deck', defaultMessage: 'Reaction deck' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
emoji_reactions: { id: 'navigation_bar.emoji_reactions', defaultMessage: 'Stamps' }, emoji_reactions: { id: 'navigation_bar.emoji_reactions', defaultMessage: 'Stamps' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' }, followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },

View file

@ -8,7 +8,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
import { IconButton } from '../../../components/icon_button';
import ActionBar from './action_bar'; import ActionBar from './action_bar';
@ -21,23 +20,27 @@ export default class NavigationBar extends ImmutablePureComponent {
}; };
render () { render () {
const username = this.props.account.get('acct')
return ( return (
<div className='navigation-bar'> <div className='navigation-bar'>
<Link to={`/@${this.props.account.get('acct')}`}> <Link to={`/@${username}`}>
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span> <span style={{ display: 'none' }}>{username}</span>
<Avatar account={this.props.account} size={46} /> <Avatar account={this.props.account} size={46} />
</Link> </Link>
<div className='navigation-bar__profile'> <div className='navigation-bar__profile'>
<Link to={`/@${this.props.account.get('acct')}`}> <span>
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong> <Link to={`/@${username}`}>
</Link> <strong className='navigation-bar__profile-account'>@{username}</strong>
</Link>
</span>
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a> <span>
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
</span>
</div> </div>
<div className='navigation-bar__actions'> <div className='navigation-bar__actions'>
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} />
<ActionBar account={this.props.account} onLogout={this.props.onLogout} /> <ActionBar account={this.props.account} onLogout={this.props.onLogout} />
</div> </div>
</div> </div>

View file

@ -1,10 +1,13 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { PureComponent } from 'react'; import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import { Blurhash } from 'mastodon/components/blurhash'; import { Blurhash } from 'mastodon/components/blurhash';
import { accountsCountRenderer } from 'mastodon/components/hashtag'; import { accountsCountRenderer } from 'mastodon/components/hashtag';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import { Skeleton } from 'mastodon/components/skeleton'; import { Skeleton } from 'mastodon/components/skeleton';
@ -13,10 +16,14 @@ export default class Story extends PureComponent {
static propTypes = { static propTypes = {
url: PropTypes.string, url: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
lang: PropTypes.string,
publisher: PropTypes.string, publisher: PropTypes.string,
publishedAt: PropTypes.string,
author: PropTypes.string,
sharedTimes: PropTypes.number, sharedTimes: PropTypes.number,
thumbnail: PropTypes.string, thumbnail: PropTypes.string,
blurhash: PropTypes.string, blurhash: PropTypes.string,
expanded: PropTypes.bool,
}; };
state = { state = {
@ -26,16 +33,16 @@ export default class Story extends PureComponent {
handleImageLoad = () => this.setState({ thumbnailLoaded: true }); handleImageLoad = () => this.setState({ thumbnailLoaded: true });
render () { render () {
const { url, title, publisher, sharedTimes, thumbnail, blurhash } = this.props; const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, blurhash } = this.props;
const { thumbnailLoaded } = this.state; const { thumbnailLoaded } = this.state;
return ( return (
<a className='story' href={url} target='blank' rel='noopener'> <a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'>
<div className='story__details'> <div className='story__details'>
<div className='story__details__publisher'>{publisher ? publisher : <Skeleton width={50} />}</div> <div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div>
<div className='story__details__title'>{title ? title : <Skeleton />}</div> <div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div>
<div className='story__details__shared'>{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div> <div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
</div> </div>
<div className='story__thumbnail'> <div className='story__thumbnail'>

View file

@ -55,12 +55,16 @@ class Links extends PureComponent {
<div className='explore__links'> <div className='explore__links'>
{banner} {banner}
{isLoading ? (<LoadingIndicator />) : links.map(link => ( {isLoading ? (<LoadingIndicator />) : links.map((link, i) => (
<Story <Story
key={link.get('id')} key={link.get('id')}
expanded={i === 0}
lang={link.get('language')}
url={link.get('url')} url={link.get('url')}
title={link.get('title')} title={link.get('title')}
publisher={link.get('provider_name')} publisher={link.get('provider_name')}
publishedAt={link.get('published_at')}
author={link.get('author_name')}
sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1} sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
thumbnail={link.get('image')} thumbnail={link.get('image')}
blurhash={link.get('blurhash')} blurhash={link.get('blurhash')}

View file

@ -47,7 +47,7 @@ class Statuses extends PureComponent {
return ( return (
<> <>
<DismissableBanner id='explore/statuses'> <DismissableBanner id='explore/statuses'>
<FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favourites are ranked higher.' /> <FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.' />
</DismissableBanner> </DismissableBanner>
<StatusList <StatusList

View file

@ -18,7 +18,7 @@ import Column from 'mastodon/features/ui/components/column';
import { getStatusList } from 'mastodon/selectors'; import { getStatusList } from 'mastodon/selectors';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.favourites', defaultMessage: 'Favourites' }, heading: { id: 'column.favourites', defaultMessage: 'Favorites' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -74,7 +74,7 @@ class Favourites extends ImmutablePureComponent {
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite posts yet. When you favourite one, it will show up here." />; const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favorite posts yet. When you favorite one, it will show up here." />;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
@ -86,7 +86,6 @@ class Favourites extends ImmutablePureComponent {
onClick={this.handleHeaderClick} onClick={this.handleHeaderClick}
pinned={pinned} pinned={pinned}
multiColumn={multiColumn} multiColumn={multiColumn}
showBackButton
/> />
<StatusList <StatusList

View file

@ -61,7 +61,7 @@ class Favourites extends ImmutablePureComponent {
); );
} }
const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favourited this post yet. When someone does, they will show up here.' />; const emptyMessage = <FormattedMessage id='empty_column.favourites' defaultMessage='No one has favorited this post yet. When someone does, they will show up here.' />;
return ( return (
<Column bindToDocument={!multiColumn}> <Column bindToDocument={!multiColumn}>

View file

@ -32,7 +32,7 @@ const messages = defineMessages({
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' }, domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },

View file

@ -116,8 +116,8 @@ class InteractionModal extends PureComponent {
break; break;
case 'favourite': case 'favourite':
icon = <Icon id='star' />; icon = <Icon id='star' />;
title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favourite {name}'s post" values={{ name }} />; title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />;
actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.' />; actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />;
break; break;
case 'emoji_reaction': case 'emoji_reaction':
icon = <Icon id='smile-o' />; icon = <Icon id='smile-o' />;
@ -163,7 +163,7 @@ class InteractionModal extends PureComponent {
<div className='interaction-modal__choices__choice'> <div className='interaction-modal__choices__choice'>
<h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3> <h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3>
<p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.' /></p> <p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.' /></p>
<Copypaste value={url} /> <Copypaste value={url} />
</div> </div>
</div> </div>

View file

@ -54,7 +54,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
</tr> </tr>
<tr> <tr>
<td><kbd>f</kbd></td> <td><kbd>f</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.favourite' defaultMessage='to favourite' /></td> <td><FormattedMessage id='keyboard_shortcuts.favourite' defaultMessage='to favorite' /></td>
</tr> </tr>
<tr> <tr>
<td><kbd>b</kbd></td> <td><kbd>b</kbd></td>
@ -138,7 +138,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
</tr> </tr>
<tr> <tr>
<td><kbd>g</kbd>+<kbd>f</kbd></td> <td><kbd>g</kbd>+<kbd>f</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.favourites' defaultMessage='to open favourites list' /></td> <td><FormattedMessage id='keyboard_shortcuts.favourites' defaultMessage='to open favorites list' /></td>
</tr> </tr>
<tr> <tr>
<td><kbd>g</kbd>+<kbd>e</kbd></td> <td><kbd>g</kbd>+<kbd>e</kbd></td>

View file

@ -65,7 +65,7 @@ class Lists extends ImmutablePureComponent {
return ( return (
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}>
<ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} showBackButton /> <ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} />
<NewListForm /> <NewListForm />

View file

@ -109,7 +109,7 @@ export default class ColumnSettings extends PureComponent {
</div> </div>
<div role='group' aria-labelledby='notifications-favourite'> <div role='group' aria-labelledby='notifications-favourite'>
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span> <span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favorites:' /></span>
<div className='column-settings__row'> <div className='column-settings__row'>
<SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} /> <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />

View file

@ -7,7 +7,7 @@ import { Icon } from 'mastodon/components/icon';
const tooltips = defineMessages({ const tooltips = defineMessages({
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' }, mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' }, favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favorites' },
emojiReactions: { id: 'notifications.filter.emoji_reactions', defaultMessage: 'Stamps' }, emojiReactions: { id: 'notifications.filter.emoji_reactions', defaultMessage: 'Stamps' },
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' }, boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
status_references: { id: 'notifications.filter.status_references', defaultMessage: 'Status references' }, status_references: { id: 'notifications.filter.status_references', defaultMessage: 'Status references' },

View file

@ -21,7 +21,7 @@ import FollowRequestContainer from '../containers/follow_request_container';
import Report from './report'; import Report from './report';
const messages = defineMessages({ const messages = defineMessages({
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' }, favourite: { id: 'notification.favourite', defaultMessage: '{name} favorited your status' },
emojiReaction: { id: 'notification.emoji_reaction', defaultMessage: '{name} reacted your status with emoji' }, emojiReaction: { id: 'notification.emoji_reaction', defaultMessage: '{name} reacted your status with emoji' },
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' }, follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' }, ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
@ -201,7 +201,7 @@ class Notification extends ImmutablePureComponent {
</div> </div>
<span title={notification.get('created_at')}> <span title={notification.get('created_at')}>
<FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} /> <FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} />
</span> </span>
</div> </div>

View file

@ -23,7 +23,7 @@ const messages = defineMessages({
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' }, reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
open: { id: 'status.open', defaultMessage: 'Expand this status' }, open: { id: 'status.open', defaultMessage: 'Expand this status' },

View file

@ -26,7 +26,7 @@ const messages = defineMessages({
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' }, reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' }, bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
more: { id: 'status.more', defaultMessage: 'More' }, more: { id: 'status.more', defaultMessage: 'More' },
mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' }, mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },

View file

@ -12,6 +12,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { Blurhash } from 'mastodon/components/blurhash'; import { Blurhash } from 'mastodon/components/blurhash';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { useBlurhash } from 'mastodon/initial_state'; import { useBlurhash } from 'mastodon/initial_state';
const IDNA_PREFIX = 'xn--'; const IDNA_PREFIX = 'xn--';
@ -57,14 +58,9 @@ export default class Card extends PureComponent {
static propTypes = { static propTypes = {
card: ImmutablePropTypes.map, card: ImmutablePropTypes.map,
onOpenMedia: PropTypes.func.isRequired, onOpenMedia: PropTypes.func.isRequired,
compact: PropTypes.bool,
sensitive: PropTypes.bool, sensitive: PropTypes.bool,
}; };
static defaultProps = {
compact: false,
};
state = { state = {
previewLoaded: false, previewLoaded: false,
embedded: false, embedded: false,
@ -148,7 +144,7 @@ export default class Card extends PureComponent {
} }
render () { render () {
const { card, compact } = this.props; const { card } = this.props;
const { embedded, revealed } = this.state; const { embedded, revealed } = this.state;
if (card === null) { if (card === null) {
@ -156,29 +152,27 @@ export default class Card extends PureComponent {
} }
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name'); const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
const horizontal = (!compact && card.get('width') > card.get('height')) || card.get('type') !== 'link' || embedded;
const interactive = card.get('type') !== 'link'; const interactive = card.get('type') !== 'link';
const className = classnames('status-card', { horizontal, compact, interactive });
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
const language = card.get('language') || ''; const language = card.get('language') || '';
const description = ( const description = (
<div className='status-card__content' lang={language}> <div className='status-card__content'>
{title} <span className='status-card__host'>
{!(horizontal || compact) && <p className='status-card__description' title={card.get('description')}>{card.get('description')}</p>} <span lang={language}>{provider}</span>
<span className='status-card__host'>{provider}</span> {card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>}
</span>
<strong className='status-card__title' title={card.get('title')} lang={language}>{card.get('title')}</strong>
{card.get('author_name').length > 0 && <span className='status-card__author'><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{card.get('author_name')}</strong> }} /></span>}
</div> </div>
); );
const thumbnailStyle = { const thumbnailStyle = {
visibility: revealed? null : 'hidden', visibility: revealed ? null : 'hidden',
aspectRatio: `${card.get('width')} / ${card.get('height')}`
}; };
if (horizontal) { let embed;
thumbnailStyle.aspectRatio = (compact && !embedded) ? '16 / 9' : `${card.get('width')} / ${card.get('height')}`;
}
let embed = '';
let canvas = ( let canvas = (
<Blurhash <Blurhash
className={classnames('status-card__image-preview', { className={classnames('status-card__image-preview', {
@ -188,12 +182,18 @@ export default class Card extends PureComponent {
dummy={!useBlurhash} dummy={!useBlurhash}
/> />
); );
let thumbnail = <img src={card.get('image')} alt='' style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />; let thumbnail = <img src={card.get('image')} alt='' style={thumbnailStyle} onLoad={this.handleImageLoad} className='status-card__image-image' />;
let spoilerButton = ( let spoilerButton = (
<button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'> <button type='button' onClick={this.handleReveal} className='spoiler-button__overlay'>
<span className='spoiler-button__overlay__label'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span> <span className='spoiler-button__overlay__label'>
<FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
</span>
</button> </button>
); );
spoilerButton = ( spoilerButton = (
<div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}> <div className={classnames('spoiler-button', { 'spoiler-button--minified': revealed })}>
{spoilerButton} {spoilerButton}
@ -219,19 +219,20 @@ export default class Card extends PureComponent {
<div className='status-card__actions'> <div className='status-card__actions'>
<div> <div>
<button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button> <button type='button' onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>} <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a>
</div> </div>
</div> </div>
)} )}
{!revealed && spoilerButton} {!revealed && spoilerButton}
</div> </div>
); );
} }
return ( return (
<div className={className} ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}> <div className='status-card' ref={this.setRef} onClick={revealed ? null : this.handleReveal} role={revealed ? 'button' : null}>
{embed} {embed}
{!compact && description} <a href={card.get('url')} target='_blank' rel='noopener noreferrer'>{description}</a>
</div> </div>
); );
} else if (card.get('image')) { } else if (card.get('image')) {
@ -243,14 +244,14 @@ export default class Card extends PureComponent {
); );
} else { } else {
embed = ( embed = (
<div className='status-card__image'> <div className='status-card__image' style={{ aspectRatio: '1.9 / 1' }}>
<Icon id='file-text' /> <Icon id='file-text' />
</div> </div>
); );
} }
return ( return (
<a href={card.get('url')} className={className} target='_blank' rel='noopener noreferrer' ref={this.setRef}> <a href={card.get('url')} className='status-card' target='_blank' rel='noopener noreferrer' ref={this.setRef}>
{embed} {embed}
{description} {description}
</a> </a>

View file

@ -122,8 +122,30 @@ class DetailedStatus extends ImmutablePureComponent {
onTranslate(status); onTranslate(status);
}; };
_properStatus () {
const { status } = this.props;
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
return status.get('reblog');
} else {
return status;
}
}
getAttachmentAspectRatio () {
const attachments = this._properStatus().get('media_attachments');
if (attachments.getIn([0, 'type']) === 'video') {
return `${attachments.getIn([0, 'meta', 'original', 'width'])} / ${attachments.getIn([0, 'meta', 'original', 'height'])}`;
} else if (attachments.getIn([0, 'type']) === 'audio') {
return '16 / 9';
} else {
return (attachments.size === 1 && attachments.getIn([0, 'meta', 'small', 'aspect'])) ? attachments.getIn([0, 'meta', 'small', 'aspect']) : '3 / 2'
}
}
render () { render () {
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const status = this._properStatus();
const outerStyle = { boxSizing: 'border-box' }; const outerStyle = { boxSizing: 'border-box' };
const { intl, compact, pictureInPicture } = this.props; const { intl, compact, pictureInPicture } = this.props;
@ -147,7 +169,7 @@ class DetailedStatus extends ImmutablePureComponent {
const language = status.getIn(['translation', 'language']) || status.get('language'); const language = status.getIn(['translation', 'language']) || status.get('language');
if (pictureInPicture.get('inUse')) { if (pictureInPicture.get('inUse')) {
media = <PictureInPicturePlaceholder />; media = <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
} else if (status.get('media_attachments').size > 0) { } else if (status.get('media_attachments').size > 0) {
if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const attachment = status.getIn(['media_attachments', 0]); const attachment = status.getIn(['media_attachments', 0]);
@ -178,13 +200,13 @@ class DetailedStatus extends ImmutablePureComponent {
<Video <Video
preview={attachment.get('preview_url')} preview={attachment.get('preview_url')}
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])} frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${attachment.getIn(['meta', 'original', 'width'])} / ${attachment.getIn(['meta', 'original', 'height'])}`}
blurhash={attachment.get('blurhash')} blurhash={attachment.get('blurhash')}
src={attachment.get('url')} src={attachment.get('url')}
alt={description} alt={description}
lang={language} lang={language}
width={300} width={300}
height={150} height={150}
inline
onOpenVideo={this.handleOpenVideo} onOpenVideo={this.handleOpenVideo}
sensitive={status.get('sensitive')} sensitive={status.get('sensitive')}
visible={this.props.showMedia} visible={this.props.showMedia}

View file

@ -38,7 +38,7 @@ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
}); });

View file

@ -75,7 +75,7 @@ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' }, redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' }, redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.' },
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' }, revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' }, hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
statusTitleWithAttachments: { id: 'status.title.with_attachments', defaultMessage: '{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}' }, statusTitleWithAttachments: { id: 'status.title.with_attachments', defaultMessage: '{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}' },
@ -208,9 +208,9 @@ class Status extends ImmutablePureComponent {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
ancestorsIds: ImmutablePropTypes.list, ancestorsIds: ImmutablePropTypes.list.isRequired,
descendantsIds: ImmutablePropTypes.list, descendantsIds: ImmutablePropTypes.list.isRequired,
referenceIds: ImmutablePropTypes.list, referenceIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
askReplyConfirmation: PropTypes.bool, askReplyConfirmation: PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
@ -237,14 +237,9 @@ class Status extends ImmutablePureComponent {
UNSAFE_componentWillReceiveProps (nextProps) { UNSAFE_componentWillReceiveProps (nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) { if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this._scrolledIntoView = false;
this.props.dispatch(fetchStatus(nextProps.params.statusId)); this.props.dispatch(fetchStatus(nextProps.params.statusId));
} }
if (nextProps.params.statusId && nextProps.ancestorsIds.size > this.props.ancestorsIds.size) {
this._scrolledIntoView = false;
}
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) { if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') }); this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
} }
@ -625,20 +620,23 @@ class Status extends ImmutablePureComponent {
this.node = c; this.node = c;
}; };
componentDidUpdate () { componentDidUpdate (prevProps) {
if (this._scrolledIntoView) { const { status, ancestorsIds, multiColumn } = this.props;
return;
}
const { status, ancestorsIds } = this.props;
if (status && ancestorsIds && ancestorsIds.size > 0) {
const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
if (status && (ancestorsIds.size > prevProps.ancestorsIds.size || prevProps.status?.get('id') !== status.get('id'))) {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
element.scrollIntoView(true); this.node?.querySelector('.detailed-status__wrapper')?.scrollIntoView(true);
// In the single-column interface, `scrollIntoView` will put the post behind the header,
// so compensate for that.
if (!multiColumn) {
const offset = document.querySelector('.column-header__wrapper')?.getBoundingClientRect()?.bottom;
if (offset) {
const scrollingElement = document.scrollingElement || document.body;
scrollingElement.scrollBy(0, -offset);
}
}
}); });
this._scrolledIntoView = true;
} }
} }

View file

@ -172,6 +172,7 @@ class MediaModal extends ImmutablePureComponent {
width={image.get('width')} width={image.get('width')}
height={image.get('height')} height={image.get('height')}
frameRate={image.getIn(['meta', 'original', 'frame_rate'])} frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${image.getIn(['meta', 'original', 'width'])} / ${image.getIn(['meta', 'original', 'height'])}`}
currentTime={currentTime || 0} currentTime={currentTime || 0}
autoPlay={autoPlay || false} autoPlay={autoPlay || false}
volume={volume || 1} volume={volume || 1}

View file

@ -24,7 +24,7 @@ const messages = defineMessages({
local: { id: 'column.local', defaultMessage: 'Local' }, local: { id: 'column.local', defaultMessage: 'Local' },
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' }, firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' }, direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },

View file

@ -35,7 +35,7 @@ const SignInBanner = () => {
return ( return (
<div className='sign-in-banner'> <div className='sign-in-banner'>
<p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.' /></p> <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
{signupButton} {signupButton}
<a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a> <a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
</div> </div>

View file

@ -49,6 +49,7 @@ class VideoModal extends ImmutablePureComponent {
<Video <Video
preview={media.get('preview_url')} preview={media.get('preview_url')}
frameRate={media.getIn(['meta', 'original', 'frame_rate'])} frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${media.getIn(['meta', 'original', 'width'])} / ${media.getIn(['meta', 'original', 'height'])}`}
blurhash={media.get('blurhash')} blurhash={media.get('blurhash')}
src={media.get('url')} src={media.get('url')}
currentTime={options.startTime} currentTime={options.startTime}

View file

@ -190,6 +190,7 @@ class SwitchingColumnsArea extends PureComponent {
{singleColumn ? <Redirect from='/deck' to='/home' exact /> : null} {singleColumn ? <Redirect from='/deck' to='/home' exact /> : null}
{singleColumn && pathName.startsWith('/deck/') ? <Redirect from={pathName} to={pathName.slice(5)} /> : null} {singleColumn && pathName.startsWith('/deck/') ? <Redirect from={pathName} to={pathName.slice(5)} /> : null}
{!singleColumn && pathName === '/getting-started' ? <Redirect from='/getting-started' to='/deck/getting-started' exact /> : null}
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} /> <WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} /> <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
@ -493,10 +494,12 @@ class UI extends PureComponent {
}; };
handleHotkeyBack = () => { handleHotkeyBack = () => {
if (window.history && window.history.state) { const { router } = this.context;
this.context.router.history.goBack();
if (router.history.location?.state?.fromMastodon) {
router.history.goBack();
} else { } else {
this.context.router.history.push('/'); router.history.push('/');
} }
}; };

View file

@ -105,6 +105,7 @@ class Video extends PureComponent {
static propTypes = { static propTypes = {
preview: PropTypes.string, preview: PropTypes.string,
frameRate: PropTypes.string, frameRate: PropTypes.string,
aspectRatio: PropTypes.string,
src: PropTypes.string.isRequired, src: PropTypes.string.isRequired,
alt: PropTypes.string, alt: PropTypes.string,
lang: PropTypes.string, lang: PropTypes.string,
@ -113,7 +114,6 @@ class Video extends PureComponent {
onOpenVideo: PropTypes.func, onOpenVideo: PropTypes.func,
onCloseVideo: PropTypes.func, onCloseVideo: PropTypes.func,
detailed: PropTypes.bool, detailed: PropTypes.bool,
inline: PropTypes.bool,
editable: PropTypes.bool, editable: PropTypes.bool,
alwaysVisible: PropTypes.bool, alwaysVisible: PropTypes.bool,
visible: PropTypes.bool, visible: PropTypes.bool,
@ -500,14 +500,9 @@ class Video extends PureComponent {
} }
render () { render () {
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props; const { preview, src, aspectRatio, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; const { currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100); const progress = Math.min((currentTime / duration) * 100, 100);
const playerStyle = {};
if (inline) {
playerStyle.aspectRatio = '16 / 9';
}
let preload; let preload;
@ -527,95 +522,101 @@ class Video extends PureComponent {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />; warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
} }
// The outer wrapper is necessary to avoid reflowing the layout when going into full screen
return ( return (
<div <div style={{ aspectRatio }}>
role='menuitem' <div
className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, editable })} role='menuitem'
style={playerStyle} className={classNames('video-player', { inactive: !revealed, detailed, fullscreen, editable })}
ref={this.setPlayerRef} style={{ aspectRatio }}
onMouseEnter={this.handleMouseEnter} ref={this.setPlayerRef}
onMouseLeave={this.handleMouseLeave} onMouseEnter={this.handleMouseEnter}
onClick={this.handleClickRoot} onMouseLeave={this.handleMouseLeave}
onKeyDown={this.handleKeyDown} onClick={this.handleClickRoot}
tabIndex={0} onKeyDown={this.handleKeyDown}
>
<Blurhash
hash={blurhash}
className={classNames('media-gallery__preview', {
'media-gallery__preview--hidden': revealed,
})}
dummy={!useBlurhash}
/>
{(revealed || editable) && <video
ref={this.setVideoRef}
src={src}
poster={preview}
preload={preload}
role='button'
tabIndex={0} tabIndex={0}
aria-label={alt} >
title={alt} <Blurhash
lang={lang} hash={blurhash}
volume={volume} className={classNames('media-gallery__preview', {
onClick={this.togglePlay} 'media-gallery__preview--hidden': revealed,
onKeyDown={this.handleVideoKeyDown} })}
onPlay={this.handlePlay} dummy={!useBlurhash}
onPause={this.handlePause} />
onLoadedData={this.handleLoadedData}
onProgress={this.handleProgress}
onVolumeChange={this.handleVolumeChange}
style={{ ...playerStyle, width: '100%' }}
/>}
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}> {(revealed || editable) && <video
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}> ref={this.setVideoRef}
<span className='spoiler-button__overlay__label'>{warning}</span> src={src}
</button> poster={preview}
</div> preload={preload}
role='button'
tabIndex={0}
aria-label={alt}
title={alt}
lang={lang}
volume={volume}
onClick={this.togglePlay}
onKeyDown={this.handleVideoKeyDown}
onPlay={this.handlePlay}
onPause={this.handlePause}
onLoadedData={this.handleLoadedData}
onProgress={this.handleProgress}
onVolumeChange={this.handleVolumeChange}
style={{ width: '100%' }}
/>}
<div className={classNames('video-player__controls', { active: paused || hovered })}> <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}> <button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} /> <span className='spoiler-button__overlay__label'>
<div className='video-player__seek__progress' style={{ width: `${progress}%` }} /> {warning}
<span className='spoiler-button__overlay__action'><FormattedMessage id='status.media.show' defaultMessage='Click to show' /></span>
<span </span>
className={classNames('video-player__seek__handle', { active: dragging })} </button>
tabIndex={0}
style={{ left: `${progress}%` }}
onKeyDown={this.handleVideoKeyDown}
/>
</div> </div>
<div className='video-player__buttons-bar'> <div className={classNames('video-player__controls', { active: paused || hovered })}>
<div className='video-player__buttons left'> <div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button> <div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> <div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}> <span
<div className='video-player__volume__current' style={{ width: `${volume * 100}%` }} /> className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex={0}
<span style={{ left: `${progress}%` }}
className={classNames('video-player__volume__handle')} onKeyDown={this.handleVideoKeyDown}
tabIndex={0} />
style={{ left: `${volume * 100}%` }}
/>
</div>
{(detailed || fullscreen) && (
<span className='video-player__time'>
<span className='video-player__time-current'>{formatTime(Math.floor(currentTime))}</span>
<span className='video-player__time-sep'>/</span>
<span className='video-player__time-total'>{formatTime(Math.floor(duration))}</span>
</span>
)}
</div> </div>
<div className='video-player__buttons right'> <div className='video-player__buttons-bar'>
{(!onCloseVideo && !editable && !fullscreen && !this.props.alwaysVisible) && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} <div className='video-player__buttons left'>
{(!fullscreen && onOpenVideo) && <button type='button' title={intl.formatMessage(messages.expand)} aria-label={intl.formatMessage(messages.expand)} className='player-button' onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>} <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
{onCloseVideo && <button type='button' title={intl.formatMessage(messages.close)} aria-label={intl.formatMessage(messages.close)} className='player-button' onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>} <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
<button type='button' title={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} className='player-button' onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
<div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
<div className='video-player__volume__current' style={{ width: `${volume * 100}%` }} />
<span
className={classNames('video-player__volume__handle')}
tabIndex={0}
style={{ left: `${volume * 100}%` }}
/>
</div>
{(detailed || fullscreen) && (
<span className='video-player__time'>
<span className='video-player__time-current'>{formatTime(Math.floor(currentTime))}</span>
<span className='video-player__time-sep'>/</span>
<span className='video-player__time-total'>{formatTime(Math.floor(duration))}</span>
</span>
)}
</div>
<div className='video-player__buttons right'>
{(!onCloseVideo && !editable && !fullscreen && !this.props.alwaysVisible) && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
{(!fullscreen && onOpenVideo) && <button type='button' title={intl.formatMessage(messages.expand)} aria-label={intl.formatMessage(messages.expand)} className='player-button' onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
{onCloseVideo && <button type='button' title={intl.formatMessage(messages.close)} aria-label={intl.formatMessage(messages.close)} className='player-button' onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
<button type='button' title={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} className='player-button' onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -51,7 +51,6 @@
* @property {boolean} activity_api_enabled * @property {boolean} activity_api_enabled
* @property {string} admin * @property {string} admin
* @property {boolean=} boost_modal * @property {boolean=} boost_modal
* @property {boolean} crop_images
* @property {boolean=} delete_modal * @property {boolean=} delete_modal
* @property {boolean=} disable_swiping * @property {boolean=} disable_swiping
* @property {string=} disabled_account_id * @property {string=} disabled_account_id
@ -114,7 +113,6 @@ const getMeta = (prop) => initialState?.meta && initialState.meta[prop];
export const activityApiEnabled = getMeta('activity_api_enabled'); export const activityApiEnabled = getMeta('activity_api_enabled');
export const autoPlayGif = getMeta('auto_play_gif'); export const autoPlayGif = getMeta('auto_play_gif');
export const boostModal = getMeta('boost_modal'); export const boostModal = getMeta('boost_modal');
export const cropImages = getMeta('crop_images');
export const deleteModal = getMeta('delete_modal'); export const deleteModal = getMeta('delete_modal');
export const disableSwiping = getMeta('disable_swiping'); export const disableSwiping = getMeta('disable_swiping');
export const disabledAccountId = getMeta('disabled_account_id'); export const disabledAccountId = getMeta('disabled_account_id');

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Схаваць апавяшчэнні ад гэтага карыстальніка?", "mute_modal.hide_notifications": "Схаваць апавяшчэнні ад гэтага карыстальніка?",
"mute_modal.indefinite": "Бестэрмінова", "mute_modal.indefinite": "Бестэрмінова",
"navigation_bar.about": "Пра нас", "navigation_bar.about": "Пра нас",
"navigation_bar.advanced_interface": "Ireki web interfaze aurreratuan",
"navigation_bar.blocks": "Заблакаваныя карыстальнікі", "navigation_bar.blocks": "Заблакаваныя карыстальнікі",
"navigation_bar.bookmarks": "Закладкі", "navigation_bar.bookmarks": "Закладкі",
"navigation_bar.community_timeline": "Лакальная стужка", "navigation_bar.community_timeline": "Лакальная стужка",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Скривате ли известията от потребителя?", "mute_modal.hide_notifications": "Скривате ли известията от потребителя?",
"mute_modal.indefinite": "Неопределено", "mute_modal.indefinite": "Неопределено",
"navigation_bar.about": "Относно", "navigation_bar.about": "Относно",
"navigation_bar.advanced_interface": "Отваряне в разширен уебинтерфейс",
"navigation_bar.blocks": "Блокирани потребители", "navigation_bar.blocks": "Блокирани потребители",
"navigation_bar.bookmarks": "Отметки", "navigation_bar.bookmarks": "Отметки",
"navigation_bar.community_timeline": "Локална часова ос", "navigation_bar.community_timeline": "Локална часова ос",

View file

@ -17,6 +17,7 @@
"account.badges.group": "দল", "account.badges.group": "দল",
"account.block": "@{name} কে ব্লক করো", "account.block": "@{name} কে ব্লক করো",
"account.block_domain": "{domain} থেকে সব লুকাও", "account.block_domain": "{domain} থেকে সব লুকাও",
"account.block_short": "অবরোধ",
"account.blocked": "অবরুদ্ধ", "account.blocked": "অবরুদ্ধ",
"account.browse_more_on_origin_server": "মূল প্রোফাইলটিতে আরও ব্রাউজ করুন", "account.browse_more_on_origin_server": "মূল প্রোফাইলটিতে আরও ব্রাউজ করুন",
"account.cancel_follow_request": "অনুসরণ অনুরোধ প্রত্যাহার করুন", "account.cancel_follow_request": "অনুসরণ অনুরোধ প্রত্যাহার করুন",
@ -48,7 +49,10 @@
"account.mention": "@{name} কে উল্লেখ করুন", "account.mention": "@{name} কে উল্লেখ করুন",
"account.moved_to": "{name} নির্দেশ করেছে যে তাদের নতুন অ্যাকাউন্ট এখন হলো:", "account.moved_to": "{name} নির্দেশ করেছে যে তাদের নতুন অ্যাকাউন্ট এখন হলো:",
"account.mute": "@{name} কে নিঃশব্দ করুন", "account.mute": "@{name} কে নিঃশব্দ করুন",
"account.mute_notifications_short": "বিজ্ঞপ্তি নিংশব্দ",
"account.mute_short": "নিঃশব্দ",
"account.muted": "নিঃশব্দ", "account.muted": "নিঃশব্দ",
"account.no_bio": "কোনো বর্ণনা দেওয়া হয়নি।",
"account.open_original_page": "মূল পৃষ্ঠা খুলুন", "account.open_original_page": "মূল পৃষ্ঠা খুলুন",
"account.posts": "টুট", "account.posts": "টুট",
"account.posts_with_replies": "টুট এবং মতামত", "account.posts_with_replies": "টুট এবং মতামত",
@ -64,6 +68,7 @@
"account.unendorse": "আপনার নিজের পাতায় এটা দেখবেন না", "account.unendorse": "আপনার নিজের পাতায় এটা দেখবেন না",
"account.unfollow": "অনুসরণ করো না", "account.unfollow": "অনুসরণ করো না",
"account.unmute": "@{name} র কার্যকলাপ আবার দেখুন", "account.unmute": "@{name} র কার্যকলাপ আবার দেখুন",
"account.unmute_notifications_short": "বিজ্ঞপ্তি শব্দ চালু করো",
"account.unmute_short": "আনমিউট করুন", "account.unmute_short": "আনমিউট করুন",
"account_note.placeholder": "নোট যোগ করতে ক্লিক করুন", "account_note.placeholder": "নোট যোগ করতে ক্লিক করুন",
"admin.dashboard.daily_retention": "সাইন আপের পর দিনে ব্যবহারকারীর ধরে রাখার হার", "admin.dashboard.daily_retention": "সাইন আপের পর দিনে ব্যবহারকারীর ধরে রাখার হার",
@ -353,6 +358,7 @@
"onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile", "onboarding.steps.share_profile.title": "Share your profile",
"onboarding.tips.accounts_from_other_servers": "<strong>তুমি কি জানতে?</strong> যেহেতু মাস্টোডন বিকেন্দ্রীভূত, কিছু অ্যাকাউন্ট তোমার নিজের ছাড়া অন্য কোনো সার্ভারে থাকতে পারে। অথচ তুমি তাদের সাথে কোনো সমস্যা ছাড়াই কথা বলতে পারছো! তাদের সার্ভার তাদের ব্যবহারকারী নামের দ্বিতীয় অর্ধাংশ!",
"poll.closed": "বন্ধ", "poll.closed": "বন্ধ",
"poll.refresh": "বদলেছে কিনা দেখতে", "poll.refresh": "বদলেছে কিনা দেখতে",
"poll.total_people": "{count, plural, one {# ব্যক্তি} other {# ব্যক্তি}}", "poll.total_people": "{count, plural, one {# ব্যক্তি} other {# ব্যক্তি}}",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Cuddio hysbysiadau gan y defnyddiwr hwn?", "mute_modal.hide_notifications": "Cuddio hysbysiadau gan y defnyddiwr hwn?",
"mute_modal.indefinite": "Parhaus", "mute_modal.indefinite": "Parhaus",
"navigation_bar.about": "Ynghylch", "navigation_bar.about": "Ynghylch",
"navigation_bar.advanced_interface": "Abrir coa interface web avanzada",
"navigation_bar.blocks": "Defnyddwyr wedi eu blocio", "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
"navigation_bar.bookmarks": "Llyfrnodau", "navigation_bar.bookmarks": "Llyfrnodau",
"navigation_bar.community_timeline": "Ffrwd leol", "navigation_bar.community_timeline": "Ffrwd leol",

View file

@ -17,6 +17,7 @@
"account.badges.group": "Gruppe", "account.badges.group": "Gruppe",
"account.block": "Blokér @{name}", "account.block": "Blokér @{name}",
"account.block_domain": "Blokér domænet {domain}", "account.block_domain": "Blokér domænet {domain}",
"account.block_short": "Bloker",
"account.blocked": "Blokeret", "account.blocked": "Blokeret",
"account.browse_more_on_origin_server": "Se mere på den oprindelige profil", "account.browse_more_on_origin_server": "Se mere på den oprindelige profil",
"account.cancel_follow_request": "Annullér anmodning om at følge", "account.cancel_follow_request": "Annullér anmodning om at følge",
@ -48,7 +49,10 @@
"account.mention": "Nævn @{name}", "account.mention": "Nævn @{name}",
"account.moved_to": "{name} har angivet, at vedkommendes nye konto nu er:", "account.moved_to": "{name} har angivet, at vedkommendes nye konto nu er:",
"account.mute": "Skjul @{name}", "account.mute": "Skjul @{name}",
"account.mute_notifications_short": "Slå lyden fra for notifikationer",
"account.mute_short": "Skjul (mute)",
"account.muted": "Skjult (muted)", "account.muted": "Skjult (muted)",
"account.no_bio": "Ingen beskrivelse til rådighed.",
"account.open_original_page": "Åbn oprindelig side", "account.open_original_page": "Åbn oprindelig side",
"account.posts": "Indlæg", "account.posts": "Indlæg",
"account.posts_with_replies": "Indlæg og svar", "account.posts_with_replies": "Indlæg og svar",
@ -64,6 +68,7 @@
"account.unendorse": "Fjern visning på din profil", "account.unendorse": "Fjern visning på din profil",
"account.unfollow": "Følg ikke længere", "account.unfollow": "Følg ikke længere",
"account.unmute": "Vis @{name} igen (unmute)", "account.unmute": "Vis @{name} igen (unmute)",
"account.unmute_notifications_short": "Slå lyden fra for notifikationer",
"account.unmute_short": "Vis igen (unmute)", "account.unmute_short": "Vis igen (unmute)",
"account_note.placeholder": "Klik for at tilføje notat", "account_note.placeholder": "Klik for at tilføje notat",
"admin.dashboard.daily_retention": "Brugerfastholdelsesrate per dag efter tilmelding", "admin.dashboard.daily_retention": "Brugerfastholdelsesrate per dag efter tilmelding",
@ -71,6 +76,10 @@
"admin.dashboard.retention.average": "Gennemsnitlig", "admin.dashboard.retention.average": "Gennemsnitlig",
"admin.dashboard.retention.cohort": "Tilmeldingsmåned", "admin.dashboard.retention.cohort": "Tilmeldingsmåned",
"admin.dashboard.retention.cohort_size": "Nye brugere", "admin.dashboard.retention.cohort_size": "Nye brugere",
"admin.impact_report.instance_accounts": "Konti profiler, som dette ville slette",
"admin.impact_report.instance_followers": "Følgere vores brugere ville miste",
"admin.impact_report.instance_follows": "Følgere deres brugere ville miste",
"admin.impact_report.title": "Resumé af virkninger",
"alert.rate_limited.message": "Forsøg igen efter {retry_time, time, medium}.", "alert.rate_limited.message": "Forsøg igen efter {retry_time, time, medium}.",
"alert.rate_limited.title": "Hastighedsbegrænset", "alert.rate_limited.title": "Hastighedsbegrænset",
"alert.unexpected.message": "En uventet fejl opstod.", "alert.unexpected.message": "En uventet fejl opstod.",
@ -266,7 +275,7 @@
"firehose.remote": "Andre servere", "firehose.remote": "Andre servere",
"follow_request.authorize": "Godkend", "follow_request.authorize": "Godkend",
"follow_request.reject": "Afvis", "follow_request.reject": "Afvis",
"follow_requests.unlocked_explanation": "Selvom din konto ikke er låst, antog {domain}-personalet, at du måske vil gennemgå dine anmodninger manuelt.", "follow_requests.unlocked_explanation": "Selvom din konto ikke er låst, synes {domain}-personalet, du måske bør gennemgå disse anmodninger manuelt.",
"followed_tags": "Hashtag, som følges", "followed_tags": "Hashtag, som følges",
"footer.about": "Om", "footer.about": "Om",
"footer.directory": "Profiloversigt", "footer.directory": "Profiloversigt",
@ -289,9 +298,13 @@
"hashtag.column_settings.tag_toggle": "Inkludér ekstra tags for denne kolonne", "hashtag.column_settings.tag_toggle": "Inkludér ekstra tags for denne kolonne",
"hashtag.follow": "Følg hashtag", "hashtag.follow": "Følg hashtag",
"hashtag.unfollow": "Stop med at følge hashtag", "hashtag.unfollow": "Stop med at følge hashtag",
"home.actions.go_to_explore": "Se, hvad som trender",
"home.actions.go_to_suggestions": "Find nogle personer at følge",
"home.column_settings.basic": "Grundlæggende", "home.column_settings.basic": "Grundlæggende",
"home.column_settings.show_reblogs": "Vis boosts", "home.column_settings.show_reblogs": "Vis boosts",
"home.column_settings.show_replies": "Vis svar", "home.column_settings.show_replies": "Vis svar",
"home.explore_prompt.body": "Dit hjem feed vil have en blanding af indlæg fra de hashtags du har valgt at følge, de personer, du har valgt at følge, og de indlæg, de booste. Det ser temmelig stille lige nu, så hvordan vi:",
"home.explore_prompt.title": "Dette er din hjemmebase i Mastodon.",
"home.hide_announcements": "Skjul bekendtgørelser", "home.hide_announcements": "Skjul bekendtgørelser",
"home.show_announcements": "Vis bekendtgørelser", "home.show_announcements": "Vis bekendtgørelser",
"interaction_modal.description.favourite": "Med en konto på Mastodon kan dette indlæg gøres til favorit for at lade forfatteren vide, at det værdsættes, samt gemme det til senere.", "interaction_modal.description.favourite": "Med en konto på Mastodon kan dette indlæg gøres til favorit for at lade forfatteren vide, at det værdsættes, samt gemme det til senere.",
@ -355,6 +368,7 @@
"lists.delete": "Slet liste", "lists.delete": "Slet liste",
"lists.edit": "Redigér liste", "lists.edit": "Redigér liste",
"lists.edit.submit": "Skift titel", "lists.edit.submit": "Skift titel",
"lists.exclusive": "Skjul disse indlæg hjemmefra",
"lists.new.create": "Tilføj liste", "lists.new.create": "Tilføj liste",
"lists.new.title_placeholder": "Ny listetitel", "lists.new.title_placeholder": "Ny listetitel",
"lists.replies_policy.followed": "Enhver bruger, der følges", "lists.replies_policy.followed": "Enhver bruger, der følges",
@ -371,6 +385,7 @@
"mute_modal.hide_notifications": "Skjul notifikationer fra denne bruger?", "mute_modal.hide_notifications": "Skjul notifikationer fra denne bruger?",
"mute_modal.indefinite": "Tidsubegrænset", "mute_modal.indefinite": "Tidsubegrænset",
"navigation_bar.about": "Om", "navigation_bar.about": "Om",
"navigation_bar.advanced_interface": "Åbn i avanceret webgrænseflade",
"navigation_bar.blocks": "Blokerede brugere", "navigation_bar.blocks": "Blokerede brugere",
"navigation_bar.bookmarks": "Bogmærker", "navigation_bar.bookmarks": "Bogmærker",
"navigation_bar.community_timeline": "Lokal tidslinje", "navigation_bar.community_timeline": "Lokal tidslinje",
@ -528,6 +543,8 @@
"report.placeholder": "Yderligere kommentarer", "report.placeholder": "Yderligere kommentarer",
"report.reasons.dislike": "Jeg bryder mig ikke om det", "report.reasons.dislike": "Jeg bryder mig ikke om det",
"report.reasons.dislike_description": "Det er ikke noget, man ønsker at se", "report.reasons.dislike_description": "Det er ikke noget, man ønsker at se",
"report.reasons.legal": "Det er ulovligt",
"report.reasons.legal_description": "Du mener, at det er i strid med lovgivningen i dit eller serverens land",
"report.reasons.other": "Det er noget andet", "report.reasons.other": "Det er noget andet",
"report.reasons.other_description": "Problemet passer ikke ind i andre kategorier", "report.reasons.other_description": "Problemet passer ikke ind i andre kategorier",
"report.reasons.spam": "Det er spam", "report.reasons.spam": "Det er spam",
@ -547,6 +564,7 @@
"report.unfollow": "Følg ikke længere @{name}", "report.unfollow": "Følg ikke længere @{name}",
"report.unfollow_explanation": "Du følger denne konto. For ikke længere at se vedkommendes indlæg i dit hjemmefeed, kan du stoppe med at følge dem.", "report.unfollow_explanation": "Du følger denne konto. For ikke længere at se vedkommendes indlæg i dit hjemmefeed, kan du stoppe med at følge dem.",
"report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} poster}} vedhæftet", "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} poster}} vedhæftet",
"report_notification.categories.legal": "Juridisk",
"report_notification.categories.other": "Andre", "report_notification.categories.other": "Andre",
"report_notification.categories.spam": "Spam", "report_notification.categories.spam": "Spam",
"report_notification.categories.violation": "Regelovertrædelse", "report_notification.categories.violation": "Regelovertrædelse",

View file

@ -135,6 +135,8 @@
"community.column_settings.remote_only": "Remote only", "community.column_settings.remote_only": "Remote only",
"compose.language.change": "Change language", "compose.language.change": "Change language",
"compose.language.search": "Search languages...", "compose.language.search": "Search languages...",
"compose.published.body": "Příspěvek zveřejněn.",
"compose.published.open": "Open",
"compose_form.direct_message_warning_learn_more": "Learn more", "compose_form.direct_message_warning_learn_more": "Learn more",
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any sensitive information over Mastodon.", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any sensitive information over Mastodon.",
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.", "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.",
@ -383,6 +385,7 @@
"mute_modal.hide_notifications": "Hide notifications from this user?", "mute_modal.hide_notifications": "Hide notifications from this user?",
"mute_modal.indefinite": "Indefinite", "mute_modal.indefinite": "Indefinite",
"navigation_bar.about": "About", "navigation_bar.about": "About",
"navigation_bar.advanced_interface": "Ireki web interfaze aurreratuan",
"navigation_bar.blocks": "Blocked users", "navigation_bar.blocks": "Blocked users",
"navigation_bar.bookmarks": "Bookmarks", "navigation_bar.bookmarks": "Bookmarks",
"navigation_bar.community_timeline": "Local timeline", "navigation_bar.community_timeline": "Local timeline",
@ -470,7 +473,7 @@
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
"onboarding.start.skip": "Want to skip right ahead?", "onboarding.start.skip": "Want to skip right ahead?",
"onboarding.start.title": "You've made it!", "onboarding.start.title": "You've made it!",
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", "onboarding.steps.follow_people.body": "Following interesting people is what Mastodon is all about.",
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
"onboarding.steps.publish_status.body": "Say hello to the World.", "onboarding.steps.publish_status.body": "Say hello to the World.",
"onboarding.steps.publish_status.title": "Make your first post", "onboarding.steps.publish_status.title": "Make your first post",
@ -616,6 +619,8 @@
"status.history.created": "{name} created {date}", "status.history.created": "{name} created {date}",
"status.history.edited": "{name} edited {date}", "status.history.edited": "{name} edited {date}",
"status.load_more": "Load more", "status.load_more": "Load more",
"status.media.open": "Klikka fyri at lata upp",
"status.media.show": "Klik om te toanen",
"status.media_hidden": "Media hidden", "status.media_hidden": "Media hidden",
"status.mention": "Mention @{name}", "status.mention": "Mention @{name}",
"status.more": "More", "status.more": "More",
@ -646,6 +651,7 @@
"status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {{attachmentCount} attachments}}", "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {{attachmentCount} attachments}}",
"status.translate": "Translate", "status.translate": "Translate",
"status.translated_from_with": "Translated from {lang} using {provider}", "status.translated_from_with": "Translated from {lang} using {provider}",
"status.uncached_media_warning": "A vista previa non está dispoñíble",
"status.unmute_conversation": "Unmute conversation", "status.unmute_conversation": "Unmute conversation",
"status.unpin": "Unpin from profile", "status.unpin": "Unpin from profile",
"subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.", "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",

View file

@ -116,7 +116,7 @@
"column.direct": "Private mentions", "column.direct": "Private mentions",
"column.directory": "Browse profiles", "column.directory": "Browse profiles",
"column.domain_blocks": "Blocked domains", "column.domain_blocks": "Blocked domains",
"column.favourites": "Favourites", "column.favourites": "Favorites",
"column.firehose": "Live feeds", "column.firehose": "Live feeds",
"column.follow_requests": "Follow requests", "column.follow_requests": "Follow requests",
"column.home": "Home", "column.home": "Home",
@ -189,7 +189,7 @@
"confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.", "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
"confirmations.mute.message": "Are you sure you want to mute {name}?", "confirmations.mute.message": "Are you sure you want to mute {name}?",
"confirmations.redraft.confirm": "Delete & redraft", "confirmations.redraft.confirm": "Delete & redraft",
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.", "confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favorites and boosts will be lost, and replies to the original post will be orphaned.",
"confirmations.reply.confirm": "Reply", "confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?", "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.unfollow.confirm": "Unfollow", "confirmations.unfollow.confirm": "Unfollow",
@ -210,7 +210,7 @@
"dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.", "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
"dismissable_banner.dismiss": "Dismiss", "dismissable_banner.dismiss": "Dismiss",
"dismissable_banner.explore_links": "These are news stories being shared the most on the social web today. Newer news stories posted by more different people are ranked higher.", "dismissable_banner.explore_links": "These are news stories being shared the most on the social web today. Newer news stories posted by more different people are ranked higher.",
"dismissable_banner.explore_statuses": "These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favourites are ranked higher.", "dismissable_banner.explore_statuses": "These are posts from across the social web that are gaining traction today. Newer posts with more boosts and favorites are ranked higher.",
"dismissable_banner.explore_tags": "These are hashtags that are gaining traction on the social web today. Hashtags that are used by more different people are ranked higher.", "dismissable_banner.explore_tags": "These are hashtags that are gaining traction on the social web today. Hashtags that are used by more different people are ranked higher.",
"dismissable_banner.public_timeline": "These are the most recent public posts from people on the social web that people on {domain} follow.", "dismissable_banner.public_timeline": "These are the most recent public posts from people on the social web that people on {domain} follow.",
"embed.instructions": "Embed this post on your website by copying the code below.", "embed.instructions": "Embed this post on your website by copying the code below.",
@ -239,8 +239,8 @@
"empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.", "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
"empty_column.domain_blocks": "There are no blocked domains yet.", "empty_column.domain_blocks": "There are no blocked domains yet.",
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!", "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
"empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.", "empty_column.favourited_statuses": "You don't have any favorite posts yet. When you favorite one, it will show up here.",
"empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.", "empty_column.favourites": "No one has favorited this post yet. When someone does, they will show up here.",
"empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.", "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
"empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.", "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
"empty_column.hashtag": "There is nothing in this hashtag yet.", "empty_column.hashtag": "There is nothing in this hashtag yet.",
@ -315,15 +315,15 @@
"home.explore_prompt.title": "This is your home base within Mastodon.", "home.explore_prompt.title": "This is your home base within Mastodon.",
"home.hide_announcements": "Hide announcements", "home.hide_announcements": "Hide announcements",
"home.show_announcements": "Show announcements", "home.show_announcements": "Show announcements",
"interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.", "interaction_modal.description.favourite": "With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.",
"interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.", "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
"interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.", "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
"interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.", "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
"interaction_modal.on_another_server": "On a different server", "interaction_modal.on_another_server": "On a different server",
"interaction_modal.on_this_server": "On this server", "interaction_modal.on_this_server": "On this server",
"interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.", "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.",
"interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.", "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
"interaction_modal.title.favourite": "Favourite {name}'s post", "interaction_modal.title.favourite": "Favorite {name}'s post",
"interaction_modal.title.follow": "Follow {name}", "interaction_modal.title.follow": "Follow {name}",
"interaction_modal.title.reblog": "Boost {name}'s post", "interaction_modal.title.reblog": "Boost {name}'s post",
"interaction_modal.title.reply": "Reply to {name}'s post", "interaction_modal.title.reply": "Reply to {name}'s post",
@ -339,8 +339,8 @@
"keyboard_shortcuts.direct": "to open private mentions column", "keyboard_shortcuts.direct": "to open private mentions column",
"keyboard_shortcuts.down": "Move down in the list", "keyboard_shortcuts.down": "Move down in the list",
"keyboard_shortcuts.enter": "Open post", "keyboard_shortcuts.enter": "Open post",
"keyboard_shortcuts.favourite": "Favourite post", "keyboard_shortcuts.favourite": "Favorite post",
"keyboard_shortcuts.favourites": "Open favourites list", "keyboard_shortcuts.favourites": "Open favorites list",
"keyboard_shortcuts.federated": "Open federated timeline", "keyboard_shortcuts.federated": "Open federated timeline",
"keyboard_shortcuts.heading": "Keyboard shortcuts", "keyboard_shortcuts.heading": "Keyboard shortcuts",
"keyboard_shortcuts.home": "Open home timeline", "keyboard_shortcuts.home": "Open home timeline",
@ -371,6 +371,7 @@
"lightbox.previous": "Previous", "lightbox.previous": "Previous",
"limited_account_hint.action": "Show profile anyway", "limited_account_hint.action": "Show profile anyway",
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.", "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
"link_preview.author": "By {name}",
"lists.account.add": "Add to list", "lists.account.add": "Add to list",
"lists.account.remove": "Remove from list", "lists.account.remove": "Remove from list",
"lists.antennas": "Related antennas", "lists.antennas": "Related antennas",
@ -405,7 +406,7 @@
"navigation_bar.domain_blocks": "Blocked domains", "navigation_bar.domain_blocks": "Blocked domains",
"navigation_bar.edit_profile": "Edit profile", "navigation_bar.edit_profile": "Edit profile",
"navigation_bar.explore": "Explore", "navigation_bar.explore": "Explore",
"navigation_bar.favourites": "Favourites", "navigation_bar.favourites": "Favorites",
"navigation_bar.filters": "Muted words", "navigation_bar.filters": "Muted words",
"navigation_bar.follow_requests": "Follow requests", "navigation_bar.follow_requests": "Follow requests",
"navigation_bar.followed_tags": "Followed hashtags", "navigation_bar.followed_tags": "Followed hashtags",
@ -424,7 +425,7 @@
"notification.admin.report": "{name} reported {target}", "notification.admin.report": "{name} reported {target}",
"notification.admin.sign_up": "{name} signed up", "notification.admin.sign_up": "{name} signed up",
"notification.emoji_reaction": "{name} reacted your post with emoji", "notification.emoji_reaction": "{name} reacted your post with emoji",
"notification.favourite": "{name} favourited your post", "notification.favourite": "{name} favorited your post",
"notification.follow": "{name} followed you", "notification.follow": "{name} followed you",
"notification.follow_request": "{name} has requested to follow you", "notification.follow_request": "{name} has requested to follow you",
"notification.mention": "{name} mentioned you", "notification.mention": "{name} mentioned you",
@ -439,7 +440,7 @@
"notifications.column_settings.admin.report": "New reports:", "notifications.column_settings.admin.report": "New reports:",
"notifications.column_settings.admin.sign_up": "New sign-ups:", "notifications.column_settings.admin.sign_up": "New sign-ups:",
"notifications.column_settings.alert": "Desktop notifications", "notifications.column_settings.alert": "Desktop notifications",
"notifications.column_settings.favourite": "Favourites:", "notifications.column_settings.favourite": "Favorites:",
"notifications.column_settings.filter_bar.advanced": "Display all categories", "notifications.column_settings.filter_bar.advanced": "Display all categories",
"notifications.column_settings.filter_bar.category": "Quick filter bar", "notifications.column_settings.filter_bar.category": "Quick filter bar",
"notifications.column_settings.filter_bar.show_bar": "Show filter bar", "notifications.column_settings.filter_bar.show_bar": "Show filter bar",
@ -457,7 +458,7 @@
"notifications.column_settings.update": "Edits:", "notifications.column_settings.update": "Edits:",
"notifications.filter.all": "All", "notifications.filter.all": "All",
"notifications.filter.boosts": "Boosts", "notifications.filter.boosts": "Boosts",
"notifications.filter.favourites": "Favourites", "notifications.filter.favourites": "Favorites",
"notifications.filter.follows": "Follows", "notifications.filter.follows": "Follows",
"notifications.filter.mentions": "Mentions", "notifications.filter.mentions": "Mentions",
"notifications.filter.polls": "Poll results", "notifications.filter.polls": "Poll results",
@ -623,7 +624,7 @@
"server_banner.server_stats": "Server stats:", "server_banner.server_stats": "Server stats:",
"sign_in_banner.create_account": "Create account", "sign_in_banner.create_account": "Create account",
"sign_in_banner.sign_in": "Login", "sign_in_banner.sign_in": "Login",
"sign_in_banner.text": "Login to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.", "sign_in_banner.text": "Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.",
"status.admin_account": "Open moderation interface for @{name}", "status.admin_account": "Open moderation interface for @{name}",
"status.admin_domain": "Open moderation interface for {domain}", "status.admin_domain": "Open moderation interface for {domain}",
"status.admin_status": "Open this post in the moderation interface", "status.admin_status": "Open this post in the moderation interface",
@ -643,7 +644,7 @@
"status.emoji_reaction": "Stamp", "status.emoji_reaction": "Stamp",
"status.emoji_reaction.pick": "Pick stamp", "status.emoji_reaction.pick": "Pick stamp",
"status.expiration.add": "Set status expired time", "status.expiration.add": "Set status expired time",
"status.favourite": "Favourite", "status.favourite": "Favorite",
"status.filter": "Filter this post", "status.filter": "Filter this post",
"status.filtered": "Filtered", "status.filtered": "Filtered",
"status.hide": "Hide post", "status.hide": "Hide post",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "¿Ocultar notificaciones de este usuario?", "mute_modal.hide_notifications": "¿Ocultar notificaciones de este usuario?",
"mute_modal.indefinite": "Indefinida", "mute_modal.indefinite": "Indefinida",
"navigation_bar.about": "Acerca de", "navigation_bar.about": "Acerca de",
"navigation_bar.advanced_interface": "Abrir en la interfaz web avanzada",
"navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.blocks": "Usuarios bloqueados",
"navigation_bar.bookmarks": "Marcadores", "navigation_bar.bookmarks": "Marcadores",
"navigation_bar.community_timeline": "Cronología local", "navigation_bar.community_timeline": "Cronología local",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Kas peita teated sellelt kasutajalt?", "mute_modal.hide_notifications": "Kas peita teated sellelt kasutajalt?",
"mute_modal.indefinite": "Lõpmatu", "mute_modal.indefinite": "Lõpmatu",
"navigation_bar.about": "Teave", "navigation_bar.about": "Teave",
"navigation_bar.advanced_interface": "Ireki web interfaze aurreratuan",
"navigation_bar.blocks": "Blokeeritud kasutajad", "navigation_bar.blocks": "Blokeeritud kasutajad",
"navigation_bar.bookmarks": "Järjehoidjad", "navigation_bar.bookmarks": "Järjehoidjad",
"navigation_bar.community_timeline": "Kohalik ajajoon", "navigation_bar.community_timeline": "Kohalik ajajoon",

View file

@ -18,7 +18,7 @@
"account.block": "مسدود کردن @{name}", "account.block": "مسدود کردن @{name}",
"account.block_domain": "مسدود کردن دامنهٔ {domain}", "account.block_domain": "مسدود کردن دامنهٔ {domain}",
"account.block_short": "انسداد", "account.block_short": "انسداد",
"account.blocked": "مسدود", "account.blocked": "مسدود شده",
"account.browse_more_on_origin_server": "مرور بیش‌تر روی نمایهٔ اصلی", "account.browse_more_on_origin_server": "مرور بیش‌تر روی نمایهٔ اصلی",
"account.cancel_follow_request": "رد کردن درخواست پی‌گیری", "account.cancel_follow_request": "رد کردن درخواست پی‌گیری",
"account.direct": "خصوصی از @{name} نام ببرید", "account.direct": "خصوصی از @{name} نام ببرید",
@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "نهفتن آگاهی‌ها از این کاربر؟", "mute_modal.hide_notifications": "نهفتن آگاهی‌ها از این کاربر؟",
"mute_modal.indefinite": "نامعلوم", "mute_modal.indefinite": "نامعلوم",
"navigation_bar.about": "درباره", "navigation_bar.about": "درباره",
"navigation_bar.advanced_interface": "بازکردن در رابط کاربری وب پیشرفته",
"navigation_bar.blocks": "کاربران مسدود شده", "navigation_bar.blocks": "کاربران مسدود شده",
"navigation_bar.bookmarks": "نشانک‌ها", "navigation_bar.bookmarks": "نشانک‌ها",
"navigation_bar.community_timeline": "خط زمانی محلّی", "navigation_bar.community_timeline": "خط زمانی محلّی",

View file

@ -204,6 +204,7 @@
"dismissable_banner.explore_links": "Näistä uutisista puhuvat ihmiset juuri nyt tällä ja muilla hajautetun verkon palvelimilla.", "dismissable_banner.explore_links": "Näistä uutisista puhuvat ihmiset juuri nyt tällä ja muilla hajautetun verkon palvelimilla.",
"dismissable_banner.explore_statuses": "Nämä viestit juuri nyt tältä ja muilta hajautetun verkon palvelimilta ovat saamassa vetoa tältä palvelimelta.", "dismissable_banner.explore_statuses": "Nämä viestit juuri nyt tältä ja muilta hajautetun verkon palvelimilta ovat saamassa vetoa tältä palvelimelta.",
"dismissable_banner.explore_tags": "Nämä aihetunnisteet saavat juuri nyt vetovoimaa tällä ja muilla hajautetun verkon palvelimilla olevien ihmisten keskuudessa.", "dismissable_banner.explore_tags": "Nämä aihetunnisteet saavat juuri nyt vetovoimaa tällä ja muilla hajautetun verkon palvelimilla olevien ihmisten keskuudessa.",
"dismissable_banner.public_timeline": "Nämä ovat viimeisimmät julkiset viestit sosiaalisen verkon ihmisiltä, joita {domain} käyttäjät seuraa.",
"embed.instructions": "Upota julkaisu verkkosivullesi kopioimalla alla oleva koodi.", "embed.instructions": "Upota julkaisu verkkosivullesi kopioimalla alla oleva koodi.",
"embed.preview": "Se tulee näyttämään tältä:", "embed.preview": "Se tulee näyttämään tältä:",
"emoji_button.activity": "Aktiviteetit", "emoji_button.activity": "Aktiviteetit",
@ -270,6 +271,8 @@
"filter_modal.select_filter.title": "Suodata tämä viesti", "filter_modal.select_filter.title": "Suodata tämä viesti",
"filter_modal.title.status": "Suodata viesti", "filter_modal.title.status": "Suodata viesti",
"firehose.all": "Kaikki", "firehose.all": "Kaikki",
"firehose.local": "Tämä palvelin",
"firehose.remote": "Muut palvelimet",
"follow_request.authorize": "Valtuuta", "follow_request.authorize": "Valtuuta",
"follow_request.reject": "Hylkää", "follow_request.reject": "Hylkää",
"follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, on palvelun {domain} ylläpito arvioinut, että saatat olla halukas tarkistamaan nämä seurauspyynnöt erikseen.", "follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, on palvelun {domain} ylläpito arvioinut, että saatat olla halukas tarkistamaan nämä seurauspyynnöt erikseen.",
@ -382,6 +385,7 @@
"mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?", "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?",
"mute_modal.indefinite": "Ikuisesti", "mute_modal.indefinite": "Ikuisesti",
"navigation_bar.about": "Tietoja", "navigation_bar.about": "Tietoja",
"navigation_bar.advanced_interface": "Avaa edistyneessä käyttöliittymässä",
"navigation_bar.blocks": "Estetyt käyttäjät", "navigation_bar.blocks": "Estetyt käyttäjät",
"navigation_bar.bookmarks": "Kirjanmerkit", "navigation_bar.bookmarks": "Kirjanmerkit",
"navigation_bar.community_timeline": "Paikallinen aikajana", "navigation_bar.community_timeline": "Paikallinen aikajana",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Fjal fráboðanir frá hesum brúkaranum?", "mute_modal.hide_notifications": "Fjal fráboðanir frá hesum brúkaranum?",
"mute_modal.indefinite": "Óásett tíðarskeið", "mute_modal.indefinite": "Óásett tíðarskeið",
"navigation_bar.about": "Um", "navigation_bar.about": "Um",
"navigation_bar.advanced_interface": "Lat upp í framkomnum vevmarkamóti",
"navigation_bar.blocks": "Bannaðir brúkarar", "navigation_bar.blocks": "Bannaðir brúkarar",
"navigation_bar.bookmarks": "Goymd", "navigation_bar.bookmarks": "Goymd",
"navigation_bar.community_timeline": "Lokal tíðarlinja", "navigation_bar.community_timeline": "Lokal tíðarlinja",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Masquer les notifications de ce compte?", "mute_modal.hide_notifications": "Masquer les notifications de ce compte?",
"mute_modal.indefinite": "Indéfinie", "mute_modal.indefinite": "Indéfinie",
"navigation_bar.about": "À propos", "navigation_bar.about": "À propos",
"navigation_bar.advanced_interface": "Ouvrir dans linterface avancée",
"navigation_bar.blocks": "Comptes bloqués", "navigation_bar.blocks": "Comptes bloqués",
"navigation_bar.bookmarks": "Signets", "navigation_bar.bookmarks": "Signets",
"navigation_bar.community_timeline": "Fil local", "navigation_bar.community_timeline": "Fil local",

View file

@ -135,6 +135,8 @@
"community.column_settings.remote_only": "Feadhainn chèin a-mhàin", "community.column_settings.remote_only": "Feadhainn chèin a-mhàin",
"compose.language.change": "Atharraich an cànan", "compose.language.change": "Atharraich an cànan",
"compose.language.search": "Lorg cànan…", "compose.language.search": "Lorg cànan…",
"compose.published.body": "Postimi u botua.",
"compose.published.open": "Fosgail",
"compose_form.direct_message_warning_learn_more": "Barrachd fiosrachaidh", "compose_form.direct_message_warning_learn_more": "Barrachd fiosrachaidh",
"compose_form.encryption_warning": "Chan eil crioptachadh ceann gu ceann air postaichean Mhastodon. Na co-roinn fiosrachadh dìomhair idir le Mastodon.", "compose_form.encryption_warning": "Chan eil crioptachadh ceann gu ceann air postaichean Mhastodon. Na co-roinn fiosrachadh dìomhair idir le Mastodon.",
"compose_form.hashtag_warning": "Cha nochd am post seo fon taga hais o nach eil e poblach. Cha ghabh ach postaichean poblach a lorg a-rèir an tagaichean hais.", "compose_form.hashtag_warning": "Cha nochd am post seo fon taga hais o nach eil e poblach. Cha ghabh ach postaichean poblach a lorg a-rèir an tagaichean hais.",
@ -383,6 +385,7 @@
"mute_modal.hide_notifications": "A bheil thu airson na brathan fhalach on chleachdaiche seo?", "mute_modal.hide_notifications": "A bheil thu airson na brathan fhalach on chleachdaiche seo?",
"mute_modal.indefinite": "Gun chrìoch", "mute_modal.indefinite": "Gun chrìoch",
"navigation_bar.about": "Mu dhèidhinn", "navigation_bar.about": "Mu dhèidhinn",
"navigation_bar.advanced_interface": "Ireki web interfaze aurreratuan",
"navigation_bar.blocks": "Cleachdaichean bacte", "navigation_bar.blocks": "Cleachdaichean bacte",
"navigation_bar.bookmarks": "Comharran-lìn", "navigation_bar.bookmarks": "Comharran-lìn",
"navigation_bar.community_timeline": "Loidhne-ama ionadail", "navigation_bar.community_timeline": "Loidhne-ama ionadail",
@ -616,6 +619,8 @@
"status.history.created": "Chruthaich {name} {date} e", "status.history.created": "Chruthaich {name} {date} e",
"status.history.edited": "Dheasaich {name} {date} e", "status.history.edited": "Dheasaich {name} {date} e",
"status.load_more": "Luchdaich barrachd dheth", "status.load_more": "Luchdaich barrachd dheth",
"status.media.open": "Klikoni për hapje",
"status.media.show": "Klikoni për shfaqje",
"status.media_hidden": "Meadhan falaichte", "status.media_hidden": "Meadhan falaichte",
"status.mention": "Thoir iomradh air @{name}", "status.mention": "Thoir iomradh air @{name}",
"status.more": "Barrachd", "status.more": "Barrachd",
@ -646,6 +651,7 @@
"status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}",
"status.translate": "Eadar-theangaich", "status.translate": "Eadar-theangaich",
"status.translated_from_with": "Air eadar-theangachadh o {lang} le {provider}", "status.translated_from_with": "Air eadar-theangachadh o {lang} le {provider}",
"status.uncached_media_warning": "Ska paraparje",
"status.unmute_conversation": "Dì-mhùch an còmhradh", "status.unmute_conversation": "Dì-mhùch an còmhradh",
"status.unpin": "Dì-phrìnich on phròifil", "status.unpin": "Dì-phrìnich on phròifil",
"subscribed_languages.lead": "Cha nochd ach na postaichean sna cànanan a thagh thu air loidhnichean-ama na dachaigh s nan liostaichean às dèidh an atharrachaidh seo. Na tagh gin ma tha thu airson na postaichean uile fhaighinn ge b e dè an cànan.", "subscribed_languages.lead": "Cha nochd ach na postaichean sna cànanan a thagh thu air loidhnichean-ama na dachaigh s nan liostaichean às dèidh an atharrachaidh seo. Na tagh gin ma tha thu airson na postaichean uile fhaighinn ge b e dè an cànan.",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Agochar notificacións desta persoa?", "mute_modal.hide_notifications": "Agochar notificacións desta persoa?",
"mute_modal.indefinite": "Indefinida", "mute_modal.indefinite": "Indefinida",
"navigation_bar.about": "Acerca de", "navigation_bar.about": "Acerca de",
"navigation_bar.advanced_interface": "Abrir coa interface web avanzada",
"navigation_bar.blocks": "Usuarias bloqueadas", "navigation_bar.blocks": "Usuarias bloqueadas",
"navigation_bar.bookmarks": "Marcadores", "navigation_bar.bookmarks": "Marcadores",
"navigation_bar.community_timeline": "Cronoloxía local", "navigation_bar.community_timeline": "Cronoloxía local",

View file

@ -94,7 +94,7 @@
"boost_modal.combo": "次からは{combo}を押せばスキップできます", "boost_modal.combo": "次からは{combo}を押せばスキップできます",
"bundle_column_error.copy_stacktrace": "エラーレポートをコピー", "bundle_column_error.copy_stacktrace": "エラーレポートをコピー",
"bundle_column_error.error.body": "要求されたページをレンダリングできませんでした。コードのバグ、またはブラウザの互換性の問題が原因である可能性があります。", "bundle_column_error.error.body": "要求されたページをレンダリングできませんでした。コードのバグ、またはブラウザの互換性の問題が原因である可能性があります。",
"bundle_column_error.error.title": "あらら……", "bundle_column_error.error.title": "おっと!",
"bundle_column_error.network.body": "このページを読み込もうとしたときにエラーが発生しました。インターネット接続またはこのサーバーの一時的な問題が発生した可能性があります。", "bundle_column_error.network.body": "このページを読み込もうとしたときにエラーが発生しました。インターネット接続またはこのサーバーの一時的な問題が発生した可能性があります。",
"bundle_column_error.network.title": "ネットワークエラー", "bundle_column_error.network.title": "ネットワークエラー",
"bundle_column_error.retry": "再試行", "bundle_column_error.retry": "再試行",
@ -210,9 +210,9 @@
"disabled_account_banner.text": "あなたのアカウント『{disabledAccount}』は現在無効になっています。", "disabled_account_banner.text": "あなたのアカウント『{disabledAccount}』は現在無効になっています。",
"dismissable_banner.community_timeline": "これらは{domain}がホストしている人たちの最新の公開投稿です。", "dismissable_banner.community_timeline": "これらは{domain}がホストしている人たちの最新の公開投稿です。",
"dismissable_banner.dismiss": "閉じる", "dismissable_banner.dismiss": "閉じる",
"dismissable_banner.explore_links": "これらのニュース記事は現在分散型ネットワークの他のサーバーの人たちに話されています。", "dismissable_banner.explore_links": "ネットワーク上で話題になっているニュースです。たくさんのユーザーにシェアされた記事ほど上位に表示されます。",
"dismissable_banner.explore_statuses": "分散型ネットワーク内の他のサーバーのこれらの投稿は現在このサーバー上で注目されています。", "dismissable_banner.explore_statuses": "ネットワーク上で注目を集めている投稿です。ブーストやお気に入り登録の多い投稿が上位に表示されます。",
"dismissable_banner.explore_tags": "これらのハッシュタグは現在分散型ネットワークの他のサーバーの人たちに話されています。", "dismissable_banner.explore_tags": "ネットワーク上でトレンドになっているハッシュタグです。たくさんのユーザーに使われたタグほど上位に表示されます。",
"dismissable_banner.public_timeline": "{domain} のユーザーがリモートフォローしているアカウントからの公開投稿のタイムラインです。", "dismissable_banner.public_timeline": "{domain} のユーザーがリモートフォローしているアカウントからの公開投稿のタイムラインです。",
"embed.instructions": "下記のコードをコピーしてウェブサイトに埋め込みます。", "embed.instructions": "下記のコードをコピーしてウェブサイトに埋め込みます。",
"embed.preview": "表示例:", "embed.preview": "表示例:",
@ -246,7 +246,7 @@
"empty_column.follow_requests": "まだフォローリクエストを受けていません。フォローリクエストを受けるとここに表示されます。", "empty_column.follow_requests": "まだフォローリクエストを受けていません。フォローリクエストを受けるとここに表示されます。",
"empty_column.followed_tags": "まだハッシュタグをフォローしていません。フォローするとここに表示されます。", "empty_column.followed_tags": "まだハッシュタグをフォローしていません。フォローするとここに表示されます。",
"empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.hashtag": "このハッシュタグはまだ使われていません。",
"empty_column.home": "ホームタイムラインはまだ空っぽです。誰かフォローして埋めてみましょう。 {suggestions}", "empty_column.home": "ホームタイムラインはまだ空っぽです。だれかをフォローして埋めてみましょう。",
"empty_column.list": "このリストにはまだなにもありません。このリストのメンバーが新しい投稿をするとここに表示されます。", "empty_column.list": "このリストにはまだなにもありません。このリストのメンバーが新しい投稿をするとここに表示されます。",
"empty_column.lists": "まだリストがありません。リストを作るとここに表示されます。", "empty_column.lists": "まだリストがありません。リストを作るとここに表示されます。",
"empty_column.mutes": "まだ誰もミュートしていません。", "empty_column.mutes": "まだ誰もミュートしていません。",
@ -485,18 +485,18 @@
"onboarding.actions.go_to_local_timeline": "ローカルの投稿を見る", "onboarding.actions.go_to_local_timeline": "ローカルの投稿を見る",
"onboarding.compose.template": "#Mastodon はじめました", "onboarding.compose.template": "#Mastodon はじめました",
"onboarding.follows.empty": "おすすめに表示できるアカウントはまだありません。検索や「見つける」を活用して、ほかのアカウントを探してみましょう。", "onboarding.follows.empty": "おすすめに表示できるアカウントはまだありません。検索や「見つける」を活用して、ほかのアカウントを探してみましょう。",
"onboarding.follows.lead": "自分の手でタイムラインを作ってみましょう。フォローを増やせば、タイムラインはより賑やかでおもしろいものになります。最初のフォローの参考になりそうなアカウントをいくつか表示しています。気になったものがあれば、ここからフォローしてみましょう。フォローはいつでも解除して大丈夫です。", "onboarding.follows.lead": "ホームタイムラインは Mastodon の軸足となる場所です。たくさんのユーザーをフォローすることで、ホームタイムラインはよりにぎやかでおもしろいものになります。手はじめに、おすすめのアカウントから何人かフォローしてみましょう:",
"onboarding.follows.title": "おすすめのアカウント", "onboarding.follows.title": "ホームタイムラインを埋める",
"onboarding.share.lead": "新しい Mastodon アカウントをみんなに紹介しましょう。", "onboarding.share.lead": "新しい Mastodon アカウントをみんなに紹介しましょう。",
"onboarding.share.message": "「{username}」で #Mastodon はじめました! {url}", "onboarding.share.message": "「{username}」で #Mastodon はじめました! {url}",
"onboarding.share.next_steps": "次のステップに進む:", "onboarding.share.next_steps": "次のステップに進む:",
"onboarding.share.title": "プロフィールをシェアする", "onboarding.share.title": "プロフィールをシェアする",
"onboarding.start.lead": "Mastodon アカウントの準備ができました。次のステップに進みましょう:", "onboarding.start.lead": "Mastodon へようこそ。Mastodon は非中央集権型SNSのひとつで、ユーザーそれぞれの考えかたを尊重するプラットフォームです。ユーザーはどんな「好き」も自由に追いかけることができます。次のステップに進んで、新天地でのつながりをみつけましょう:",
"onboarding.start.skip": "下のどれかをクリックしてチュートリアルを終了", "onboarding.start.skip": "下のどれかをクリックしてチュートリアルを終了",
"onboarding.start.title": "はじめに", "onboarding.start.title": "はじめに",
"onboarding.steps.follow_people.body": "タイムラインを充実させましょう。", "onboarding.steps.follow_people.body": "ユーザーをフォローしてみましょう。これが Mastodon を楽しむ基本です。",
"onboarding.steps.follow_people.title": "最初の{count, plural, other {#人}}をフォローする", "onboarding.steps.follow_people.title": "ホームタイムラインを埋める",
"onboarding.steps.publish_status.body": "試しに何か書いてみましょう。", "onboarding.steps.publish_status.body": "試しになにか書いてみましょう。写真、ビデオ、アンケートなど、なんでも大丈夫です {emoji}",
"onboarding.steps.publish_status.title": "はじめての投稿", "onboarding.steps.publish_status.title": "はじめての投稿",
"onboarding.steps.setup_profile.body": "ほかのユーザーが親しみやすいように、プロフィールを整えましょう。", "onboarding.steps.setup_profile.body": "ほかのユーザーが親しみやすいように、プロフィールを整えましょう。",
"onboarding.steps.setup_profile.title": "プロフィールを完成させる", "onboarding.steps.setup_profile.title": "プロフィールを完成させる",

View file

@ -373,7 +373,7 @@
"lists.new.title_placeholder": "새 리스트의 이름", "lists.new.title_placeholder": "새 리스트의 이름",
"lists.replies_policy.followed": "팔로우 한 사용자 누구나", "lists.replies_policy.followed": "팔로우 한 사용자 누구나",
"lists.replies_policy.list": "리스트의 구성원", "lists.replies_policy.list": "리스트의 구성원",
"lists.replies_policy.none": "선택 안함", "lists.replies_policy.none": "모두 제외",
"lists.replies_policy.title": "답글 표시:", "lists.replies_policy.title": "답글 표시:",
"lists.search": "팔로우 중인 사람들 중에서 찾기", "lists.search": "팔로우 중인 사람들 중에서 찾기",
"lists.subheading": "리스트", "lists.subheading": "리스트",
@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "이 사용자로부터의 알림을 숨기시겠습니까?", "mute_modal.hide_notifications": "이 사용자로부터의 알림을 숨기시겠습니까?",
"mute_modal.indefinite": "무기한", "mute_modal.indefinite": "무기한",
"navigation_bar.about": "정보", "navigation_bar.about": "정보",
"navigation_bar.advanced_interface": "고급 웹 인터페이스에서 열기",
"navigation_bar.blocks": "차단한 사용자", "navigation_bar.blocks": "차단한 사용자",
"navigation_bar.bookmarks": "북마크", "navigation_bar.bookmarks": "북마크",
"navigation_bar.community_timeline": "로컬 타임라인", "navigation_bar.community_timeline": "로컬 타임라인",
@ -392,7 +393,7 @@
"navigation_bar.direct": "개인적인 멘션", "navigation_bar.direct": "개인적인 멘션",
"navigation_bar.discover": "발견하기", "navigation_bar.discover": "발견하기",
"navigation_bar.domain_blocks": "차단한 도메인", "navigation_bar.domain_blocks": "차단한 도메인",
"navigation_bar.edit_profile": "프로필 편집", "navigation_bar.edit_profile": "프로필 수정",
"navigation_bar.explore": "둘러보기", "navigation_bar.explore": "둘러보기",
"navigation_bar.favourites": "좋아요", "navigation_bar.favourites": "좋아요",
"navigation_bar.filters": "뮤트한 단어", "navigation_bar.filters": "뮤트한 단어",
@ -608,7 +609,7 @@
"status.direct": "@{name} 님에게 개인적으로 멘션", "status.direct": "@{name} 님에게 개인적으로 멘션",
"status.direct_indicator": "개인적인 멘션", "status.direct_indicator": "개인적인 멘션",
"status.edit": "수정", "status.edit": "수정",
"status.edited": "{date}에 편집됨", "status.edited": "{date}에 수정함",
"status.edited_x_times": "{count}번 수정됨", "status.edited_x_times": "{count}번 수정됨",
"status.embed": "공유하기", "status.embed": "공유하기",
"status.favourite": "좋아요", "status.favourite": "좋아요",
@ -682,7 +683,7 @@
"upload_form.audio_description": "청각 장애인을 위한 설명", "upload_form.audio_description": "청각 장애인을 위한 설명",
"upload_form.description": "시각장애인을 위한 설명", "upload_form.description": "시각장애인을 위한 설명",
"upload_form.description_missing": "설명이 추가되지 않음", "upload_form.description_missing": "설명이 추가되지 않음",
"upload_form.edit": "편집", "upload_form.edit": "수정",
"upload_form.thumbnail": "썸네일 변경", "upload_form.thumbnail": "썸네일 변경",
"upload_form.undo": "삭제", "upload_form.undo": "삭제",
"upload_form.video_description": "청각, 시각 장애인을 위한 설명", "upload_form.video_description": "청각, 시각 장애인을 위한 설명",
@ -692,7 +693,7 @@
"upload_modal.choose_image": "이미지 선택", "upload_modal.choose_image": "이미지 선택",
"upload_modal.description_placeholder": "다람쥐 헌 쳇바퀴 타고파", "upload_modal.description_placeholder": "다람쥐 헌 쳇바퀴 타고파",
"upload_modal.detect_text": "이미지에서 텍스트 추출", "upload_modal.detect_text": "이미지에서 텍스트 추출",
"upload_modal.edit_media": "미디어 편집", "upload_modal.edit_media": "미디어 수정",
"upload_modal.hint": "미리보기를 클릭하거나 드래그 해서 포컬 포인트를 맞추세요. 이 점은 썸네일에 항상 보여질 부분을 나타냅니다.", "upload_modal.hint": "미리보기를 클릭하거나 드래그 해서 포컬 포인트를 맞추세요. 이 점은 썸네일에 항상 보여질 부분을 나타냅니다.",
"upload_modal.preparing_ocr": "OCR 준비 중…", "upload_modal.preparing_ocr": "OCR 준비 중…",
"upload_modal.preview_label": "미리보기 ({ratio})", "upload_modal.preview_label": "미리보기 ({ratio})",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "ဤအကောင့်မှသတိပေးချက်များကိုပိတ်မလား?", "mute_modal.hide_notifications": "ဤအကောင့်မှသတိပေးချက်များကိုပိတ်မလား?",
"mute_modal.indefinite": "ရေတွက်လို့မရပါ", "mute_modal.indefinite": "ရေတွက်လို့မရပါ",
"navigation_bar.about": "အကြောင်း", "navigation_bar.about": "အကြောင်း",
"navigation_bar.advanced_interface": "အဆင့်မြင့်ဝဘ်ပုံစံ ဖွင့်ပါ",
"navigation_bar.blocks": "ဘလော့ထားသောအကောင့်များ", "navigation_bar.blocks": "ဘလော့ထားသောအကောင့်များ",
"navigation_bar.bookmarks": "မှတ်ထားသည်များ", "navigation_bar.bookmarks": "မှတ်ထားသည်များ",
"navigation_bar.community_timeline": "ဒေသစံတော်ချိန်", "navigation_bar.community_timeline": "ဒေသစံတော်ချိန်",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Skjul varsel frå denne brukaren?", "mute_modal.hide_notifications": "Skjul varsel frå denne brukaren?",
"mute_modal.indefinite": "På ubestemt tid", "mute_modal.indefinite": "På ubestemt tid",
"navigation_bar.about": "Om", "navigation_bar.about": "Om",
"navigation_bar.advanced_interface": "Åpne i det avanserte nettgrensesnittet",
"navigation_bar.blocks": "Blokkerte brukarar", "navigation_bar.blocks": "Blokkerte brukarar",
"navigation_bar.bookmarks": "Bokmerke", "navigation_bar.bookmarks": "Bokmerke",
"navigation_bar.community_timeline": "Lokal tidsline", "navigation_bar.community_timeline": "Lokal tidsline",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?", "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?",
"mute_modal.indefinite": "På ubestemt tid", "mute_modal.indefinite": "På ubestemt tid",
"navigation_bar.about": "Om", "navigation_bar.about": "Om",
"navigation_bar.advanced_interface": "Åpne i det avanserte nettgrensesnittet",
"navigation_bar.blocks": "Blokkerte brukere", "navigation_bar.blocks": "Blokkerte brukere",
"navigation_bar.bookmarks": "Bokmerker", "navigation_bar.bookmarks": "Bokmerker",
"navigation_bar.community_timeline": "Lokal tidslinje", "navigation_bar.community_timeline": "Lokal tidslinje",

View file

@ -76,6 +76,9 @@
"admin.dashboard.retention.average": "Średnia", "admin.dashboard.retention.average": "Średnia",
"admin.dashboard.retention.cohort": "Miesiąc rejestracji", "admin.dashboard.retention.cohort": "Miesiąc rejestracji",
"admin.dashboard.retention.cohort_size": "Nowi użytkownicy", "admin.dashboard.retention.cohort_size": "Nowi użytkownicy",
"admin.impact_report.instance_accounts": "Profile kont, które usuną",
"admin.impact_report.instance_followers": "Obserwujący stracili nasi użytkownicy",
"admin.impact_report.instance_follows": "Obserwujący ich użytkownicy stracą",
"admin.impact_report.title": "Podsumowanie wpływu", "admin.impact_report.title": "Podsumowanie wpływu",
"alert.rate_limited.message": "Spróbuj ponownie po {retry_time, time, medium}.", "alert.rate_limited.message": "Spróbuj ponownie po {retry_time, time, medium}.",
"alert.rate_limited.title": "Ograniczony czasowo", "alert.rate_limited.title": "Ograniczony czasowo",
@ -301,6 +304,7 @@
"home.column_settings.show_reblogs": "Pokazuj podbicia", "home.column_settings.show_reblogs": "Pokazuj podbicia",
"home.column_settings.show_replies": "Pokazuj odpowiedzi", "home.column_settings.show_replies": "Pokazuj odpowiedzi",
"home.explore_prompt.body": "Twój kanał główny będzie zawierał kombinację postów z tagów, które wybrano do obserwacji, osoby, które wybrano obserwować i wpisy, które one podbijają. Obecnie jest tu całkiem cicho, więc co myślisz o:", "home.explore_prompt.body": "Twój kanał główny będzie zawierał kombinację postów z tagów, które wybrano do obserwacji, osoby, które wybrano obserwować i wpisy, które one podbijają. Obecnie jest tu całkiem cicho, więc co myślisz o:",
"home.explore_prompt.title": "To twoja baza domowa w Mastodon.",
"home.hide_announcements": "Ukryj ogłoszenia", "home.hide_announcements": "Ukryj ogłoszenia",
"home.show_announcements": "Pokaż ogłoszenia", "home.show_announcements": "Pokaż ogłoszenia",
"interaction_modal.description.favourite": "Mając konto na Mastodonie, możesz dodawać wpisy do ulubionych by dać znać jego autorowi, że podoba Ci się ten wpis i zachować go na później.", "interaction_modal.description.favourite": "Mając konto na Mastodonie, możesz dodawać wpisy do ulubionych by dać znać jego autorowi, że podoba Ci się ten wpis i zachować go na później.",
@ -381,6 +385,7 @@
"mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?",
"mute_modal.indefinite": "Nieokreślony", "mute_modal.indefinite": "Nieokreślony",
"navigation_bar.about": "O serwerze", "navigation_bar.about": "O serwerze",
"navigation_bar.advanced_interface": "Otwórz w zaawansowanym interfejsie użytkownika",
"navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.blocks": "Zablokowani użytkownicy",
"navigation_bar.bookmarks": "Zakładki", "navigation_bar.bookmarks": "Zakładki",
"navigation_bar.community_timeline": "Lokalna oś czasu", "navigation_bar.community_timeline": "Lokalna oś czasu",

View file

@ -17,6 +17,7 @@
"account.badges.group": "Grupo", "account.badges.group": "Grupo",
"account.block": "Bloquear @{name}", "account.block": "Bloquear @{name}",
"account.block_domain": "Esconder tudo do domínio {domain}", "account.block_domain": "Esconder tudo do domínio {domain}",
"account.block_short": "Bloquear",
"account.blocked": "Bloqueado(a)", "account.blocked": "Bloqueado(a)",
"account.browse_more_on_origin_server": "Encontrar mais no perfil original", "account.browse_more_on_origin_server": "Encontrar mais no perfil original",
"account.cancel_follow_request": "Retirar pedido para seguir", "account.cancel_follow_request": "Retirar pedido para seguir",
@ -48,7 +49,10 @@
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar @{name}",
"account.moved_to": "{name} indicou que a sua nova conta é agora:", "account.moved_to": "{name} indicou que a sua nova conta é agora:",
"account.mute": "Silenciar @{name}", "account.mute": "Silenciar @{name}",
"account.mute_notifications_short": "Silenciar notificações",
"account.mute_short": "Silenciar",
"account.muted": "Silenciada", "account.muted": "Silenciada",
"account.no_bio": "Nenhuma descrição fornecida.",
"account.open_original_page": "Abrir a página original", "account.open_original_page": "Abrir a página original",
"account.posts": "Publicações", "account.posts": "Publicações",
"account.posts_with_replies": "Publicações e respostas", "account.posts_with_replies": "Publicações e respostas",
@ -64,6 +68,7 @@
"account.unendorse": "Não destacar no perfil", "account.unendorse": "Não destacar no perfil",
"account.unfollow": "Deixar de seguir", "account.unfollow": "Deixar de seguir",
"account.unmute": "Deixar de silenciar @{name}", "account.unmute": "Deixar de silenciar @{name}",
"account.unmute_notifications_short": "Parar de silenciar notificações",
"account.unmute_short": "Deixar de silenciar", "account.unmute_short": "Deixar de silenciar",
"account_note.placeholder": "Clique para adicionar nota", "account_note.placeholder": "Clique para adicionar nota",
"admin.dashboard.daily_retention": "Taxa de retenção de utilizadores por dia após a inscrição", "admin.dashboard.daily_retention": "Taxa de retenção de utilizadores por dia após a inscrição",
@ -71,6 +76,10 @@
"admin.dashboard.retention.average": "Média", "admin.dashboard.retention.average": "Média",
"admin.dashboard.retention.cohort": "Mês de inscrição", "admin.dashboard.retention.cohort": "Mês de inscrição",
"admin.dashboard.retention.cohort_size": "Novos utilizadores", "admin.dashboard.retention.cohort_size": "Novos utilizadores",
"admin.impact_report.instance_accounts": "Perfis de contas que isto eliminaria",
"admin.impact_report.instance_followers": "Seguidores que os nossos utilizadores perderiam",
"admin.impact_report.instance_follows": "Seguidores que os utilizadores deles perderiam",
"admin.impact_report.title": "Resumo do impacto",
"alert.rate_limited.message": "Volte a tentar depois das {retry_time, time, medium}.", "alert.rate_limited.message": "Volte a tentar depois das {retry_time, time, medium}.",
"alert.rate_limited.title": "Limite de tentativas", "alert.rate_limited.title": "Limite de tentativas",
"alert.unexpected.message": "Ocorreu um erro inesperado.", "alert.unexpected.message": "Ocorreu um erro inesperado.",
@ -105,6 +114,7 @@
"column.directory": "Explorar perfis", "column.directory": "Explorar perfis",
"column.domain_blocks": "Domínios bloqueados", "column.domain_blocks": "Domínios bloqueados",
"column.favourites": "Preferidos", "column.favourites": "Preferidos",
"column.firehose": "Cronologias",
"column.follow_requests": "Seguidores pendentes", "column.follow_requests": "Seguidores pendentes",
"column.home": "Início", "column.home": "Início",
"column.lists": "Listas", "column.lists": "Listas",
@ -125,6 +135,8 @@
"community.column_settings.remote_only": "Apenas remoto", "community.column_settings.remote_only": "Apenas remoto",
"compose.language.change": "Alterar língua", "compose.language.change": "Alterar língua",
"compose.language.search": "Pesquisar línguas...", "compose.language.search": "Pesquisar línguas...",
"compose.published.body": "Publicado.",
"compose.published.open": "Abrir",
"compose_form.direct_message_warning_learn_more": "Conhecer mais", "compose_form.direct_message_warning_learn_more": "Conhecer mais",
"compose_form.encryption_warning": "As publicações no Mastodon não são criptografadas de ponta a ponta. Não partilhe nenhuma informação sensível através do Mastodon.", "compose_form.encryption_warning": "As publicações no Mastodon não são criptografadas de ponta a ponta. Não partilhe nenhuma informação sensível através do Mastodon.",
"compose_form.hashtag_warning": "Esta publicação não será listada em qualquer etiqueta, pois não é pública. Apenas as publicações públicas podem ser pesquisadas por etiquetas.", "compose_form.hashtag_warning": "Esta publicação não será listada em qualquer etiqueta, pois não é pública. Apenas as publicações públicas podem ser pesquisadas por etiquetas.",
@ -192,6 +204,7 @@
"dismissable_banner.explore_links": "Essas histórias de notícias estão, no momento, a ser faladas por pessoas neste e noutros servidores da rede descentralizada.", "dismissable_banner.explore_links": "Essas histórias de notícias estão, no momento, a ser faladas por pessoas neste e noutros servidores da rede descentralizada.",
"dismissable_banner.explore_statuses": "Estas publicações, deste e de outros servidores na rede descentralizada, estão, neste momento, a ganhar atenção neste servidor.", "dismissable_banner.explore_statuses": "Estas publicações, deste e de outros servidores na rede descentralizada, estão, neste momento, a ganhar atenção neste servidor.",
"dismissable_banner.explore_tags": "Estas #etiquetas estão presentemente a ganhar atenção entre as pessoas neste e noutros servidores da rede descentralizada.", "dismissable_banner.explore_tags": "Estas #etiquetas estão presentemente a ganhar atenção entre as pessoas neste e noutros servidores da rede descentralizada.",
"dismissable_banner.public_timeline": "Estas são as publicações públicas mais recentes de pessoas na rede social que as pessoas em {domain} seguem.",
"embed.instructions": "Incorpore esta publicação no seu site copiando o código abaixo.", "embed.instructions": "Incorpore esta publicação no seu site copiando o código abaixo.",
"embed.preview": "Podes ver aqui como irá ficar:", "embed.preview": "Podes ver aqui como irá ficar:",
"emoji_button.activity": "Actividade", "emoji_button.activity": "Actividade",
@ -257,6 +270,9 @@
"filter_modal.select_filter.subtitle": "Utilize uma categoria existente ou crie uma nova", "filter_modal.select_filter.subtitle": "Utilize uma categoria existente ou crie uma nova",
"filter_modal.select_filter.title": "Filtrar esta publicação", "filter_modal.select_filter.title": "Filtrar esta publicação",
"filter_modal.title.status": "Filtrar uma publicação", "filter_modal.title.status": "Filtrar uma publicação",
"firehose.all": "Todas",
"firehose.local": "Este servidor",
"firehose.remote": "Outros servidores",
"follow_request.authorize": "Autorizar", "follow_request.authorize": "Autorizar",
"follow_request.reject": "Rejeitar", "follow_request.reject": "Rejeitar",
"follow_requests.unlocked_explanation": "Apesar de a sua não ser privada, a administração de {domain} pensa que poderá querer rever manualmente os pedidos de seguimento dessas contas.", "follow_requests.unlocked_explanation": "Apesar de a sua não ser privada, a administração de {domain} pensa que poderá querer rever manualmente os pedidos de seguimento dessas contas.",
@ -282,9 +298,13 @@
"hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionais para esta coluna", "hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionais para esta coluna",
"hashtag.follow": "Seguir #etiqueta", "hashtag.follow": "Seguir #etiqueta",
"hashtag.unfollow": "Deixar de seguir #etiqueta", "hashtag.unfollow": "Deixar de seguir #etiqueta",
"home.actions.go_to_explore": "Veja as tendências atuais",
"home.actions.go_to_suggestions": "Encontrar pessoas para seguir",
"home.column_settings.basic": "Básico", "home.column_settings.basic": "Básico",
"home.column_settings.show_reblogs": "Mostrar impulsos", "home.column_settings.show_reblogs": "Mostrar impulsos",
"home.column_settings.show_replies": "Mostrar respostas", "home.column_settings.show_replies": "Mostrar respostas",
"home.explore_prompt.body": "A sua página inicial terá uma mistura de publicações com as hashtags que escolheu seguir, das pessoas que escolheu seguir e as publicações que elas partilham. Parece bastante quieto por agora, então que tal:",
"home.explore_prompt.title": "Esta é a sua base principal dentro do Mastodon.",
"home.hide_announcements": "Ocultar comunicações", "home.hide_announcements": "Ocultar comunicações",
"home.show_announcements": "Exibir comunicações", "home.show_announcements": "Exibir comunicações",
"interaction_modal.description.favourite": "Com uma conta no Mastodon, pode adicionar esta publicação aos marcadores para que o autor saiba que gostou e guardá-la para mais tarde.", "interaction_modal.description.favourite": "Com uma conta no Mastodon, pode adicionar esta publicação aos marcadores para que o autor saiba que gostou e guardá-la para mais tarde.",
@ -348,6 +368,7 @@
"lists.delete": "Eliminar lista", "lists.delete": "Eliminar lista",
"lists.edit": "Editar lista", "lists.edit": "Editar lista",
"lists.edit.submit": "Mudar o título", "lists.edit.submit": "Mudar o título",
"lists.exclusive": "Ocultar essas publicações da página inicial",
"lists.new.create": "Adicionar lista", "lists.new.create": "Adicionar lista",
"lists.new.title_placeholder": "Título da nova lista", "lists.new.title_placeholder": "Título da nova lista",
"lists.replies_policy.followed": "Qualquer utilizador seguido", "lists.replies_policy.followed": "Qualquer utilizador seguido",
@ -364,6 +385,7 @@
"mute_modal.hide_notifications": "Esconder notificações deste utilizador?", "mute_modal.hide_notifications": "Esconder notificações deste utilizador?",
"mute_modal.indefinite": "Indefinidamente", "mute_modal.indefinite": "Indefinidamente",
"navigation_bar.about": "Sobre", "navigation_bar.about": "Sobre",
"navigation_bar.advanced_interface": "Abrir na interface web avançada",
"navigation_bar.blocks": "Utilizadores bloqueados", "navigation_bar.blocks": "Utilizadores bloqueados",
"navigation_bar.bookmarks": "Marcadores", "navigation_bar.bookmarks": "Marcadores",
"navigation_bar.community_timeline": "Cronologia local", "navigation_bar.community_timeline": "Cronologia local",
@ -468,6 +490,7 @@
"picture_in_picture.restore": "Colocá-lo de volta", "picture_in_picture.restore": "Colocá-lo de volta",
"poll.closed": "Fechado", "poll.closed": "Fechado",
"poll.refresh": "Recarregar", "poll.refresh": "Recarregar",
"poll.reveal": "Ver resultados",
"poll.total_people": "{count, plural, one {# pessoa} other {# pessoas}}", "poll.total_people": "{count, plural, one {# pessoa} other {# pessoas}}",
"poll.total_votes": "{count, plural, one {# voto} other {# votos}}", "poll.total_votes": "{count, plural, one {# voto} other {# votos}}",
"poll.vote": "Votar", "poll.vote": "Votar",
@ -520,6 +543,8 @@
"report.placeholder": "Comentários adicionais", "report.placeholder": "Comentários adicionais",
"report.reasons.dislike": "Não gosto disto", "report.reasons.dislike": "Não gosto disto",
"report.reasons.dislike_description": "Não é algo que deseje ver", "report.reasons.dislike_description": "Não é algo que deseje ver",
"report.reasons.legal": "É ilegal",
"report.reasons.legal_description": "Acredita que isto viola a lei do seu país ou do país do seu servidor",
"report.reasons.other": "É outra coisa", "report.reasons.other": "É outra coisa",
"report.reasons.other_description": "O problema não se encaixa nas outras categorias", "report.reasons.other_description": "O problema não se encaixa nas outras categorias",
"report.reasons.spam": "É spam", "report.reasons.spam": "É spam",
@ -539,6 +564,7 @@
"report.unfollow": "Deixar de seguir @{name}", "report.unfollow": "Deixar de seguir @{name}",
"report.unfollow_explanation": "Está a seguir esta conta. Para não ver mais as publicações desta conta na sua página inicial, deixe de segui-la.", "report.unfollow_explanation": "Está a seguir esta conta. Para não ver mais as publicações desta conta na sua página inicial, deixe de segui-la.",
"report_notification.attached_statuses": "{count, plural,one {{count} publicação} other {{count} publicações}} em anexo", "report_notification.attached_statuses": "{count, plural,one {{count} publicação} other {{count} publicações}} em anexo",
"report_notification.categories.legal": "Legal",
"report_notification.categories.other": "Outro", "report_notification.categories.other": "Outro",
"report_notification.categories.spam": "Spam", "report_notification.categories.spam": "Spam",
"report_notification.categories.violation": "Violação de regra", "report_notification.categories.violation": "Violação de regra",
@ -593,6 +619,8 @@
"status.history.created": "{name} criado em {date}", "status.history.created": "{name} criado em {date}",
"status.history.edited": "{name} editado em {date}", "status.history.edited": "{name} editado em {date}",
"status.load_more": "Carregar mais", "status.load_more": "Carregar mais",
"status.media.open": "Clique para abrir",
"status.media.show": "Clique para ver",
"status.media_hidden": "Media escondida", "status.media_hidden": "Media escondida",
"status.mention": "Mencionar @{name}", "status.mention": "Mencionar @{name}",
"status.more": "Mais", "status.more": "Mais",
@ -623,6 +651,7 @@
"status.title.with_attachments": "{user} publicou {attachmentCount, plural,one {um anexo} other {{attachmentCount} anexos}}", "status.title.with_attachments": "{user} publicou {attachmentCount, plural,one {um anexo} other {{attachmentCount} anexos}}",
"status.translate": "Traduzir", "status.translate": "Traduzir",
"status.translated_from_with": "Traduzido do {lang} usando {provider}", "status.translated_from_with": "Traduzido do {lang} usando {provider}",
"status.uncached_media_warning": "Pré-visualização não disponível",
"status.unmute_conversation": "Deixar de silenciar esta conversa", "status.unmute_conversation": "Deixar de silenciar esta conversa",
"status.unpin": "Desafixar do perfil", "status.unpin": "Desafixar do perfil",
"subscribed_languages.lead": "Após a alteração, apenas as publicações nas línguas seleccionadas aparecerão na sua página inicial e listas. Não selecione nenhuma para receber publicações de todas as línguas.", "subscribed_languages.lead": "Após a alteração, apenas as publicações nas línguas seleccionadas aparecerão na sua página inicial e listas. Não selecione nenhuma para receber publicações de todas as línguas.",

View file

@ -78,6 +78,8 @@
"admin.dashboard.retention.cohort_size": "Новые пользователи", "admin.dashboard.retention.cohort_size": "Новые пользователи",
"admin.impact_report.instance_accounts": "Профили учетных записей, которые будут удалены", "admin.impact_report.instance_accounts": "Профили учетных записей, которые будут удалены",
"admin.impact_report.instance_followers": "Последователи, которых потеряют наши пользователи", "admin.impact_report.instance_followers": "Последователи, которых потеряют наши пользователи",
"admin.impact_report.instance_follows": "Последователи, которых потеряют наши пользователи",
"admin.impact_report.title": "Резюме воздействия",
"alert.rate_limited.message": "Пожалуйста, повторите после {retry_time, time, medium}.", "alert.rate_limited.message": "Пожалуйста, повторите после {retry_time, time, medium}.",
"alert.rate_limited.title": "Ограничение количества запросов", "alert.rate_limited.title": "Ограничение количества запросов",
"alert.unexpected.message": "Произошла непредвиденная ошибка.", "alert.unexpected.message": "Произошла непредвиденная ошибка.",
@ -112,6 +114,7 @@
"column.directory": "Просмотр профилей", "column.directory": "Просмотр профилей",
"column.domain_blocks": "Заблокированные домены", "column.domain_blocks": "Заблокированные домены",
"column.favourites": "Избранное", "column.favourites": "Избранное",
"column.firehose": "Живая лента",
"column.follow_requests": "Запросы на подписку", "column.follow_requests": "Запросы на подписку",
"column.home": "Главная", "column.home": "Главная",
"column.lists": "Списки", "column.lists": "Списки",
@ -132,6 +135,8 @@
"community.column_settings.remote_only": "Только удалённые", "community.column_settings.remote_only": "Только удалённые",
"compose.language.change": "Изменить язык", "compose.language.change": "Изменить язык",
"compose.language.search": "Поиск языков...", "compose.language.search": "Поиск языков...",
"compose.published.body": "Запись опубликована.",
"compose.published.open": "Открыть",
"compose_form.direct_message_warning_learn_more": "Подробнее", "compose_form.direct_message_warning_learn_more": "Подробнее",
"compose_form.encryption_warning": "Посты в Mastodon не защищены сквозным шифрованием. Не делитесь конфиденциальной информацией через Mastodon.", "compose_form.encryption_warning": "Посты в Mastodon не защищены сквозным шифрованием. Не делитесь конфиденциальной информацией через Mastodon.",
"compose_form.hashtag_warning": "Этот пост не будет виден ни под одним из хэштегов, так как он не публичный. Только публичные посты можно найти по хэштегу.", "compose_form.hashtag_warning": "Этот пост не будет виден ни под одним из хэштегов, так как он не публичный. Только публичные посты можно найти по хэштегу.",
@ -199,6 +204,7 @@
"dismissable_banner.explore_links": "Об этих новостях прямо сейчас говорят люди на этом и других серверах децентрализованной сети.", "dismissable_banner.explore_links": "Об этих новостях прямо сейчас говорят люди на этом и других серверах децентрализованной сети.",
"dismissable_banner.explore_statuses": "Эти сообщения с этого и других серверов в децентрализованной сети сейчас набирают популярность на этом сервере.", "dismissable_banner.explore_statuses": "Эти сообщения с этого и других серверов в децентрализованной сети сейчас набирают популярность на этом сервере.",
"dismissable_banner.explore_tags": "Эти хэштеги привлекают людей на этом и других серверах децентрализованной сети прямо сейчас.", "dismissable_banner.explore_tags": "Эти хэштеги привлекают людей на этом и других серверах децентрализованной сети прямо сейчас.",
"dismissable_banner.public_timeline": "Это самые последние публичные сообщения от людей в социальной сети, за которыми подписались пользователи {domain}.",
"embed.instructions": "Встройте этот пост на свой сайт, скопировав следующий код:", "embed.instructions": "Встройте этот пост на свой сайт, скопировав следующий код:",
"embed.preview": "Так это будет выглядеть:", "embed.preview": "Так это будет выглядеть:",
"emoji_button.activity": "Занятия", "emoji_button.activity": "Занятия",
@ -264,6 +270,9 @@
"filter_modal.select_filter.subtitle": "Используйте существующую категорию или создайте новую", "filter_modal.select_filter.subtitle": "Используйте существующую категорию или создайте новую",
"filter_modal.select_filter.title": "Фильтровать этот пост", "filter_modal.select_filter.title": "Фильтровать этот пост",
"filter_modal.title.status": "Фильтровать пост", "filter_modal.title.status": "Фильтровать пост",
"firehose.all": "Все",
"firehose.local": "Текущий сервер",
"firehose.remote": "Другие серверы",
"follow_request.authorize": "Авторизовать", "follow_request.authorize": "Авторизовать",
"follow_request.reject": "Отказать", "follow_request.reject": "Отказать",
"follow_requests.unlocked_explanation": "Хотя ваша учетная запись не закрыта, команда {domain} подумала, что вы захотите просмотреть запросы от этих учетных записей вручную.", "follow_requests.unlocked_explanation": "Хотя ваша учетная запись не закрыта, команда {domain} подумала, что вы захотите просмотреть запросы от этих учетных записей вручную.",
@ -289,9 +298,13 @@
"hashtag.column_settings.tag_toggle": "Включить дополнительные теги для этой колонки", "hashtag.column_settings.tag_toggle": "Включить дополнительные теги для этой колонки",
"hashtag.follow": "Подписаться на новые посты", "hashtag.follow": "Подписаться на новые посты",
"hashtag.unfollow": "Отписаться", "hashtag.unfollow": "Отписаться",
"home.actions.go_to_explore": "Посмотреть, что актуально",
"home.actions.go_to_suggestions": "Подпишитесь на людей",
"home.column_settings.basic": "Основные", "home.column_settings.basic": "Основные",
"home.column_settings.show_reblogs": "Показывать продвижения", "home.column_settings.show_reblogs": "Показывать продвижения",
"home.column_settings.show_replies": "Показывать ответы", "home.column_settings.show_replies": "Показывать ответы",
"home.explore_prompt.body": "В вашем доме появятся сообщения из хэштегов, на которые вы хотите подписаться, люди, которых вы выбрали подписаться, и сообщения, которые они увеличили. Сейчас выглядит спокойно, так что:",
"home.explore_prompt.title": "Это ваша домашняя база в Мастодоне.",
"home.hide_announcements": "Скрыть объявления", "home.hide_announcements": "Скрыть объявления",
"home.show_announcements": "Показать объявления", "home.show_announcements": "Показать объявления",
"interaction_modal.description.favourite": "С учётной записью Mastodon, вы можете добавить этот пост в избранное, чтобы сохранить его на будущее и дать автору знать, что пост вам понравился.", "interaction_modal.description.favourite": "С учётной записью Mastodon, вы можете добавить этот пост в избранное, чтобы сохранить его на будущее и дать автору знать, что пост вам понравился.",
@ -355,6 +368,7 @@
"lists.delete": "Удалить список", "lists.delete": "Удалить список",
"lists.edit": "Изменить список", "lists.edit": "Изменить список",
"lists.edit.submit": "Изменить название", "lists.edit.submit": "Изменить название",
"lists.exclusive": "Скрыть эти сообщения из дома",
"lists.new.create": "Создать список", "lists.new.create": "Создать список",
"lists.new.title_placeholder": "Название для нового списка", "lists.new.title_placeholder": "Название для нового списка",
"lists.replies_policy.followed": "Любой подписанный пользователь", "lists.replies_policy.followed": "Любой подписанный пользователь",
@ -371,6 +385,7 @@
"mute_modal.hide_notifications": "Скрыть уведомления от этого пользователя?", "mute_modal.hide_notifications": "Скрыть уведомления от этого пользователя?",
"mute_modal.indefinite": "Не определена", "mute_modal.indefinite": "Не определена",
"navigation_bar.about": "О проекте", "navigation_bar.about": "О проекте",
"navigation_bar.advanced_interface": "Включить многоколоночный интерфейс",
"navigation_bar.blocks": "Заблокированные пользователи", "navigation_bar.blocks": "Заблокированные пользователи",
"navigation_bar.bookmarks": "Закладки", "navigation_bar.bookmarks": "Закладки",
"navigation_bar.community_timeline": "Локальная лента", "navigation_bar.community_timeline": "Локальная лента",
@ -475,6 +490,7 @@
"picture_in_picture.restore": "Вернуть обратно", "picture_in_picture.restore": "Вернуть обратно",
"poll.closed": "Завершён", "poll.closed": "Завершён",
"poll.refresh": "Обновить", "poll.refresh": "Обновить",
"poll.reveal": "Результаты",
"poll.total_people": "{count, plural, one {# человек} few {# человека} many {# человек} other {# человек}}", "poll.total_people": "{count, plural, one {# человек} few {# человека} many {# человек} other {# человек}}",
"poll.total_votes": "{count, plural, one {# голос} few {# голоса} many {# голосов} other {# голосов}}", "poll.total_votes": "{count, plural, one {# голос} few {# голоса} many {# голосов} other {# голосов}}",
"poll.vote": "Голосовать", "poll.vote": "Голосовать",
@ -527,6 +543,8 @@
"report.placeholder": "Дополнительные комментарии", "report.placeholder": "Дополнительные комментарии",
"report.reasons.dislike": "Мне не нравится", "report.reasons.dislike": "Мне не нравится",
"report.reasons.dislike_description": "Не хотел(а) бы видеть такой контент", "report.reasons.dislike_description": "Не хотел(а) бы видеть такой контент",
"report.reasons.legal": "Это незаконно",
"report.reasons.legal_description": "Вы считаете, что оно нарушает закон вашей страны или сервера",
"report.reasons.other": "Другое", "report.reasons.other": "Другое",
"report.reasons.other_description": "Проблема не попадает ни под одну из категорий", "report.reasons.other_description": "Проблема не попадает ни под одну из категорий",
"report.reasons.spam": "Это спам", "report.reasons.spam": "Это спам",
@ -546,6 +564,7 @@
"report.unfollow": "Отписаться от @{name}", "report.unfollow": "Отписаться от @{name}",
"report.unfollow_explanation": "Вы подписаны на этого пользователя. Чтобы не видеть его/её посты в своей домашней ленте, отпишитесь от него/неё.", "report.unfollow_explanation": "Вы подписаны на этого пользователя. Чтобы не видеть его/её посты в своей домашней ленте, отпишитесь от него/неё.",
"report_notification.attached_statuses": "{count, plural, one {{count} сообщение} few {{count} сообщения} many {{count} сообщений} other {{count} сообщений}} вложено", "report_notification.attached_statuses": "{count, plural, one {{count} сообщение} few {{count} сообщения} many {{count} сообщений} other {{count} сообщений}} вложено",
"report_notification.categories.legal": "Правовая информация",
"report_notification.categories.other": "Прочее", "report_notification.categories.other": "Прочее",
"report_notification.categories.spam": "Спам", "report_notification.categories.spam": "Спам",
"report_notification.categories.violation": "Нарушение правил", "report_notification.categories.violation": "Нарушение правил",
@ -600,6 +619,8 @@
"status.history.created": "{name} создал {date}", "status.history.created": "{name} создал {date}",
"status.history.edited": "{name} отредактировал(а) {date}", "status.history.edited": "{name} отредактировал(а) {date}",
"status.load_more": "Загрузить остальное", "status.load_more": "Загрузить остальное",
"status.media.open": "Нажмите, чтобы открыть.",
"status.media.show": "Нажмите для просмотра",
"status.media_hidden": "Файл скрыт", "status.media_hidden": "Файл скрыт",
"status.mention": "Упомянуть @{name}", "status.mention": "Упомянуть @{name}",
"status.more": "Ещё", "status.more": "Ещё",
@ -630,6 +651,7 @@
"status.title.with_attachments": "{user} размещено {attachmentCount, plural, one {вложение} other {{attachmentCount} вложений}}", "status.title.with_attachments": "{user} размещено {attachmentCount, plural, one {вложение} other {{attachmentCount} вложений}}",
"status.translate": "Перевод", "status.translate": "Перевод",
"status.translated_from_with": "Переведено с {lang}, используя {provider}", "status.translated_from_with": "Переведено с {lang}, используя {provider}",
"status.uncached_media_warning": "Прослушивание недоступно",
"status.unmute_conversation": "Не игнорировать обсуждение", "status.unmute_conversation": "Не игнорировать обсуждение",
"status.unpin": "Открепить от профиля", "status.unpin": "Открепить от профиля",
"subscribed_languages.lead": "Посты только на выбранных языках будут отображаться на вашей домашней странице и в списке лент после изменения. Выберите «Нет», чтобы получать посты на всех языках.", "subscribed_languages.lead": "Посты только на выбранных языках будут отображаться на вашей домашней странице и в списке лент после изменения. Выберите «Нет», чтобы получать посты на всех языках.",

View file

@ -75,6 +75,10 @@
"admin.dashboard.retention.average": "Povprečje", "admin.dashboard.retention.average": "Povprečje",
"admin.dashboard.retention.cohort": "Mesec prijave", "admin.dashboard.retention.cohort": "Mesec prijave",
"admin.dashboard.retention.cohort_size": "Novi uporabniki", "admin.dashboard.retention.cohort_size": "Novi uporabniki",
"admin.impact_report.instance_accounts": "Profili računov, ki bi jih s tem izbrisali",
"admin.impact_report.instance_followers": "Sledilci, ki bi jih izgubili naši uporabniki",
"admin.impact_report.instance_follows": "Sledilci, ki bi jih izgubili njihovi uporabniki",
"admin.impact_report.title": "Povzetek učinka",
"alert.rate_limited.message": "Poskusite znova čez {retry_time, time, medium}.", "alert.rate_limited.message": "Poskusite znova čez {retry_time, time, medium}.",
"alert.rate_limited.title": "Hitrost omejena", "alert.rate_limited.title": "Hitrost omejena",
"alert.unexpected.message": "Zgodila se je nepričakovana napaka.", "alert.unexpected.message": "Zgodila se je nepričakovana napaka.",
@ -376,6 +380,7 @@
"mute_modal.hide_notifications": "Ali želite skriti obvestila tega uporabnika?", "mute_modal.hide_notifications": "Ali želite skriti obvestila tega uporabnika?",
"mute_modal.indefinite": "Nedoločeno", "mute_modal.indefinite": "Nedoločeno",
"navigation_bar.about": "O Mastodonu", "navigation_bar.about": "O Mastodonu",
"navigation_bar.advanced_interface": "Odpri v naprednem spletnem vmesniku",
"navigation_bar.blocks": "Blokirani uporabniki", "navigation_bar.blocks": "Blokirani uporabniki",
"navigation_bar.bookmarks": "Zaznamki", "navigation_bar.bookmarks": "Zaznamki",
"navigation_bar.community_timeline": "Krajevna časovnica", "navigation_bar.community_timeline": "Krajevna časovnica",
@ -480,6 +485,7 @@
"picture_in_picture.restore": "Postavi nazaj", "picture_in_picture.restore": "Postavi nazaj",
"poll.closed": "Zaprto", "poll.closed": "Zaprto",
"poll.refresh": "Osveži", "poll.refresh": "Osveži",
"poll.reveal": "Poglej rezultate",
"poll.total_people": "{count, plural, one {# oseba} two {# osebi} few {# osebe} other {# oseb}}", "poll.total_people": "{count, plural, one {# oseba} two {# osebi} few {# osebe} other {# oseb}}",
"poll.total_votes": "{count, plural, one {# glas} two {# glasova} few {# glasovi} other {# glasov}}", "poll.total_votes": "{count, plural, one {# glas} two {# glasova} few {# glasovi} other {# glasov}}",
"poll.vote": "Glasuj", "poll.vote": "Glasuj",
@ -532,6 +538,7 @@
"report.placeholder": "Dodatni komentarji", "report.placeholder": "Dodatni komentarji",
"report.reasons.dislike": "Ni mi všeč", "report.reasons.dislike": "Ni mi všeč",
"report.reasons.dislike_description": "To ni tisto, kar želim videti", "report.reasons.dislike_description": "To ni tisto, kar želim videti",
"report.reasons.legal": "To ni legalno",
"report.reasons.other": "Gre za nekaj drugega", "report.reasons.other": "Gre za nekaj drugega",
"report.reasons.other_description": "Težava ne sodi v druge kategorije", "report.reasons.other_description": "Težava ne sodi v druge kategorije",
"report.reasons.spam": "To je neželena vsebina", "report.reasons.spam": "To je neželena vsebina",
@ -551,6 +558,7 @@
"report.unfollow": "Ne sledi več @{name}", "report.unfollow": "Ne sledi več @{name}",
"report.unfollow_explanation": "Temu računu sledite. Da ne boste več videli njegovih objav v svojem domačem viru, mu prenehajte slediti.", "report.unfollow_explanation": "Temu računu sledite. Da ne boste več videli njegovih objav v svojem domačem viru, mu prenehajte slediti.",
"report_notification.attached_statuses": "{count, plural, one {{count} objava pripeta} two {{count} objavi pripeti} few {{count} objave pripete} other {{count} objav pripetih}}", "report_notification.attached_statuses": "{count, plural, one {{count} objava pripeta} two {{count} objavi pripeti} few {{count} objave pripete} other {{count} objav pripetih}}",
"report_notification.categories.legal": "Legalno",
"report_notification.categories.other": "Drugo", "report_notification.categories.other": "Drugo",
"report_notification.categories.spam": "Neželeno", "report_notification.categories.spam": "Neželeno",
"report_notification.categories.violation": "Kršitev pravila", "report_notification.categories.violation": "Kršitev pravila",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Të kalohen të fshehura njoftimet prej këtij përdoruesi?", "mute_modal.hide_notifications": "Të kalohen të fshehura njoftimet prej këtij përdoruesi?",
"mute_modal.indefinite": "E pacaktuar", "mute_modal.indefinite": "E pacaktuar",
"navigation_bar.about": "Mbi", "navigation_bar.about": "Mbi",
"navigation_bar.advanced_interface": "Ireki web interfaze aurreratuan",
"navigation_bar.blocks": "Përdorues të bllokuar", "navigation_bar.blocks": "Përdorues të bllokuar",
"navigation_bar.bookmarks": "Faqerojtës", "navigation_bar.bookmarks": "Faqerojtës",
"navigation_bar.community_timeline": "Rrjedhë kohore vendore", "navigation_bar.community_timeline": "Rrjedhë kohore vendore",

View file

@ -209,7 +209,7 @@
"emoji_button.food": "Mat & dryck", "emoji_button.food": "Mat & dryck",
"emoji_button.label": "Lägg till emoji", "emoji_button.label": "Lägg till emoji",
"emoji_button.nature": "Natur", "emoji_button.nature": "Natur",
"emoji_button.not_found": "Inga emojos!! (╯°□°)╯︵ ┻━┻", "emoji_button.not_found": "Inga matchande emojis hittades",
"emoji_button.objects": "Objekt", "emoji_button.objects": "Objekt",
"emoji_button.people": "Personer", "emoji_button.people": "Personer",
"emoji_button.recent": "Ofta använda", "emoji_button.recent": "Ofta använda",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Bu kullanıcıdan bildirimler gizlensin mı?", "mute_modal.hide_notifications": "Bu kullanıcıdan bildirimler gizlensin mı?",
"mute_modal.indefinite": "Belirsiz", "mute_modal.indefinite": "Belirsiz",
"navigation_bar.about": "Hakkında", "navigation_bar.about": "Hakkında",
"navigation_bar.advanced_interface": "Gelişmiş web arayüzünde aç",
"navigation_bar.blocks": "Engellenen kullanıcılar", "navigation_bar.blocks": "Engellenen kullanıcılar",
"navigation_bar.bookmarks": "Yer İşaretleri", "navigation_bar.bookmarks": "Yer İşaretleri",
"navigation_bar.community_timeline": "Yerel Zaman Tüneli", "navigation_bar.community_timeline": "Yerel Zaman Tüneli",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "Ẩn thông báo từ người này?", "mute_modal.hide_notifications": "Ẩn thông báo từ người này?",
"mute_modal.indefinite": "Vĩnh viễn", "mute_modal.indefinite": "Vĩnh viễn",
"navigation_bar.about": "Giới thiệu", "navigation_bar.about": "Giới thiệu",
"navigation_bar.advanced_interface": "Dùng bố cục nhiều cột",
"navigation_bar.blocks": "Người đã chặn", "navigation_bar.blocks": "Người đã chặn",
"navigation_bar.bookmarks": "Đã lưu", "navigation_bar.bookmarks": "Đã lưu",
"navigation_bar.community_timeline": "Cộng đồng", "navigation_bar.community_timeline": "Cộng đồng",
@ -648,7 +649,7 @@
"status.show_more_all": "Hiển thị tất cả", "status.show_more_all": "Hiển thị tất cả",
"status.show_original": "Bản gốc", "status.show_original": "Bản gốc",
"status.title.with_attachments": "{user} đã đăng {attachmentCount, plural, other {{attachmentCount} đính kèm}}", "status.title.with_attachments": "{user} đã đăng {attachmentCount, plural, other {{attachmentCount} đính kèm}}",
"status.translate": "Dịch", "status.translate": "Dịch Tút",
"status.translated_from_with": "Dịch từ {lang} bằng {provider}", "status.translated_from_with": "Dịch từ {lang} bằng {provider}",
"status.uncached_media_warning": "Xem trước không sẵn có", "status.uncached_media_warning": "Xem trước không sẵn có",
"status.unmute_conversation": "Quan tâm", "status.unmute_conversation": "Quan tâm",

View file

@ -385,6 +385,7 @@
"mute_modal.hide_notifications": "是否隱藏來自這位使用者的通知?", "mute_modal.hide_notifications": "是否隱藏來自這位使用者的通知?",
"mute_modal.indefinite": "無期限", "mute_modal.indefinite": "無期限",
"navigation_bar.about": "關於", "navigation_bar.about": "關於",
"navigation_bar.advanced_interface": "以進階網頁介面開啟",
"navigation_bar.blocks": "已封鎖的使用者", "navigation_bar.blocks": "已封鎖的使用者",
"navigation_bar.bookmarks": "書籤", "navigation_bar.bookmarks": "書籤",
"navigation_bar.community_timeline": "本站時間軸", "navigation_bar.community_timeline": "本站時間軸",

View file

@ -5,11 +5,16 @@ import { normalizeStatusTranslation } from '../actions/importer/normalizer';
import { import {
REBLOG_REQUEST, REBLOG_REQUEST,
REBLOG_FAIL, REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST, FAVOURITE_REQUEST,
FAVOURITE_FAIL, FAVOURITE_FAIL,
UNFAVOURITE_SUCCESS, UNFAVOURITE_REQUEST,
UNFAVOURITE_FAIL,
BOOKMARK_REQUEST, BOOKMARK_REQUEST,
BOOKMARK_FAIL, BOOKMARK_FAIL,
UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL,
} from '../actions/interactions'; } from '../actions/interactions';
import { import {
STATUS_MUTE_SUCCESS, STATUS_MUTE_SUCCESS,
@ -96,18 +101,28 @@ export default function statuses(state = initialState, action) {
return importStatuses(state, action.statuses); return importStatuses(state, action.statuses);
case FAVOURITE_REQUEST: case FAVOURITE_REQUEST:
return state.setIn([action.status.get('id'), 'favourited'], true); return state.setIn([action.status.get('id'), 'favourited'], true);
case UNFAVOURITE_SUCCESS:
return state.updateIn([action.status.get('id'), 'favourites_count'], x => Math.max(0, x - 1));
case FAVOURITE_FAIL: case FAVOURITE_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], false);
case UNFAVOURITE_REQUEST:
return state.setIn([action.status.get('id'), 'favourited'], false);
case UNFAVOURITE_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'favourited'], true);
case BOOKMARK_REQUEST: case BOOKMARK_REQUEST:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true);
case BOOKMARK_FAIL: case BOOKMARK_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false);
case UNBOOKMARK_REQUEST:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false);
case UNBOOKMARK_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true);
case REBLOG_REQUEST: case REBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], true); return state.setIn([action.status.get('id'), 'reblogged'], true);
case REBLOG_FAIL: case REBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], true);
case STATUS_MUTE_SUCCESS: case STATUS_MUTE_SUCCESS:
return state.setIn([action.id, 'muted'], true); return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS: case STATUS_UNMUTE_SUCCESS:

View file

@ -24,13 +24,16 @@ html {
.column > .scrollable, .column > .scrollable,
.getting-started, .getting-started,
.column-inline-form, .column-inline-form,
.error-column,
.regeneration-indicator { .regeneration-indicator {
background: $white; background: $white;
border: 1px solid lighten($ui-base-color, 8%); border: 1px solid lighten($ui-base-color, 8%);
border-top: 0; border-top: 0;
} }
.error-column {
border: 1px solid lighten($ui-base-color, 8%);
}
.column > .scrollable.about { .column > .scrollable.about {
border-top: 1px solid lighten($ui-base-color, 8%); border-top: 1px solid lighten($ui-base-color, 8%);
} }
@ -77,6 +80,10 @@ html {
background: $white; background: $white;
} }
.column-header {
border-bottom: 0;
}
.column-header__button.active { .column-header__button.active {
color: $ui-highlight-color; color: $ui-highlight-color;
@ -423,7 +430,7 @@ html {
.column-header__collapsible-inner { .column-header__collapsible-inner {
background: darken($ui-base-color, 4%); background: darken($ui-base-color, 4%);
border: 1px solid lighten($ui-base-color, 8%); border: 1px solid lighten($ui-base-color, 8%);
border-top: 0; border-bottom: 0;
} }
.dashboard__quick-access, .dashboard__quick-access,

View file

@ -5,7 +5,7 @@ $white: #ffffff;
$classic-base-color: #282c37; $classic-base-color: #282c37;
$classic-primary-color: #9baec8; $classic-primary-color: #9baec8;
$classic-secondary-color: #d9e1e8; $classic-secondary-color: #d9e1e8;
$classic-highlight-color: #6364ff; $classic-highlight-color: #858afa;
$blurple-600: #563acc; // Iris $blurple-600: #563acc; // Iris
$blurple-500: #6364ff; // Brand purple $blurple-500: #6364ff; // Brand purple

View file

@ -161,11 +161,22 @@ body {
} }
} }
a {
&:focus {
border-radius: 4px;
outline: $ui-button-icon-focus-outline;
}
&:focus:not(:focus-visible) {
outline: none;
}
}
button { button {
font-family: inherit; font-family: inherit;
cursor: pointer; cursor: pointer;
&:focus { &:focus:not(:focus-visible) {
outline: none; outline: none;
} }
} }

View file

@ -74,6 +74,10 @@
background-color: $ui-button-focus-background-color; background-color: $ui-button-focus-background-color;
} }
&:focus-visible {
outline: $ui-button-icon-focus-outline;
}
&--destructive { &--destructive {
&:active, &:active,
&:focus, &:focus,
@ -98,16 +102,6 @@
transition: none; transition: none;
} }
&::-moz-focus-inner {
border: 0;
}
&::-moz-focus-inner,
&:focus,
&:active {
outline: 0 !important;
}
&.button-secondary { &.button-secondary {
color: $ui-button-secondary-color; color: $ui-button-secondary-color;
background: transparent; background: transparent;
@ -197,8 +191,6 @@
border-radius: 4px; border-radius: 4px;
background: transparent; background: transparent;
cursor: pointer; cursor: pointer;
transition: all 100ms ease-in;
transition-property: background-color, color;
text-decoration: none; text-decoration: none;
a { a {
@ -211,12 +203,10 @@
&:focus { &:focus {
color: lighten($action-button-color, 7%); color: lighten($action-button-color, 7%);
background-color: rgba($action-button-color, 0.15); background-color: rgba($action-button-color, 0.15);
transition: all 200ms ease-out;
transition-property: background-color, color;
} }
&:focus { &:focus-visible {
background-color: rgba($action-button-color, 0.3); outline: $ui-button-icon-focus-outline;
} }
&.disabled { &.disabled {
@ -225,20 +215,6 @@
cursor: default; cursor: default;
} }
&.active {
color: $highlight-text-color;
}
&::-moz-focus-inner {
border: 0;
}
&::-moz-focus-inner,
&:focus,
&:active {
outline: 0 !important;
}
&.inverted { &.inverted {
color: $lighter-text-color; color: $lighter-text-color;
@ -249,8 +225,8 @@
background-color: rgba($lighter-text-color, 0.15); background-color: rgba($lighter-text-color, 0.15);
} }
&:focus { &:focus-visible {
background-color: rgba($lighter-text-color, 0.3); outline: $ui-button-icon-focus-outline;
} }
&.disabled { &.disabled {
@ -261,6 +237,13 @@
&.active { &.active {
color: $highlight-text-color; color: $highlight-text-color;
&:hover,
&:active,
&:focus {
color: $highlight-text-color;
background-color: transparent;
}
&.disabled { &.disabled {
color: lighten($highlight-text-color, 13%); color: lighten($highlight-text-color, 13%);
} }
@ -269,13 +252,14 @@
&.overlayed { &.overlayed {
box-sizing: content-box; box-sizing: content-box;
background: rgba($base-overlay-background, 0.6); background: rgba($black, 0.65);
color: rgba($primary-text-color, 0.7); backdrop-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);
color: rgba($white, 0.7);
border-radius: 4px; border-radius: 4px;
padding: 2px; padding: 2px;
&:hover { &:hover {
background: rgba($base-overlay-background, 0.9); background: rgba($black, 0.9);
} }
} }
@ -305,21 +289,16 @@
font-size: 11px; font-size: 11px;
padding: 0 3px; padding: 0 3px;
line-height: 27px; line-height: 27px;
outline: 0;
transition: all 100ms ease-in;
transition-property: background-color, color;
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
color: darken($lighter-text-color, 7%); color: darken($lighter-text-color, 7%);
background-color: rgba($lighter-text-color, 0.15); background-color: rgba($lighter-text-color, 0.15);
transition: all 200ms ease-out;
transition-property: background-color, color;
} }
&:focus { &:focus-visible {
background-color: rgba($lighter-text-color, 0.3); outline: $ui-button-icon-focus-outline;
} }
&.disabled { &.disabled {
@ -330,16 +309,13 @@
&.active { &.active {
color: $highlight-text-color; color: $highlight-text-color;
}
&::-moz-focus-inner { &:hover,
border: 0; &:active,
} &:focus {
color: $highlight-text-color;
&::-moz-focus-inner, background-color: transparent;
&:focus, }
&:active {
outline: 0 !important;
} }
} }
@ -735,7 +711,6 @@ body > [data-popper-placement] {
flex: 0 0 auto; flex: 0 0 auto;
.compose-form__publish-button-wrapper { .compose-form__publish-button-wrapper {
overflow: hidden;
padding-top: 15px; padding-top: 15px;
} }
} }
@ -1433,6 +1408,10 @@ body > [data-popper-placement] {
} }
} }
.scrollable > div:first-child .detailed-status {
border-top: 0;
}
.detailed-status__meta { .detailed-status__meta {
margin-top: 16px; margin-top: 16px;
color: $dark-text-color; color: $dark-text-color;
@ -1478,6 +1457,7 @@ body > [data-popper-placement] {
.detailed-status__link { .detailed-status__link {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
white-space: nowrap;
} }
.detailed-status__favorites, .detailed-status__favorites,
@ -1983,13 +1963,6 @@ a.account__display-name {
.navigation-bar__actions { .navigation-bar__actions {
position: relative; position: relative;
.icon-button.close {
position: absolute;
pointer-events: none;
transform: scale(0, 1) translate(-100%, 0);
opacity: 0;
}
.compose__action-bar .icon-button { .compose__action-bar .icon-button {
pointer-events: auto; pointer-events: auto;
transform: scale(1, 1) translate(0, 0); transform: scale(1, 1) translate(0, 0);
@ -1999,19 +1972,21 @@ a.account__display-name {
} }
.navigation-bar__profile { .navigation-bar__profile {
display: flex;
flex-direction: column;
flex: 1 1 auto; flex: 1 1 auto;
line-height: 20px; line-height: 20px;
overflow: hidden;
} }
.navigation-bar__profile-account { .navigation-bar__profile-account {
display: block; display: inline;
font-weight: 500; font-weight: 500;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.navigation-bar__profile-edit { .navigation-bar__profile-edit {
display: inline;
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
@ -2068,7 +2043,7 @@ a.account__display-name {
font-size: inherit; font-size: inherit;
line-height: inherit; line-height: inherit;
&:focus { &:focus-visible {
outline: 1px dotted; outline: 1px dotted;
} }
} }
@ -3589,12 +3564,10 @@ button.icon-button.active i.fa-retweet {
} }
.status-card { .status-card {
display: block;
position: relative; position: relative;
display: flex;
font-size: 14px; font-size: 14px;
border: 1px solid lighten($ui-base-color, 8%); color: $darker-text-color;
border-radius: 4px;
color: $dark-text-color;
margin-top: 14px; margin-top: 14px;
text-decoration: none; text-decoration: none;
overflow: hidden; overflow: hidden;
@ -3648,8 +3621,29 @@ button.icon-button.active i.fa-retweet {
a.status-card { a.status-card {
cursor: pointer; cursor: pointer;
&:hover { &:hover,
background: lighten($ui-base-color, 8%); &:focus,
&:active {
.status-card__title,
.status-card__host,
.status-card__author {
color: $highlight-text-color;
}
}
}
.status-card a {
color: inherit;
text-decoration: none;
&:hover,
&:focus,
&:active {
.status-card__title,
.status-card__host,
.status-card__author {
color: $highlight-text-color;
}
} }
} }
@ -3675,42 +3669,42 @@ a.status-card {
.status-card__title { .status-card__title {
display: block; display: block;
font-weight: 500; font-weight: 700;
margin-bottom: 5px; font-size: 19px;
color: $darker-text-color; line-height: 24px;
color: $primary-text-color;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-decoration: none;
} }
.status-card__content { .status-card__content {
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
padding: 14px 14px 14px 8px; padding: 15px 0;
} padding-bottom: 0;
.status-card__description {
color: $darker-text-color;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
} }
.status-card__host { .status-card__host {
display: block; display: block;
margin-top: 5px; font-size: 14px;
font-size: 13px; margin-bottom: 8px;
overflow: hidden; }
text-overflow: ellipsis;
white-space: nowrap; .status-card__author {
display: block;
margin-top: 8px;
font-size: 14px;
color: $primary-text-color;
strong {
font-weight: 500;
}
} }
.status-card__image { .status-card__image {
flex: 0 0 100px; width: 100%;
background: lighten($ui-base-color, 8%); background: lighten($ui-base-color, 8%);
position: relative; position: relative;
border-radius: 8px;
& > .fa { & > .fa {
font-size: 21px; font-size: 21px;
@ -3722,50 +3716,8 @@ a.status-card {
} }
} }
.status-card.horizontal {
display: block;
.status-card__image {
width: 100%;
}
.status-card__image-image,
.status-card__image-preview {
border-radius: 4px 4px 0 0;
}
.status-card__title {
white-space: inherit;
}
}
.status-card.compact {
border-color: lighten($ui-base-color, 4%);
&.interactive {
border: 0;
}
.status-card__content {
padding: 8px;
padding-top: 10px;
}
.status-card__title {
white-space: nowrap;
}
.status-card__image {
flex: 0 0 60px;
}
}
a.status-card.compact:hover {
background-color: lighten($ui-base-color, 4%);
}
.status-card__image-image { .status-card__image-image {
border-radius: 4px 0 0 4px; border-radius: 8px;
display: block; display: block;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -3776,7 +3728,7 @@ a.status-card.compact:hover {
} }
.status-card__image-preview { .status-card__image-preview {
border-radius: 4px 0 0 4px; border-radius: 8px;
display: block; display: block;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -3931,7 +3883,6 @@ a.status-card.compact:hover {
position: relative; position: relative;
z-index: 2; z-index: 2;
outline: 0; outline: 0;
overflow: hidden;
& > button { & > button {
margin: 0; margin: 0;
@ -3946,6 +3897,10 @@ a.status-card.compact:hover {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
flex: 1; flex: 1;
&:focus-visible {
outline: $ui-button-icon-focus-outline;
}
} }
& > .column-header__back-button { & > .column-header__back-button {
@ -3986,10 +3941,18 @@ a.status-card.compact:hover {
font-size: 16px; font-size: 16px;
padding: 0 15px; padding: 0 15px;
&:last-child {
border-start-end-radius: 4px;
}
&:hover { &:hover {
color: lighten($darker-text-color, 4%); color: lighten($darker-text-color, 4%);
} }
&:focus-visible {
outline: $ui-button-icon-focus-outline;
}
&.active { &.active {
color: $primary-text-color; color: $primary-text-color;
background: lighten($ui-base-color, 4%); background: lighten($ui-base-color, 4%);
@ -4278,6 +4241,7 @@ a.status-card.compact:hover {
margin: 0; margin: 0;
border: 0; border: 0;
border-radius: 4px; border-radius: 4px;
color: $white;
&__label { &__label {
display: flex; display: flex;
@ -4285,7 +4249,6 @@ a.status-card.compact:hover {
justify-content: center; justify-content: center;
gap: 8px; gap: 8px;
flex-direction: column; flex-direction: column;
color: $primary-text-color;
font-weight: 500; font-weight: 500;
font-size: 14px; font-size: 14px;
} }
@ -4456,7 +4419,7 @@ a.status-card.compact:hover {
} }
.relationship-tag { .relationship-tag {
color: $primary-text-color; color: $white;
margin-bottom: 4px; margin-bottom: 4px;
display: block; display: block;
background-color: rgba($black, 0.45); background-color: rgba($black, 0.45);
@ -4647,7 +4610,7 @@ a.status-card.compact:hover {
.emoji-picker-dropdown__menu { .emoji-picker-dropdown__menu {
background: $simple-background-color; background: $simple-background-color;
position: relative; position: relative;
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); box-shadow: var(--dropdown-shadow);
border-radius: 4px; border-radius: 4px;
margin-top: 5px; margin-top: 5px;
z-index: 2; z-index: 2;
@ -4806,11 +4769,6 @@ a.status-card.compact:hover {
outline: 0; outline: 0;
cursor: pointer; cursor: pointer;
&:active,
&:focus {
outline: 0 !important;
}
img { img {
filter: grayscale(100%); filter: grayscale(100%);
opacity: 0.8; opacity: 0.8;
@ -4826,6 +4784,13 @@ a.status-card.compact:hover {
img { img {
opacity: 1; opacity: 1;
filter: none; filter: none;
border-radius: 100%;
}
}
&:focus-visible {
img {
outline: $ui-button-icon-focus-outline;
} }
} }
} }
@ -4838,7 +4803,7 @@ a.status-card.compact:hover {
.privacy-dropdown__dropdown, .privacy-dropdown__dropdown,
.expiration-dropdown__dropdown { .expiration-dropdown__dropdown {
background: $simple-background-color; background: $simple-background-color;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); box-shadow: var(--dropdown-shadow);
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
z-index: 2; z-index: 2;
@ -4920,19 +4885,6 @@ a.status-card.compact:hover {
.expiration-dropdown__value { .expiration-dropdown__value {
background: $simple-background-color; background: $simple-background-color;
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
.icon-button {
transition: none;
}
&.active {
background: $ui-highlight-color;
.icon-button {
color: $primary-text-color;
}
}
} }
&.top .privacy-dropdown__value, &.top .privacy-dropdown__value,
@ -4943,7 +4895,7 @@ a.status-card.compact:hover {
.privacy-dropdown__dropdown, .privacy-dropdown__dropdown,
.expiration-dropdown__dropdown { .expiration-dropdown__dropdown {
display: block; display: block;
box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1); box-shadow: var(--dropdown-shadow);
} }
} }
@ -4962,7 +4914,7 @@ a.status-card.compact:hover {
.language-dropdown { .language-dropdown {
&__dropdown { &__dropdown {
background: $simple-background-color; background: $simple-background-color;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); box-shadow: var(--dropdown-shadow);
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
z-index: 2; z-index: 2;
@ -5150,7 +5102,6 @@ a.status-card.compact:hover {
position: absolute; position: absolute;
top: 16px; top: 16px;
inset-inline-end: 10px; inset-inline-end: 10px;
z-index: 2;
display: inline-block; display: inline-block;
opacity: 0; opacity: 0;
transition: all 100ms linear; transition: all 100ms linear;
@ -5289,9 +5240,9 @@ a.status-card.compact:hover {
display: flex; display: flex;
} }
.video-modal__container { .video-modal .video-player {
max-height: 80vh;
max-width: 100vw; max-width: 100vw;
max-height: 100vh;
} }
.audio-modal__container { .audio-modal__container {
@ -6310,7 +6261,7 @@ a.status-card.compact:hover {
box-sizing: border-box; box-sizing: border-box;
margin-top: 8px; margin-top: 8px;
overflow: hidden; overflow: hidden;
border-radius: 4px; border-radius: 8px;
position: relative; position: relative;
width: 100%; width: 100%;
min-height: 64px; min-height: 64px;
@ -6341,7 +6292,7 @@ a.status-card.compact:hover {
box-sizing: border-box; box-sizing: border-box;
display: block; display: block;
position: relative; position: relative;
border-radius: 4px; border-radius: 8px;
overflow: hidden; overflow: hidden;
&--tall { &--tall {
@ -6427,7 +6378,7 @@ a.status-card.compact:hover {
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
background: darken($ui-base-color, 8%); background: darken($ui-base-color, 8%);
border-radius: 4px; border-radius: 8px;
padding-bottom: 44px; padding-bottom: 44px;
width: 100%; width: 100%;
@ -6494,7 +6445,7 @@ a.status-card.compact:hover {
position: relative; position: relative;
background: $base-shadow-color; background: $base-shadow-color;
max-width: 100%; max-width: 100%;
border-radius: 4px; border-radius: 8px;
box-sizing: border-box; box-sizing: border-box;
color: $white; color: $white;
display: flex; display: flex;
@ -6511,8 +6462,6 @@ a.status-card.compact:hover {
video { video {
display: block; display: block;
max-width: 100vw;
max-height: 80vh;
z-index: 1; z-index: 1;
} }
@ -6520,22 +6469,15 @@ a.status-card.compact:hover {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
margin: 0; margin: 0;
aspect-ratio: auto !important;
video { video {
max-width: 100% !important;
max-height: 100% !important;
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
outline: 0; outline: 0;
} }
} }
&.inline {
video {
object-fit: contain;
}
}
&__controls { &__controls {
position: absolute; position: absolute;
direction: ltr; direction: ltr;
@ -8336,6 +8278,7 @@ noscript {
.search__input { .search__input {
border: 1px solid lighten($ui-base-color, 8%); border: 1px solid lighten($ui-base-color, 8%);
padding: 10px; padding: 10px;
padding-inline-end: 28px;
} }
.search__popout { .search__popout {
@ -8364,8 +8307,9 @@ noscript {
align-items: center; align-items: center;
color: $primary-text-color; color: $primary-text-color;
text-decoration: none; text-decoration: none;
padding: 15px 0; padding: 15px;
border-bottom: 1px solid lighten($ui-base-color, 8%); border-bottom: 1px solid lighten($ui-base-color, 8%);
gap: 15px;
&:last-child { &:last-child {
border-bottom: 0; border-bottom: 0;
@ -8374,33 +8318,40 @@ noscript {
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
background-color: lighten($ui-base-color, 4%); color: $highlight-text-color;
.story__details__publisher,
.story__details__shared {
color: $highlight-text-color;
}
} }
&__details { &__details {
padding: 0 15px;
flex: 1 1 auto; flex: 1 1 auto;
&__publisher { &__publisher {
color: $darker-text-color; color: $darker-text-color;
margin-bottom: 4px; margin-bottom: 8px;
} }
&__title { &__title {
font-size: 19px; font-size: 19px;
line-height: 24px; line-height: 24px;
font-weight: 500; font-weight: 500;
margin-bottom: 4px; margin-bottom: 8px;
} }
&__shared { &__shared {
color: $darker-text-color; color: $darker-text-color;
} }
strong {
font-weight: 500;
}
} }
&__thumbnail { &__thumbnail {
flex: 0 0 auto; flex: 0 0 auto;
margin: 0 15px;
position: relative; position: relative;
width: 120px; width: 120px;
height: 120px; height: 120px;
@ -8411,7 +8362,7 @@ noscript {
} }
img { img {
border-radius: 4px; border-radius: 8px;
display: block; display: block;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -8420,7 +8371,7 @@ noscript {
} }
&__preview { &__preview {
border-radius: 4px; border-radius: 8px;
display: block; display: block;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -8436,6 +8387,23 @@ noscript {
} }
} }
} }
&.expanded {
flex-direction: column;
.story__thumbnail {
order: 1;
width: 100%;
height: auto;
aspect-ratio: 1.91 / 1;
}
.story__details {
order: 2;
width: 100%;
flex: 0 0 auto;
}
}
} }
.server-banner { .server-banner {

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